From ptprasanna at gmail.com  Fri Jul  1 07:42:41 2022
From: ptprasanna at gmail.com (Prasanna T)
Date: Fri,  1 Jul 2022 20:12:41 +0530
Subject: Shakti-platform-UART-Update
Message-ID: <20220701144241.9082-1-ptprasanna@gmail.com>

Please find the attached revised patch, with respect to the latest changes made. Have dealt other amendments through dts. A primary change that was made as part of this patch is follows.The age old version of UART was upgraded long back, but that was missed out in the code which we did updated now (The old version of UART is not supported anymore, hence removed the inline comment which is also outdated now), The current version is the only UART supported right now by Shakti
---

diff --git a/lib/utils/serial/shakti-uart.c b/lib/utils/serial/shakti-uart.c
index 5f2fe75..122218b 100644
--- a/lib/utils/serial/shakti-uart.c
+++ b/lib/utils/serial/shakti-uart.c
@@ -19,21 +19,22 @@
 #define REG_RX_THRES	0x20
 
 #define UART_TX_FULL  0x2
+#define UART_RX_NOT_EMPTY 0x4
 #define UART_RX_FULL  0x8
 
 static volatile char *uart_base;
 
 static void shakti_uart_putc(char ch)
 {
-	while((readw(uart_base + REG_STATUS) & UART_TX_FULL))
+	while((readb(uart_base + REG_STATUS) & UART_TX_FULL))
 		;
 	writeb(ch, uart_base + REG_TX);
 }
 
 static int shakti_uart_getc(void)
 {
-	u16 status = readw(uart_base + REG_STATUS);
-	if (status & UART_RX_FULL)
+	u16 status = readb(uart_base + REG_STATUS);
+	if (status & UART_RX_NOT_EMPTY)
 		return readb(uart_base + REG_RX);
 	return -1;
 }


From wxjstz at 126.com  Fri Jul  1 22:59:59 2022
From: wxjstz at 126.com (Xiang W)
Date: Sat, 02 Jul 2022 13:59:59 +0800
Subject: Shakti-platform-UART-Update
In-Reply-To: <20220701144241.9082-1-ptprasanna@gmail.com>
References: <20220701144241.9082-1-ptprasanna@gmail.com>
Message-ID: <6e1ff68cbd22600374fb3f48a2bae821f7a7bf99.camel@126.com>

? 2022-07-01???? 20:12 +0530?Prasanna T???
> Please find the attached revised patch, with respect to the latest changes made. Have dealt other amendments through dts. A primary change
> that was made as part of this patch is follows.The age old version of UART was upgraded long back, but that was missed out in the code which
> we did updated now (The old version of UART is not supported anymore, hence removed the inline comment which is also outdated now), The
> current version is the only UART supported right now by Shakti
> ---
> 
> diff --git a/lib/utils/serial/shakti-uart.c b/lib/utils/serial/shakti-uart.c
> index 5f2fe75..122218b 100644
> --- a/lib/utils/serial/shakti-uart.c
> +++ b/lib/utils/serial/shakti-uart.c
> @@ -19,21 +19,22 @@
> ?#define REG_RX_THRES???0x20
> ?
> ?#define UART_TX_FULL? 0x2
> +#define UART_RX_NOT_EMPTY 0x4
> ?#define UART_RX_FULL? 0x8
> ?
> ?static volatile char *uart_base;
> ?
> ?static void shakti_uart_putc(char ch)
> ?{
> -???????while((readw(uart_base + REG_STATUS) & UART_TX_FULL))
> +???????while((readb(uart_base + REG_STATUS) & UART_TX_FULL))
> ????????????????;
> ????????writeb(ch, uart_base + REG_TX);
> ?}
> ?
> ?static int shakti_uart_getc(void)
> ?{
> -???????u16 status = readw(uart_base + REG_STATUS);
> -???????if (status & UART_RX_FULL)
> +???????u16 status = readb(uart_base + REG_STATUS);
readb return u8. So the type of 'status' needs to be u8.

Regards,
Xiang W
> +???????if (status & UART_RX_NOT_EMPTY)
> ????????????????return readb(uart_base + REG_RX);
> ????????return -1;
> ?}
> 




From anup at brainfault.org  Tue Jul  5 20:23:53 2022
From: anup at brainfault.org (Anup Patel)
Date: Wed, 6 Jul 2022 08:53:53 +0530
Subject: [PATCH v2 1/1] lib: sbi_timer: Added a blocking wait function
 that waits until a certain condition is satisfied or timeout occurs
In-Reply-To: <20220630094635.389645-2-adnan.chowdhury@sifive.com>
References: <20220630094635.389645-1-adnan.chowdhury@sifive.com> <20220630094635.389645-2-adnan.chowdhury@sifive.com>
Message-ID: <CAAhSdy3AHouZAfA17EEhD8Jy=0pgU5Psn2te9k7G3gzdjWUmyQ@mail.gmail.com>

On Thu, Jun 30, 2022 at 3:16 PM Adnan Rahman Chowdhury
<adnan.chowdhury at sifive.com> wrote:
>
> Motivation: Suppose a peripheral needs to be configured to transmit
> data. There is an SFR bit which indicates that the peripheral is ready
> to transmit. The firmware should check the bit and will only transmit
> data when the peripheral is ready. When the firmware starts polling the
> SFR, the peripheral could be busy transmitting/receiving other data so
> the firmware must wait till that completes. Assuming that there is no
> other way, the firmware shouldn't wait indefinitely.
>
> The function sbi_timer_waitms_until() will constantly check whether a
> certain condition is satisfied, or timeout occurs. It can be used for
> the cases when a timeout is required.
>
> Signed-off-by: Adnan Rahman Chowdhury <adnan.chowdhury at sifive.com>
> Reviewed-by: Xiang W <wxjstz at 126.com>

Looks good to me.

Reviewed-by: Anup Patel <anup at brainfault.org>

Regards,
Anup

> ---
>  include/sbi/sbi_timer.h | 18 ++++++++++++++++++
>  lib/sbi/sbi_timer.c     | 13 +++++++++++++
>  2 files changed, 31 insertions(+)
>
> diff --git a/include/sbi/sbi_timer.h b/include/sbi/sbi_timer.h
> index 63ef1af..ac48e2b 100644
> --- a/include/sbi/sbi_timer.h
> +++ b/include/sbi/sbi_timer.h
> @@ -48,6 +48,24 @@ static inline void sbi_timer_udelay(ulong usecs)
>         sbi_timer_delay_loop(usecs, 1000000, NULL, NULL);
>  }
>
> +/**
> + * A blocking function that will wait until @p predicate returns true or
> + * @p timeout_ms milliseconds elapsed. @p arg will be passed as argument to
> + * @p predicate function.
> + *
> + * @param predicate Pointer to a function that returns true if certain
> + * condition is met. It shouldn't block the code execution.
> + * @param arg Argument to pass to @p predicate.
> + * @param timeout_ms Timeout value in milliseconds. The function will return
> + * false if @p timeout_ms time period elapsed but still @p predicate doesn't
> + * return true.
> + *
> + * @return true if @p predicate returns true within @p timeout_ms, false
> + * otherwise.
> + */
> +bool sbi_timer_waitms_until(bool (*predicate)(void *), void *arg,
> +                           uint64_t timeout_ms);
> +
>  /** Get timer value for current HART */
>  u64 sbi_timer_value(void);
>
> diff --git a/lib/sbi/sbi_timer.c b/lib/sbi/sbi_timer.c
> index acdba92..649a4e6 100644
> --- a/lib/sbi/sbi_timer.c
> +++ b/lib/sbi/sbi_timer.c
> @@ -81,6 +81,19 @@ void sbi_timer_delay_loop(ulong units, u64 unit_freq,
>                 delay_fn(opaque);
>  }
>
> +bool sbi_timer_waitms_until(bool (*predicate)(void *), void *arg,
> +                           uint64_t timeout_ms)
> +{
> +       uint64_t start_time = sbi_timer_value();
> +       uint64_t ticks =
> +               (sbi_timer_get_device()->timer_freq / 1000) *
> +               timeout_ms;
> +       while(!predicate(arg))
> +               if (sbi_timer_value() - start_time  >= ticks)
> +                       return false;
> +       return true;
> +}
> +
>  u64 sbi_timer_value(void)
>  {
>         if (get_time_val)
> --
> 2.30.2
>
>
> --
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi


From junliang.tan at linux.starfivetech.com  Thu Jul  7 22:50:05 2022
From: junliang.tan at linux.starfivetech.com (Jun Liang Tan)
Date: Fri,  8 Jul 2022 13:50:05 +0800
Subject: [PATCH] lib: utils: serial: Add Cadence UART driver
Message-ID: <20220708055005.26295-1-junliang.tan@linux.starfivetech.com>

Add Cadence UART driver

Signed-off-by: Jun Liang Tan <junliang.tan at linux.starfivetech.com>
Signed-off-by: Wei Liang Lim <weiliang.lim at linux.starfivetech.com>
---
 include/sbi_utils/fdt/fdt_helper.h      |   3 +
 include/sbi_utils/serial/cadence-uart.h |  16 +++
 lib/utils/fdt/fdt_helper.c              |  35 +++++++
 lib/utils/serial/cadence-uart.c         | 125 ++++++++++++++++++++++++
 lib/utils/serial/fdt_serial.c           |   2 +
 lib/utils/serial/fdt_serial_cadence.c   |  35 +++++++
 lib/utils/serial/objects.mk             |   2 +
 7 files changed, 218 insertions(+)
 create mode 100644 include/sbi_utils/serial/cadence-uart.h
 create mode 100644 lib/utils/serial/cadence-uart.c
 create mode 100644 lib/utils/serial/fdt_serial_cadence.c

diff --git a/include/sbi_utils/fdt/fdt_helper.h b/include/sbi_utils/fdt/fdt_helper.h
index 24fee7a..1166208 100644
--- a/include/sbi_utils/fdt/fdt_helper.h
+++ b/include/sbi_utils/fdt/fdt_helper.h
@@ -53,6 +53,9 @@ int fdt_parse_max_hart_id(void *fdt, u32 *max_hartid);
 
 int fdt_parse_timebase_frequency(void *fdt, unsigned long *freq);
 
+int fdt_parse_cadence_uart_node(void *fdt, int nodeoffset,
+				struct platform_uart_data *uart);
+
 int fdt_parse_gaisler_uart_node(void *fdt, int nodeoffset,
 				struct platform_uart_data *uart);
 
diff --git a/include/sbi_utils/serial/cadence-uart.h b/include/sbi_utils/serial/cadence-uart.h
new file mode 100644
index 0000000..e75fb95
--- /dev/null
+++ b/include/sbi_utils/serial/cadence-uart.h
@@ -0,0 +1,16 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2022 StarFive Technology Co., Ltd.
+ *
+ * Author: Jun Liang Tan <junliang.tan at linux.starfivetech.com>
+ */
+
+#ifndef __SERIAL_CADENCE_UART_H__
+#define __SERIAL_CADENCE_UART_H__
+
+#include <sbi/sbi_types.h>
+
+int cadence_uart_init(unsigned long base, u32 in_freq, u32 baudrate);
+
+#endif
diff --git a/lib/utils/fdt/fdt_helper.c b/lib/utils/fdt/fdt_helper.c
index 5bf4021..3ad0a5e 100644
--- a/lib/utils/fdt/fdt_helper.c
+++ b/lib/utils/fdt/fdt_helper.c
@@ -290,6 +290,41 @@ int fdt_parse_timebase_frequency(void *fdt, unsigned long *freq)
 	return 0;
 }
 
+int fdt_parse_cadence_uart_node(void *fdt, int nodeoffset,
+				struct platform_uart_data *uart)
+{
+	int len, rc;
+	const fdt32_t *val;
+	u64 reg_addr, reg_size;
+
+	if (nodeoffset < 0 || !uart || !fdt)
+		return SBI_ENODEV;
+
+	rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
+				    &reg_addr, &reg_size);
+	if (rc < 0 || !reg_addr || !reg_size)
+		return SBI_ENODEV;
+	uart->addr = reg_addr;
+
+	/**
+	 * UART address is mandatory. clock-frequency and current-speed
+	 * may not be present. Don't return error.
+	 */
+	val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "clock-frequency", &len);
+	if (len > 0 && val)
+		uart->freq = fdt32_to_cpu(*val);
+	else
+		uart->freq = DEFAULT_UART_FREQ;
+
+	val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "current-speed", &len);
+	if (len > 0 && val)
+		uart->baud = fdt32_to_cpu(*val);
+	else
+		uart->baud = DEFAULT_UART_BAUD;
+
+	return 0;
+}
+
 int fdt_parse_gaisler_uart_node(void *fdt, int nodeoffset,
 				struct platform_uart_data *uart)
 {
diff --git a/lib/utils/serial/cadence-uart.c b/lib/utils/serial/cadence-uart.c
new file mode 100644
index 0000000..ffa79f8
--- /dev/null
+++ b/lib/utils/serial/cadence-uart.c
@@ -0,0 +1,125 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2022 StarFive Technology Co., Ltd.
+ *
+ * Author: Jun Liang Tan <junliang.tan at linux.starfivetech.com>
+ */
+
+#include <sbi/riscv_io.h>
+#include <sbi/sbi_console.h>
+#include <sbi_utils/serial/cadence-uart.h>
+
+/* clang-format off */
+
+#define UART_REG_OFFSET_CTRL		0x00	/* Control register */
+#define UART_REG_OFFSET_MODE		0x04	/* Mode register */
+#define UART_REG_OFFSET_CH_STS		0x2C	/* Channel status register */
+#define UART_REG_OFFSET_RX_TX_FIFO	0x30	/* RX and TX FIFO register */
+#define UART_REG_OFFSET_INT_DIS		0x0C	/* Interrupt disable register */
+#define UART_REG_OFFSET_BRG	0x18	/* Baud rate generator register */
+#define UART_REG_OFFSET_BRD	0x34	/* Baud rate divider register */
+
+/* Control register, 0x00 */
+#define UART_CR_TX_DIS	0x00000020	/* TX disabled, bit 5 */
+#define UART_CR_TX_EN	0x00000010	/* TX enabled, bit 4 */
+#define UART_CR_RX_DIS	0x00000008	/* RX disabled, bit 3 */
+#define UART_CR_RX_EN	0x00000004	/* RX enabled, bit 2*/
+#define UART_CR_TXRST	0x00000002	/* TX logic reset, bit 1 */
+#define UART_CR_RXRST	0x00000001	/* RX logic reset, bit 0 */
+
+/* Channel status register, 0x2C */
+#define	UART_CHSR_TXFULL	0x00000010	/* TX FIFO full, bit 4 */
+#define	UART_CHSR_RXEMPTY	0x00000002	/* RX FIFO empty, bit 1 */
+
+/* Mode register, 0x04 */
+#define UART_MR_PARITY_NONE	0x00000020	/* No parity set */
+
+/* Baud rate generator register, 0x18 */
+#define UART_BRG_CLK_DIVISOR	0x00000001	/* baud_sample = sel_clk */
+
+/* clang-format on */
+
+static volatile void *uart_base;
+static u32 uart_in_freq;
+static u32 uart_baudrate;
+
+/**
+ * Find minimum divisor divides in_freq to max_target_hz;
+ * Based on SiFive UART driver (sifive-uart.c)
+ */
+static inline unsigned int uart_min_clk_divisor(uint64_t in_freq,
+						uint64_t max_target_hz)
+{
+	uint64_t quotient = (in_freq + max_target_hz - 1) / (max_target_hz);
+	/* Avoid underflow */
+	if (quotient == 0)
+		return 0;
+	else
+		return quotient - 1;
+}
+
+static u32 get_reg(u32 offset)
+{
+	return readl(uart_base + offset);
+}
+
+static void set_reg(u32 offset, u32 val)
+{
+	writel(val, uart_base + offset);
+}
+
+static void cadence_uart_putc(char ch)
+{
+	while (get_reg(UART_REG_OFFSET_CH_STS) & UART_CHSR_TXFULL)
+		;
+
+	set_reg(UART_REG_OFFSET_RX_TX_FIFO, ch);
+}
+
+static int cadence_uart_getc(void)
+{
+	u32 ret = get_reg(UART_REG_OFFSET_CH_STS);
+
+	if (!(ret & UART_CHSR_RXEMPTY))
+		return get_reg(UART_REG_OFFSET_RX_TX_FIFO);
+	return -1;
+}
+
+static struct sbi_console_device cadence_console = {
+	.name = "cadence_uart",
+	.console_putc = cadence_uart_putc,
+	.console_getc = cadence_uart_getc
+};
+
+int cadence_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
+{
+	uart_base     = (volatile void *)base;
+	uart_in_freq  = in_freq;
+	uart_baudrate = baudrate;
+
+	/* Disable interrupts */
+	set_reg(UART_REG_OFFSET_INT_DIS, 0xFFFFFFFF);
+	/* Disable TX RX */
+	set_reg(UART_REG_OFFSET_CTRL, UART_CR_TX_DIS | UART_CR_RX_DIS);
+	/* Configure baudrate */
+	if (in_freq) {
+		set_reg(UART_REG_OFFSET_BRG, UART_BRG_CLK_DIVISOR);
+		set_reg(UART_REG_OFFSET_BRD,
+			uart_min_clk_divisor(in_freq, baudrate));
+	}
+	/* Software reset TX RX data path and enable TX RX */
+	set_reg(UART_REG_OFFSET_CTRL, UART_CR_TXRST | UART_CR_RXRST
+		| UART_CR_TX_EN | UART_CR_RX_EN);
+	/* Set:
+	 * 1 stop bit, bits[07:06] = 0x00,
+	 * no parity set, bits[05:03] = 0x100,
+	 * 8 bits character length, bits[02:01] = 0x00,
+	 * sel_clk = uart_clk, bit[0] = 0x0
+	 */
+	set_reg(UART_REG_OFFSET_MODE, UART_MR_PARITY_NONE);
+
+	sbi_console_set_device(&cadence_console);
+
+	return 0;
+}
diff --git a/lib/utils/serial/fdt_serial.c b/lib/utils/serial/fdt_serial.c
index f73d26a..460f516 100644
--- a/lib/utils/serial/fdt_serial.c
+++ b/lib/utils/serial/fdt_serial.c
@@ -13,6 +13,7 @@
 #include <sbi_utils/fdt/fdt_helper.h>
 #include <sbi_utils/serial/fdt_serial.h>
 
+extern struct fdt_serial fdt_serial_cadence;
 extern struct fdt_serial fdt_serial_uart8250;
 extern struct fdt_serial fdt_serial_sifive;
 extern struct fdt_serial fdt_serial_litex;
@@ -21,6 +22,7 @@ extern struct fdt_serial fdt_serial_shakti;
 extern struct fdt_serial fdt_serial_gaisler;
 
 static struct fdt_serial *serial_drivers[] = {
+	&fdt_serial_cadence,
 	&fdt_serial_uart8250,
 	&fdt_serial_sifive,
 	&fdt_serial_litex,
diff --git a/lib/utils/serial/fdt_serial_cadence.c b/lib/utils/serial/fdt_serial_cadence.c
new file mode 100644
index 0000000..f6f13d6
--- /dev/null
+++ b/lib/utils/serial/fdt_serial_cadence.c
@@ -0,0 +1,35 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2022 StarFive Technology Co., Ltd.
+ *
+ * Author: Jun Liang Tan <junliang.tan at linux.starfivetech.com>
+ */
+
+#include <sbi_utils/fdt/fdt_helper.h>
+#include <sbi_utils/serial/fdt_serial.h>
+#include <sbi_utils/serial/cadence-uart.h>
+
+static int serial_cadence_init(void *fdt, int nodeoff,
+			       const struct fdt_match *match)
+{
+	int rc;
+	struct platform_uart_data uart;
+
+	rc = fdt_parse_cadence_uart_node(fdt, nodeoff, &uart);
+	if (rc)
+		return rc;
+
+	return cadence_uart_init(uart.addr, uart.freq, uart.baud);
+}
+
+static const struct fdt_match serial_cadence_match[] = {
+	{ .compatible = "cdns,uart-r1p12" },
+	{ .compatible = "starfive,jh8100-uart" },
+	{ },
+};
+
+struct fdt_serial fdt_serial_cadence = {
+	.match_table = serial_cadence_match,
+	.init = serial_cadence_init
+};
diff --git a/lib/utils/serial/objects.mk b/lib/utils/serial/objects.mk
index 4f751ba..c201d76 100644
--- a/lib/utils/serial/objects.mk
+++ b/lib/utils/serial/objects.mk
@@ -8,12 +8,14 @@
 #
 
 libsbiutils-objs-y += serial/fdt_serial.o
+libsbiutils-objs-y += serial/fdt_serial_cadence.o
 libsbiutils-objs-y += serial/fdt_serial_gaisler.o
 libsbiutils-objs-y += serial/fdt_serial_htif.o
 libsbiutils-objs-y += serial/fdt_serial_shakti.o
 libsbiutils-objs-y += serial/fdt_serial_sifive.o
 libsbiutils-objs-y += serial/fdt_serial_litex.o
 libsbiutils-objs-y += serial/fdt_serial_uart8250.o
+libsbiutils-objs-y += serial/cadence-uart.o
 libsbiutils-objs-y += serial/gaisler-uart.o
 libsbiutils-objs-y += serial/shakti-uart.o
 libsbiutils-objs-y += serial/sifive-uart.o
-- 
2.25.1




From ben.dooks at sifive.com  Fri Jul  8 02:27:29 2022
From: ben.dooks at sifive.com (Ben Dooks)
Date: Fri,  8 Jul 2022 10:27:29 +0100
Subject: [PATCH] sbi: add check for ipi device for hsm start
Message-ID: <20220708092729.246862-1-ben.dooks@sifive.com>

If the ecall SBI_EXT_HSM_HART_START is called it might try to wake the
secondary hart using sbi_ipi_raw_send() to send an IPI to the hart.
This can fail if there is no IPI device but no error is returned from
sbi_ipi_raw_send() so the ecall returns as if the action completed and
the caller continues without noticing (in the case of Linux it just hangs
waiting for the secondary hart to become active)

Fix this by changing sbi_ipi_raw_send() to return and error, and if an
error is returned, then return it via SBI_EXT_HSM_HART_START call.

Signed-off-by: Ben Dooks <ben.dooks at sifive.com>
---
 include/sbi/sbi_ipi.h |  2 +-
 lib/sbi/sbi_hsm.c     |  4 +++-
 lib/sbi/sbi_ipi.c     | 11 ++++++++---
 3 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/include/sbi/sbi_ipi.h b/include/sbi/sbi_ipi.h
index fb7d658..f6ac807 100644
--- a/include/sbi/sbi_ipi.h
+++ b/include/sbi/sbi_ipi.h
@@ -75,7 +75,7 @@ int sbi_ipi_send_halt(ulong hmask, ulong hbase);
 
 void sbi_ipi_process(void);
 
-void sbi_ipi_raw_send(u32 target_hart);
+int sbi_ipi_raw_send(u32 target_hart);
 
 const struct sbi_ipi_device *sbi_ipi_get_device(void);
 
diff --git a/lib/sbi/sbi_hsm.c b/lib/sbi/sbi_hsm.c
index 1165acc..836008f 100644
--- a/lib/sbi/sbi_hsm.c
+++ b/lib/sbi/sbi_hsm.c
@@ -289,7 +289,9 @@ int sbi_hsm_hart_start(struct sbi_scratch *scratch,
 	   (hsm_device_has_hart_secondary_boot() && !init_count)) {
 		return hsm_device_hart_start(hartid, scratch->warmboot_addr);
 	} else {
-		sbi_ipi_raw_send(hartid);
+		int rc = sbi_ipi_raw_send(hartid);
+		if (rc)
+		    return rc;
 	}
 
 	return 0;
diff --git a/lib/sbi/sbi_ipi.c b/lib/sbi/sbi_ipi.c
index 1014909..c2ede2d 100644
--- a/lib/sbi/sbi_ipi.c
+++ b/lib/sbi/sbi_ipi.c
@@ -208,10 +208,15 @@ skip:
 	};
 }
 
-void sbi_ipi_raw_send(u32 target_hart)
+int sbi_ipi_raw_send(u32 target_hart)
 {
-	if (ipi_dev && ipi_dev->ipi_send)
-		ipi_dev->ipi_send(target_hart);
+	if (!ipi_dev)
+		return SBI_EINVAL;
+	if (!ipi_dev->ipi_send)
+		return SBI_EINVAL;
+
+	ipi_dev->ipi_send(target_hart);
+	return 0;
 }
 
 const struct sbi_ipi_device *sbi_ipi_get_device(void)
-- 
2.35.1



From anup at brainfault.org  Fri Jul  8 02:26:35 2022
From: anup at brainfault.org (Anup Patel)
Date: Fri, 8 Jul 2022 14:56:35 +0530
Subject: [PATCH v2 1/1] lib: sbi_timer: Added a blocking wait function
 that waits until a certain condition is satisfied or timeout occurs
In-Reply-To: <CAAhSdy3AHouZAfA17EEhD8Jy=0pgU5Psn2te9k7G3gzdjWUmyQ@mail.gmail.com>
References: <20220630094635.389645-1-adnan.chowdhury@sifive.com>
 <20220630094635.389645-2-adnan.chowdhury@sifive.com> <CAAhSdy3AHouZAfA17EEhD8Jy=0pgU5Psn2te9k7G3gzdjWUmyQ@mail.gmail.com>
Message-ID: <CAAhSdy3q3kNckaGO1Ora-YZ0kvZy5hwo+3Ln9H1doo--Y_EHKg@mail.gmail.com>

On Wed, Jul 6, 2022 at 8:53 AM Anup Patel <anup at brainfault.org> wrote:
>
> On Thu, Jun 30, 2022 at 3:16 PM Adnan Rahman Chowdhury
> <adnan.chowdhury at sifive.com> wrote:
> >
> > Motivation: Suppose a peripheral needs to be configured to transmit
> > data. There is an SFR bit which indicates that the peripheral is ready
> > to transmit. The firmware should check the bit and will only transmit
> > data when the peripheral is ready. When the firmware starts polling the
> > SFR, the peripheral could be busy transmitting/receiving other data so
> > the firmware must wait till that completes. Assuming that there is no
> > other way, the firmware shouldn't wait indefinitely.
> >
> > The function sbi_timer_waitms_until() will constantly check whether a
> > certain condition is satisfied, or timeout occurs. It can be used for
> > the cases when a timeout is required.
> >
> > Signed-off-by: Adnan Rahman Chowdhury <adnan.chowdhury at sifive.com>
> > Reviewed-by: Xiang W <wxjstz at 126.com>
>
> Looks good to me.
>
> Reviewed-by: Anup Patel <anup at brainfault.org>

I have shortened the patch subject at time of merging this patch.

Applied this patch to the riscv/opensbi repo

Thanks,
Anup

>
> Regards,
> Anup
>
> > ---
> >  include/sbi/sbi_timer.h | 18 ++++++++++++++++++
> >  lib/sbi/sbi_timer.c     | 13 +++++++++++++
> >  2 files changed, 31 insertions(+)
> >
> > diff --git a/include/sbi/sbi_timer.h b/include/sbi/sbi_timer.h
> > index 63ef1af..ac48e2b 100644
> > --- a/include/sbi/sbi_timer.h
> > +++ b/include/sbi/sbi_timer.h
> > @@ -48,6 +48,24 @@ static inline void sbi_timer_udelay(ulong usecs)
> >         sbi_timer_delay_loop(usecs, 1000000, NULL, NULL);
> >  }
> >
> > +/**
> > + * A blocking function that will wait until @p predicate returns true or
> > + * @p timeout_ms milliseconds elapsed. @p arg will be passed as argument to
> > + * @p predicate function.
> > + *
> > + * @param predicate Pointer to a function that returns true if certain
> > + * condition is met. It shouldn't block the code execution.
> > + * @param arg Argument to pass to @p predicate.
> > + * @param timeout_ms Timeout value in milliseconds. The function will return
> > + * false if @p timeout_ms time period elapsed but still @p predicate doesn't
> > + * return true.
> > + *
> > + * @return true if @p predicate returns true within @p timeout_ms, false
> > + * otherwise.
> > + */
> > +bool sbi_timer_waitms_until(bool (*predicate)(void *), void *arg,
> > +                           uint64_t timeout_ms);
> > +
> >  /** Get timer value for current HART */
> >  u64 sbi_timer_value(void);
> >
> > diff --git a/lib/sbi/sbi_timer.c b/lib/sbi/sbi_timer.c
> > index acdba92..649a4e6 100644
> > --- a/lib/sbi/sbi_timer.c
> > +++ b/lib/sbi/sbi_timer.c
> > @@ -81,6 +81,19 @@ void sbi_timer_delay_loop(ulong units, u64 unit_freq,
> >                 delay_fn(opaque);
> >  }
> >
> > +bool sbi_timer_waitms_until(bool (*predicate)(void *), void *arg,
> > +                           uint64_t timeout_ms)
> > +{
> > +       uint64_t start_time = sbi_timer_value();
> > +       uint64_t ticks =
> > +               (sbi_timer_get_device()->timer_freq / 1000) *
> > +               timeout_ms;
> > +       while(!predicate(arg))
> > +               if (sbi_timer_value() - start_time  >= ticks)
> > +                       return false;
> > +       return true;
> > +}
> > +
> >  u64 sbi_timer_value(void)
> >  {
> >         if (get_time_val)
> > --
> > 2.30.2
> >
> >
> > --
> > opensbi mailing list
> > opensbi at lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/opensbi


From anup at brainfault.org  Fri Jul  8 04:52:05 2022
From: anup at brainfault.org (Anup Patel)
Date: Fri, 8 Jul 2022 17:22:05 +0530
Subject: Shakti-platform-UART-Update
In-Reply-To: <20220701144241.9082-1-ptprasanna@gmail.com>
References: <20220701144241.9082-1-ptprasanna@gmail.com>
Message-ID: <CAAhSdy1A6oHXWMJnQacA3WrJ1j3DRV2HNEjAp653iZFSAaJmhA@mail.gmail.com>

On Fri, Jul 1, 2022 at 8:13 PM Prasanna T <ptprasanna at gmail.com> wrote:
>
> Please find the attached revised patch, with respect to the latest changes made. Have dealt other amendments through dts. A primary change that was made as part of this patch is follows.The age old version of UART was upgraded long back, but that was missed out in the code which we did updated now (The old version of UART is not supported anymore, hence removed the inline comment which is also outdated now), The current version is the only UART supported right now by Shakti

The patch subject should be:
lib: utils/serial: Update Shakti UART based on latest implementation

The patch description should be simplified and each line should
be less than 75 characters.

Also, a "Signed-off-by" is mandatory for each patch which is
missing here.

Further with every patch revision, you should use "--subject-prefix"
parameter of "git format-patch". For example, to generate v3 patch
you can do: git format-patch -M -C --subject-prefix="PATCH v3" ...

I will send a PATCH v3 on your behalf but please take care next time.

Regards,
Anup

> ---
>
> diff --git a/lib/utils/serial/shakti-uart.c b/lib/utils/serial/shakti-uart.c
> index 5f2fe75..122218b 100644
> --- a/lib/utils/serial/shakti-uart.c
> +++ b/lib/utils/serial/shakti-uart.c
> @@ -19,21 +19,22 @@
>  #define REG_RX_THRES   0x20
>
>  #define UART_TX_FULL  0x2
> +#define UART_RX_NOT_EMPTY 0x4
>  #define UART_RX_FULL  0x8
>
>  static volatile char *uart_base;
>
>  static void shakti_uart_putc(char ch)
>  {
> -       while((readw(uart_base + REG_STATUS) & UART_TX_FULL))
> +       while((readb(uart_base + REG_STATUS) & UART_TX_FULL))
>                 ;
>         writeb(ch, uart_base + REG_TX);
>  }
>
>  static int shakti_uart_getc(void)
>  {
> -       u16 status = readw(uart_base + REG_STATUS);
> -       if (status & UART_RX_FULL)
> +       u16 status = readb(uart_base + REG_STATUS);
> +       if (status & UART_RX_NOT_EMPTY)
>                 return readb(uart_base + REG_RX);
>         return -1;
>  }
>
> --
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi


From apatel at ventanamicro.com  Fri Jul  8 04:52:44 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Fri,  8 Jul 2022 17:22:44 +0530
Subject: [PATCH v3] lib: utils/serial: Update Shakti UART based on latest implementation
Message-ID: <20220708115244.556074-1-apatel@ventanamicro.com>

From: Prasanna T <ptprasanna at gmail.com>

The age old version of Shakti UART was upgraded long back, but we missed
updating the driver in OpenSBI. The old version of UART is not supported
anymore, hence removed the inline comment which is also outdated now.

Signed-off-by: Prasanna T <ptprasanna at gmail.com>
Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
Changes since v2:
 - Updated patch subject and description
 - Minor fix in shakti_uart_getc()
---
 lib/utils/serial/shakti-uart.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/lib/utils/serial/shakti-uart.c b/lib/utils/serial/shakti-uart.c
index 5f2fe75..3556935 100644
--- a/lib/utils/serial/shakti-uart.c
+++ b/lib/utils/serial/shakti-uart.c
@@ -19,21 +19,21 @@
 #define REG_RX_THRES	0x20
 
 #define UART_TX_FULL  0x2
+#define UART_RX_NOT_EMPTY 0x4
 #define UART_RX_FULL  0x8
 
 static volatile char *uart_base;
 
 static void shakti_uart_putc(char ch)
 {
-	while((readw(uart_base + REG_STATUS) & UART_TX_FULL))
+	while ((readb(uart_base + REG_STATUS) & UART_TX_FULL))
 		;
 	writeb(ch, uart_base + REG_TX);
 }
 
 static int shakti_uart_getc(void)
 {
-	u16 status = readw(uart_base + REG_STATUS);
-	if (status & UART_RX_FULL)
+	if (readb(uart_base + REG_STATUS) & UART_RX_NOT_EMPTY)
 		return readb(uart_base + REG_RX);
 	return -1;
 }
-- 
2.34.1



From wxjstz at 126.com  Fri Jul  8 05:06:15 2022
From: wxjstz at 126.com (Xiang W)
Date: Fri, 08 Jul 2022 20:06:15 +0800
Subject: [PATCH v3] lib: utils/serial: Update Shakti UART based on
 latest implementation
In-Reply-To: <20220708115244.556074-1-apatel@ventanamicro.com>
References: <20220708115244.556074-1-apatel@ventanamicro.com>
Message-ID: <f9244f5b660be8be981385b07d68986717cf1f0d.camel@126.com>

? 2022-07-08???? 17:22 +0530?Anup Patel???
> From: Prasanna T <ptprasanna at gmail.com>
> 
> The age old version of Shakti UART was upgraded long back, but we missed
> updating the driver in OpenSBI. The old version of UART is not supported
> anymore, hence removed the inline comment which is also outdated now.
> 
> Signed-off-by: Prasanna T <ptprasanna at gmail.com>
> Signed-off-by: Anup Patel <apatel at ventanamicro.com>
look good to me

Reviewed-by: Xiang W <wxjstz at 126.com>
> ---
> Changes since v2:
> ?- Updated patch subject and description
> ?- Minor fix in shakti_uart_getc()
> ---
> ?lib/utils/serial/shakti-uart.c | 6 +++---
> ?1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/lib/utils/serial/shakti-uart.c b/lib/utils/serial/shakti-uart.c
> index 5f2fe75..3556935 100644
> --- a/lib/utils/serial/shakti-uart.c
> +++ b/lib/utils/serial/shakti-uart.c
> @@ -19,21 +19,21 @@
> ?#define REG_RX_THRES???0x20
> ?
> ?#define UART_TX_FULL? 0x2
> +#define UART_RX_NOT_EMPTY 0x4
> ?#define UART_RX_FULL? 0x8
> ?
> ?static volatile char *uart_base;
> ?
> ?static void shakti_uart_putc(char ch)
> ?{
> -???????while((readw(uart_base + REG_STATUS) & UART_TX_FULL))
> +???????while ((readb(uart_base + REG_STATUS) & UART_TX_FULL))
> ????????????????;
> ????????writeb(ch, uart_base + REG_TX);
> ?}
> ?
> ?static int shakti_uart_getc(void)
> ?{
> -???????u16 status = readw(uart_base + REG_STATUS);
> -???????if (status & UART_RX_FULL)
> +???????if (readb(uart_base + REG_STATUS) & UART_RX_NOT_EMPTY)
> ????????????????return readb(uart_base + REG_RX);
> ????????return -1;
> ?}
> -- 
> 2.34.1
> 
> 




From anup at brainfault.org  Fri Jul  8 05:10:17 2022
From: anup at brainfault.org (Anup Patel)
Date: Fri, 8 Jul 2022 17:40:17 +0530
Subject: [PATCH] lib: utils: serial: Add Cadence UART driver
In-Reply-To: <20220708055005.26295-1-junliang.tan@linux.starfivetech.com>
References: <20220708055005.26295-1-junliang.tan@linux.starfivetech.com>
Message-ID: <CAAhSdy3Wpzhc=qhWE_qEZgS3yL3W52q1wK04gf_YUca3YKi4Fg@mail.gmail.com>

On Fri, Jul 8, 2022 at 11:21 AM Jun Liang Tan
<junliang.tan at linux.starfivetech.com> wrote:
>
> Add Cadence UART driver
>
> Signed-off-by: Jun Liang Tan <junliang.tan at linux.starfivetech.com>
> Signed-off-by: Wei Liang Lim <weiliang.lim at linux.starfivetech.com>
> ---
>  include/sbi_utils/fdt/fdt_helper.h      |   3 +
>  include/sbi_utils/serial/cadence-uart.h |  16 +++
>  lib/utils/fdt/fdt_helper.c              |  35 +++++++
>  lib/utils/serial/cadence-uart.c         | 125 ++++++++++++++++++++++++
>  lib/utils/serial/fdt_serial.c           |   2 +
>  lib/utils/serial/fdt_serial_cadence.c   |  35 +++++++
>  lib/utils/serial/objects.mk             |   2 +
>  7 files changed, 218 insertions(+)
>  create mode 100644 include/sbi_utils/serial/cadence-uart.h
>  create mode 100644 lib/utils/serial/cadence-uart.c
>  create mode 100644 lib/utils/serial/fdt_serial_cadence.c
>
> diff --git a/include/sbi_utils/fdt/fdt_helper.h b/include/sbi_utils/fdt/fdt_helper.h
> index 24fee7a..1166208 100644
> --- a/include/sbi_utils/fdt/fdt_helper.h
> +++ b/include/sbi_utils/fdt/fdt_helper.h
> @@ -53,6 +53,9 @@ int fdt_parse_max_hart_id(void *fdt, u32 *max_hartid);
>
>  int fdt_parse_timebase_frequency(void *fdt, unsigned long *freq);
>
> +int fdt_parse_cadence_uart_node(void *fdt, int nodeoffset,
> +                               struct platform_uart_data *uart);
> +
>  int fdt_parse_gaisler_uart_node(void *fdt, int nodeoffset,
>                                 struct platform_uart_data *uart);
>
> diff --git a/include/sbi_utils/serial/cadence-uart.h b/include/sbi_utils/serial/cadence-uart.h
> new file mode 100644
> index 0000000..e75fb95
> --- /dev/null
> +++ b/include/sbi_utils/serial/cadence-uart.h
> @@ -0,0 +1,16 @@
> +/*
> + * SPDX-License-Identifier: BSD-2-Clause
> + *
> + * Copyright (c) 2022 StarFive Technology Co., Ltd.
> + *
> + * Author: Jun Liang Tan <junliang.tan at linux.starfivetech.com>
> + */
> +
> +#ifndef __SERIAL_CADENCE_UART_H__
> +#define __SERIAL_CADENCE_UART_H__
> +
> +#include <sbi/sbi_types.h>
> +
> +int cadence_uart_init(unsigned long base, u32 in_freq, u32 baudrate);
> +
> +#endif
> diff --git a/lib/utils/fdt/fdt_helper.c b/lib/utils/fdt/fdt_helper.c
> index 5bf4021..3ad0a5e 100644
> --- a/lib/utils/fdt/fdt_helper.c
> +++ b/lib/utils/fdt/fdt_helper.c
> @@ -290,6 +290,41 @@ int fdt_parse_timebase_frequency(void *fdt, unsigned long *freq)
>         return 0;
>  }
>
> +int fdt_parse_cadence_uart_node(void *fdt, int nodeoffset,
> +                               struct platform_uart_data *uart)
> +{
> +       int len, rc;
> +       const fdt32_t *val;
> +       u64 reg_addr, reg_size;
> +
> +       if (nodeoffset < 0 || !uart || !fdt)
> +               return SBI_ENODEV;
> +
> +       rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
> +                                   &reg_addr, &reg_size);
> +       if (rc < 0 || !reg_addr || !reg_size)
> +               return SBI_ENODEV;
> +       uart->addr = reg_addr;
> +
> +       /**
> +        * UART address is mandatory. clock-frequency and current-speed
> +        * may not be present. Don't return error.
> +        */
> +       val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "clock-frequency", &len);
> +       if (len > 0 && val)
> +               uart->freq = fdt32_to_cpu(*val);
> +       else
> +               uart->freq = DEFAULT_UART_FREQ;
> +
> +       val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "current-speed", &len);
> +       if (len > 0 && val)
> +               uart->baud = fdt32_to_cpu(*val);
> +       else
> +               uart->baud = DEFAULT_UART_BAUD;
> +
> +       return 0;
> +}
> +

Instead of adding one more UART node parsing function better
rename fdt_parse_uart8250() to fdt_parse_uart() and use it in
place of fdt_parse_cadence_uart_node().

>  int fdt_parse_gaisler_uart_node(void *fdt, int nodeoffset,
>                                 struct platform_uart_data *uart)
>  {
> diff --git a/lib/utils/serial/cadence-uart.c b/lib/utils/serial/cadence-uart.c
> new file mode 100644
> index 0000000..ffa79f8
> --- /dev/null
> +++ b/lib/utils/serial/cadence-uart.c
> @@ -0,0 +1,125 @@
> +/*
> + * SPDX-License-Identifier: BSD-2-Clause
> + *
> + * Copyright (c) 2022 StarFive Technology Co., Ltd.
> + *
> + * Author: Jun Liang Tan <junliang.tan at linux.starfivetech.com>
> + */
> +
> +#include <sbi/riscv_io.h>
> +#include <sbi/sbi_console.h>
> +#include <sbi_utils/serial/cadence-uart.h>
> +
> +/* clang-format off */
> +
> +#define UART_REG_OFFSET_CTRL           0x00    /* Control register */
> +#define UART_REG_OFFSET_MODE           0x04    /* Mode register */
> +#define UART_REG_OFFSET_CH_STS         0x2C    /* Channel status register */
> +#define UART_REG_OFFSET_RX_TX_FIFO     0x30    /* RX and TX FIFO register */
> +#define UART_REG_OFFSET_INT_DIS                0x0C    /* Interrupt disable register */
> +#define UART_REG_OFFSET_BRG    0x18    /* Baud rate generator register */
> +#define UART_REG_OFFSET_BRD    0x34    /* Baud rate divider register */
> +
> +/* Control register, 0x00 */
> +#define UART_CR_TX_DIS 0x00000020      /* TX disabled, bit 5 */
> +#define UART_CR_TX_EN  0x00000010      /* TX enabled, bit 4 */
> +#define UART_CR_RX_DIS 0x00000008      /* RX disabled, bit 3 */
> +#define UART_CR_RX_EN  0x00000004      /* RX enabled, bit 2*/
> +#define UART_CR_TXRST  0x00000002      /* TX logic reset, bit 1 */
> +#define UART_CR_RXRST  0x00000001      /* RX logic reset, bit 0 */
> +
> +/* Channel status register, 0x2C */
> +#define        UART_CHSR_TXFULL        0x00000010      /* TX FIFO full, bit 4 */
> +#define        UART_CHSR_RXEMPTY       0x00000002      /* RX FIFO empty, bit 1 */
> +
> +/* Mode register, 0x04 */
> +#define UART_MR_PARITY_NONE    0x00000020      /* No parity set */
> +
> +/* Baud rate generator register, 0x18 */
> +#define UART_BRG_CLK_DIVISOR   0x00000001      /* baud_sample = sel_clk */
> +
> +/* clang-format on */
> +
> +static volatile void *uart_base;
> +static u32 uart_in_freq;
> +static u32 uart_baudrate;
> +
> +/**
> + * Find minimum divisor divides in_freq to max_target_hz;
> + * Based on SiFive UART driver (sifive-uart.c)
> + */
> +static inline unsigned int uart_min_clk_divisor(uint64_t in_freq,
> +                                               uint64_t max_target_hz)
> +{
> +       uint64_t quotient = (in_freq + max_target_hz - 1) / (max_target_hz);
> +       /* Avoid underflow */
> +       if (quotient == 0)
> +               return 0;
> +       else
> +               return quotient - 1;
> +}
> +
> +static u32 get_reg(u32 offset)
> +{
> +       return readl(uart_base + offset);
> +}
> +
> +static void set_reg(u32 offset, u32 val)
> +{
> +       writel(val, uart_base + offset);
> +}
> +
> +static void cadence_uart_putc(char ch)
> +{
> +       while (get_reg(UART_REG_OFFSET_CH_STS) & UART_CHSR_TXFULL)
> +               ;
> +
> +       set_reg(UART_REG_OFFSET_RX_TX_FIFO, ch);
> +}
> +
> +static int cadence_uart_getc(void)
> +{
> +       u32 ret = get_reg(UART_REG_OFFSET_CH_STS);
> +
> +       if (!(ret & UART_CHSR_RXEMPTY))
> +               return get_reg(UART_REG_OFFSET_RX_TX_FIFO);
> +       return -1;
> +}
> +
> +static struct sbi_console_device cadence_console = {
> +       .name = "cadence_uart",
> +       .console_putc = cadence_uart_putc,
> +       .console_getc = cadence_uart_getc
> +};
> +
> +int cadence_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
> +{
> +       uart_base     = (volatile void *)base;
> +       uart_in_freq  = in_freq;
> +       uart_baudrate = baudrate;
> +
> +       /* Disable interrupts */
> +       set_reg(UART_REG_OFFSET_INT_DIS, 0xFFFFFFFF);
> +       /* Disable TX RX */
> +       set_reg(UART_REG_OFFSET_CTRL, UART_CR_TX_DIS | UART_CR_RX_DIS);
> +       /* Configure baudrate */
> +       if (in_freq) {
> +               set_reg(UART_REG_OFFSET_BRG, UART_BRG_CLK_DIVISOR);
> +               set_reg(UART_REG_OFFSET_BRD,
> +                       uart_min_clk_divisor(in_freq, baudrate));
> +       }
> +       /* Software reset TX RX data path and enable TX RX */
> +       set_reg(UART_REG_OFFSET_CTRL, UART_CR_TXRST | UART_CR_RXRST
> +               | UART_CR_TX_EN | UART_CR_RX_EN);
> +       /* Set:
> +        * 1 stop bit, bits[07:06] = 0x00,
> +        * no parity set, bits[05:03] = 0x100,
> +        * 8 bits character length, bits[02:01] = 0x00,
> +        * sel_clk = uart_clk, bit[0] = 0x0
> +        */
> +       set_reg(UART_REG_OFFSET_MODE, UART_MR_PARITY_NONE);
> +
> +       sbi_console_set_device(&cadence_console);
> +
> +       return 0;
> +}
> diff --git a/lib/utils/serial/fdt_serial.c b/lib/utils/serial/fdt_serial.c
> index f73d26a..460f516 100644
> --- a/lib/utils/serial/fdt_serial.c
> +++ b/lib/utils/serial/fdt_serial.c
> @@ -13,6 +13,7 @@
>  #include <sbi_utils/fdt/fdt_helper.h>
>  #include <sbi_utils/serial/fdt_serial.h>
>
> +extern struct fdt_serial fdt_serial_cadence;

This change is not required.

>  extern struct fdt_serial fdt_serial_uart8250;
>  extern struct fdt_serial fdt_serial_sifive;
>  extern struct fdt_serial fdt_serial_litex;
> @@ -21,6 +22,7 @@ extern struct fdt_serial fdt_serial_shakti;
>  extern struct fdt_serial fdt_serial_gaisler;
>
>  static struct fdt_serial *serial_drivers[] = {
> +       &fdt_serial_cadence,

This change is not required as well.

As-per latest OpenSBI, you just need following two lines
in lib/utils/serial/objects.mk:
carray-fdt_serial_drivers-y += fdt_serial_cadence
libsbiutils-objs-y += serial/fdt_serial_cadence.o


>         &fdt_serial_uart8250,
>         &fdt_serial_sifive,
>         &fdt_serial_litex,
> diff --git a/lib/utils/serial/fdt_serial_cadence.c b/lib/utils/serial/fdt_serial_cadence.c
> new file mode 100644
> index 0000000..f6f13d6
> --- /dev/null
> +++ b/lib/utils/serial/fdt_serial_cadence.c
> @@ -0,0 +1,35 @@
> +/*
> + * SPDX-License-Identifier: BSD-2-Clause
> + *
> + * Copyright (c) 2022 StarFive Technology Co., Ltd.
> + *
> + * Author: Jun Liang Tan <junliang.tan at linux.starfivetech.com>
> + */
> +
> +#include <sbi_utils/fdt/fdt_helper.h>
> +#include <sbi_utils/serial/fdt_serial.h>
> +#include <sbi_utils/serial/cadence-uart.h>
> +
> +static int serial_cadence_init(void *fdt, int nodeoff,
> +                              const struct fdt_match *match)
> +{
> +       int rc;
> +       struct platform_uart_data uart;
> +
> +       rc = fdt_parse_cadence_uart_node(fdt, nodeoff, &uart);
> +       if (rc)
> +               return rc;
> +
> +       return cadence_uart_init(uart.addr, uart.freq, uart.baud);
> +}
> +
> +static const struct fdt_match serial_cadence_match[] = {
> +       { .compatible = "cdns,uart-r1p12" },
> +       { .compatible = "starfive,jh8100-uart" },
> +       { },
> +};
> +
> +struct fdt_serial fdt_serial_cadence = {
> +       .match_table = serial_cadence_match,
> +       .init = serial_cadence_init
> +};
> diff --git a/lib/utils/serial/objects.mk b/lib/utils/serial/objects.mk
> index 4f751ba..c201d76 100644
> --- a/lib/utils/serial/objects.mk
> +++ b/lib/utils/serial/objects.mk
> @@ -8,12 +8,14 @@
>  #
>
>  libsbiutils-objs-y += serial/fdt_serial.o
> +libsbiutils-objs-y += serial/fdt_serial_cadence.o
>  libsbiutils-objs-y += serial/fdt_serial_gaisler.o
>  libsbiutils-objs-y += serial/fdt_serial_htif.o
>  libsbiutils-objs-y += serial/fdt_serial_shakti.o
>  libsbiutils-objs-y += serial/fdt_serial_sifive.o
>  libsbiutils-objs-y += serial/fdt_serial_litex.o
>  libsbiutils-objs-y += serial/fdt_serial_uart8250.o
> +libsbiutils-objs-y += serial/cadence-uart.o
>  libsbiutils-objs-y += serial/gaisler-uart.o
>  libsbiutils-objs-y += serial/shakti-uart.o
>  libsbiutils-objs-y += serial/sifive-uart.o

Please see the above comment. This is not aligned with
latest OpenSBI sources.

Regards,
Anup

> --
> 2.25.1
>
>
>
> --
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi


From anup at brainfault.org  Fri Jul  8 05:14:24 2022
From: anup at brainfault.org (Anup Patel)
Date: Fri, 8 Jul 2022 17:44:24 +0530
Subject: [PATCH] lib: utils: serial: Add Cadence UART driver
In-Reply-To: <CAAhSdy3Wpzhc=qhWE_qEZgS3yL3W52q1wK04gf_YUca3YKi4Fg@mail.gmail.com>
References: <20220708055005.26295-1-junliang.tan@linux.starfivetech.com> <CAAhSdy3Wpzhc=qhWE_qEZgS3yL3W52q1wK04gf_YUca3YKi4Fg@mail.gmail.com>
Message-ID: <CAAhSdy3qh-Gts5EO5U_zW0p_ipg3FLQkV_hE3HxCvv8a5BiFhQ@mail.gmail.com>

On Fri, Jul 8, 2022 at 5:40 PM Anup Patel <anup at brainfault.org> wrote:
>
> On Fri, Jul 8, 2022 at 11:21 AM Jun Liang Tan
> <junliang.tan at linux.starfivetech.com> wrote:
> >
> > Add Cadence UART driver
> >
> > Signed-off-by: Jun Liang Tan <junliang.tan at linux.starfivetech.com>
> > Signed-off-by: Wei Liang Lim <weiliang.lim at linux.starfivetech.com>
> > ---
> >  include/sbi_utils/fdt/fdt_helper.h      |   3 +
> >  include/sbi_utils/serial/cadence-uart.h |  16 +++
> >  lib/utils/fdt/fdt_helper.c              |  35 +++++++
> >  lib/utils/serial/cadence-uart.c         | 125 ++++++++++++++++++++++++
> >  lib/utils/serial/fdt_serial.c           |   2 +
> >  lib/utils/serial/fdt_serial_cadence.c   |  35 +++++++
> >  lib/utils/serial/objects.mk             |   2 +
> >  7 files changed, 218 insertions(+)
> >  create mode 100644 include/sbi_utils/serial/cadence-uart.h
> >  create mode 100644 lib/utils/serial/cadence-uart.c
> >  create mode 100644 lib/utils/serial/fdt_serial_cadence.c
> >
> > diff --git a/include/sbi_utils/fdt/fdt_helper.h b/include/sbi_utils/fdt/fdt_helper.h
> > index 24fee7a..1166208 100644
> > --- a/include/sbi_utils/fdt/fdt_helper.h
> > +++ b/include/sbi_utils/fdt/fdt_helper.h
> > @@ -53,6 +53,9 @@ int fdt_parse_max_hart_id(void *fdt, u32 *max_hartid);
> >
> >  int fdt_parse_timebase_frequency(void *fdt, unsigned long *freq);
> >
> > +int fdt_parse_cadence_uart_node(void *fdt, int nodeoffset,
> > +                               struct platform_uart_data *uart);
> > +
> >  int fdt_parse_gaisler_uart_node(void *fdt, int nodeoffset,
> >                                 struct platform_uart_data *uart);
> >
> > diff --git a/include/sbi_utils/serial/cadence-uart.h b/include/sbi_utils/serial/cadence-uart.h
> > new file mode 100644
> > index 0000000..e75fb95
> > --- /dev/null
> > +++ b/include/sbi_utils/serial/cadence-uart.h
> > @@ -0,0 +1,16 @@
> > +/*
> > + * SPDX-License-Identifier: BSD-2-Clause
> > + *
> > + * Copyright (c) 2022 StarFive Technology Co., Ltd.
> > + *
> > + * Author: Jun Liang Tan <junliang.tan at linux.starfivetech.com>
> > + */
> > +
> > +#ifndef __SERIAL_CADENCE_UART_H__
> > +#define __SERIAL_CADENCE_UART_H__
> > +
> > +#include <sbi/sbi_types.h>
> > +
> > +int cadence_uart_init(unsigned long base, u32 in_freq, u32 baudrate);
> > +
> > +#endif
> > diff --git a/lib/utils/fdt/fdt_helper.c b/lib/utils/fdt/fdt_helper.c
> > index 5bf4021..3ad0a5e 100644
> > --- a/lib/utils/fdt/fdt_helper.c
> > +++ b/lib/utils/fdt/fdt_helper.c
> > @@ -290,6 +290,41 @@ int fdt_parse_timebase_frequency(void *fdt, unsigned long *freq)
> >         return 0;
> >  }
> >
> > +int fdt_parse_cadence_uart_node(void *fdt, int nodeoffset,
> > +                               struct platform_uart_data *uart)
> > +{
> > +       int len, rc;
> > +       const fdt32_t *val;
> > +       u64 reg_addr, reg_size;
> > +
> > +       if (nodeoffset < 0 || !uart || !fdt)
> > +               return SBI_ENODEV;
> > +
> > +       rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
> > +                                   &reg_addr, &reg_size);
> > +       if (rc < 0 || !reg_addr || !reg_size)
> > +               return SBI_ENODEV;
> > +       uart->addr = reg_addr;
> > +
> > +       /**
> > +        * UART address is mandatory. clock-frequency and current-speed
> > +        * may not be present. Don't return error.
> > +        */
> > +       val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "clock-frequency", &len);
> > +       if (len > 0 && val)
> > +               uart->freq = fdt32_to_cpu(*val);
> > +       else
> > +               uart->freq = DEFAULT_UART_FREQ;
> > +
> > +       val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "current-speed", &len);
> > +       if (len > 0 && val)
> > +               uart->baud = fdt32_to_cpu(*val);
> > +       else
> > +               uart->baud = DEFAULT_UART_BAUD;
> > +
> > +       return 0;
> > +}
> > +
>
> Instead of adding one more UART node parsing function better
> rename fdt_parse_uart8250() to fdt_parse_uart() and use it in
> place of fdt_parse_cadence_uart_node().

I meant rename fdt_parse_uart8250_node() to fdt_parse_uart_node().

Regards,
Anup

>
> >  int fdt_parse_gaisler_uart_node(void *fdt, int nodeoffset,
> >                                 struct platform_uart_data *uart)
> >  {
> > diff --git a/lib/utils/serial/cadence-uart.c b/lib/utils/serial/cadence-uart.c
> > new file mode 100644
> > index 0000000..ffa79f8
> > --- /dev/null
> > +++ b/lib/utils/serial/cadence-uart.c
> > @@ -0,0 +1,125 @@
> > +/*
> > + * SPDX-License-Identifier: BSD-2-Clause
> > + *
> > + * Copyright (c) 2022 StarFive Technology Co., Ltd.
> > + *
> > + * Author: Jun Liang Tan <junliang.tan at linux.starfivetech.com>
> > + */
> > +
> > +#include <sbi/riscv_io.h>
> > +#include <sbi/sbi_console.h>
> > +#include <sbi_utils/serial/cadence-uart.h>
> > +
> > +/* clang-format off */
> > +
> > +#define UART_REG_OFFSET_CTRL           0x00    /* Control register */
> > +#define UART_REG_OFFSET_MODE           0x04    /* Mode register */
> > +#define UART_REG_OFFSET_CH_STS         0x2C    /* Channel status register */
> > +#define UART_REG_OFFSET_RX_TX_FIFO     0x30    /* RX and TX FIFO register */
> > +#define UART_REG_OFFSET_INT_DIS                0x0C    /* Interrupt disable register */
> > +#define UART_REG_OFFSET_BRG    0x18    /* Baud rate generator register */
> > +#define UART_REG_OFFSET_BRD    0x34    /* Baud rate divider register */
> > +
> > +/* Control register, 0x00 */
> > +#define UART_CR_TX_DIS 0x00000020      /* TX disabled, bit 5 */
> > +#define UART_CR_TX_EN  0x00000010      /* TX enabled, bit 4 */
> > +#define UART_CR_RX_DIS 0x00000008      /* RX disabled, bit 3 */
> > +#define UART_CR_RX_EN  0x00000004      /* RX enabled, bit 2*/
> > +#define UART_CR_TXRST  0x00000002      /* TX logic reset, bit 1 */
> > +#define UART_CR_RXRST  0x00000001      /* RX logic reset, bit 0 */
> > +
> > +/* Channel status register, 0x2C */
> > +#define        UART_CHSR_TXFULL        0x00000010      /* TX FIFO full, bit 4 */
> > +#define        UART_CHSR_RXEMPTY       0x00000002      /* RX FIFO empty, bit 1 */
> > +
> > +/* Mode register, 0x04 */
> > +#define UART_MR_PARITY_NONE    0x00000020      /* No parity set */
> > +
> > +/* Baud rate generator register, 0x18 */
> > +#define UART_BRG_CLK_DIVISOR   0x00000001      /* baud_sample = sel_clk */
> > +
> > +/* clang-format on */
> > +
> > +static volatile void *uart_base;
> > +static u32 uart_in_freq;
> > +static u32 uart_baudrate;
> > +
> > +/**
> > + * Find minimum divisor divides in_freq to max_target_hz;
> > + * Based on SiFive UART driver (sifive-uart.c)
> > + */
> > +static inline unsigned int uart_min_clk_divisor(uint64_t in_freq,
> > +                                               uint64_t max_target_hz)
> > +{
> > +       uint64_t quotient = (in_freq + max_target_hz - 1) / (max_target_hz);
> > +       /* Avoid underflow */
> > +       if (quotient == 0)
> > +               return 0;
> > +       else
> > +               return quotient - 1;
> > +}
> > +
> > +static u32 get_reg(u32 offset)
> > +{
> > +       return readl(uart_base + offset);
> > +}
> > +
> > +static void set_reg(u32 offset, u32 val)
> > +{
> > +       writel(val, uart_base + offset);
> > +}
> > +
> > +static void cadence_uart_putc(char ch)
> > +{
> > +       while (get_reg(UART_REG_OFFSET_CH_STS) & UART_CHSR_TXFULL)
> > +               ;
> > +
> > +       set_reg(UART_REG_OFFSET_RX_TX_FIFO, ch);
> > +}
> > +
> > +static int cadence_uart_getc(void)
> > +{
> > +       u32 ret = get_reg(UART_REG_OFFSET_CH_STS);
> > +
> > +       if (!(ret & UART_CHSR_RXEMPTY))
> > +               return get_reg(UART_REG_OFFSET_RX_TX_FIFO);
> > +       return -1;
> > +}
> > +
> > +static struct sbi_console_device cadence_console = {
> > +       .name = "cadence_uart",
> > +       .console_putc = cadence_uart_putc,
> > +       .console_getc = cadence_uart_getc
> > +};
> > +
> > +int cadence_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
> > +{
> > +       uart_base     = (volatile void *)base;
> > +       uart_in_freq  = in_freq;
> > +       uart_baudrate = baudrate;
> > +
> > +       /* Disable interrupts */
> > +       set_reg(UART_REG_OFFSET_INT_DIS, 0xFFFFFFFF);
> > +       /* Disable TX RX */
> > +       set_reg(UART_REG_OFFSET_CTRL, UART_CR_TX_DIS | UART_CR_RX_DIS);
> > +       /* Configure baudrate */
> > +       if (in_freq) {
> > +               set_reg(UART_REG_OFFSET_BRG, UART_BRG_CLK_DIVISOR);
> > +               set_reg(UART_REG_OFFSET_BRD,
> > +                       uart_min_clk_divisor(in_freq, baudrate));
> > +       }
> > +       /* Software reset TX RX data path and enable TX RX */
> > +       set_reg(UART_REG_OFFSET_CTRL, UART_CR_TXRST | UART_CR_RXRST
> > +               | UART_CR_TX_EN | UART_CR_RX_EN);
> > +       /* Set:
> > +        * 1 stop bit, bits[07:06] = 0x00,
> > +        * no parity set, bits[05:03] = 0x100,
> > +        * 8 bits character length, bits[02:01] = 0x00,
> > +        * sel_clk = uart_clk, bit[0] = 0x0
> > +        */
> > +       set_reg(UART_REG_OFFSET_MODE, UART_MR_PARITY_NONE);
> > +
> > +       sbi_console_set_device(&cadence_console);
> > +
> > +       return 0;
> > +}
> > diff --git a/lib/utils/serial/fdt_serial.c b/lib/utils/serial/fdt_serial.c
> > index f73d26a..460f516 100644
> > --- a/lib/utils/serial/fdt_serial.c
> > +++ b/lib/utils/serial/fdt_serial.c
> > @@ -13,6 +13,7 @@
> >  #include <sbi_utils/fdt/fdt_helper.h>
> >  #include <sbi_utils/serial/fdt_serial.h>
> >
> > +extern struct fdt_serial fdt_serial_cadence;
>
> This change is not required.
>
> >  extern struct fdt_serial fdt_serial_uart8250;
> >  extern struct fdt_serial fdt_serial_sifive;
> >  extern struct fdt_serial fdt_serial_litex;
> > @@ -21,6 +22,7 @@ extern struct fdt_serial fdt_serial_shakti;
> >  extern struct fdt_serial fdt_serial_gaisler;
> >
> >  static struct fdt_serial *serial_drivers[] = {
> > +       &fdt_serial_cadence,
>
> This change is not required as well.
>
> As-per latest OpenSBI, you just need following two lines
> in lib/utils/serial/objects.mk:
> carray-fdt_serial_drivers-y += fdt_serial_cadence
> libsbiutils-objs-y += serial/fdt_serial_cadence.o
>
>
> >         &fdt_serial_uart8250,
> >         &fdt_serial_sifive,
> >         &fdt_serial_litex,
> > diff --git a/lib/utils/serial/fdt_serial_cadence.c b/lib/utils/serial/fdt_serial_cadence.c
> > new file mode 100644
> > index 0000000..f6f13d6
> > --- /dev/null
> > +++ b/lib/utils/serial/fdt_serial_cadence.c
> > @@ -0,0 +1,35 @@
> > +/*
> > + * SPDX-License-Identifier: BSD-2-Clause
> > + *
> > + * Copyright (c) 2022 StarFive Technology Co., Ltd.
> > + *
> > + * Author: Jun Liang Tan <junliang.tan at linux.starfivetech.com>
> > + */
> > +
> > +#include <sbi_utils/fdt/fdt_helper.h>
> > +#include <sbi_utils/serial/fdt_serial.h>
> > +#include <sbi_utils/serial/cadence-uart.h>
> > +
> > +static int serial_cadence_init(void *fdt, int nodeoff,
> > +                              const struct fdt_match *match)
> > +{
> > +       int rc;
> > +       struct platform_uart_data uart;
> > +
> > +       rc = fdt_parse_cadence_uart_node(fdt, nodeoff, &uart);
> > +       if (rc)
> > +               return rc;
> > +
> > +       return cadence_uart_init(uart.addr, uart.freq, uart.baud);
> > +}
> > +
> > +static const struct fdt_match serial_cadence_match[] = {
> > +       { .compatible = "cdns,uart-r1p12" },
> > +       { .compatible = "starfive,jh8100-uart" },
> > +       { },
> > +};
> > +
> > +struct fdt_serial fdt_serial_cadence = {
> > +       .match_table = serial_cadence_match,
> > +       .init = serial_cadence_init
> > +};
> > diff --git a/lib/utils/serial/objects.mk b/lib/utils/serial/objects.mk
> > index 4f751ba..c201d76 100644
> > --- a/lib/utils/serial/objects.mk
> > +++ b/lib/utils/serial/objects.mk
> > @@ -8,12 +8,14 @@
> >  #
> >
> >  libsbiutils-objs-y += serial/fdt_serial.o
> > +libsbiutils-objs-y += serial/fdt_serial_cadence.o
> >  libsbiutils-objs-y += serial/fdt_serial_gaisler.o
> >  libsbiutils-objs-y += serial/fdt_serial_htif.o
> >  libsbiutils-objs-y += serial/fdt_serial_shakti.o
> >  libsbiutils-objs-y += serial/fdt_serial_sifive.o
> >  libsbiutils-objs-y += serial/fdt_serial_litex.o
> >  libsbiutils-objs-y += serial/fdt_serial_uart8250.o
> > +libsbiutils-objs-y += serial/cadence-uart.o
> >  libsbiutils-objs-y += serial/gaisler-uart.o
> >  libsbiutils-objs-y += serial/shakti-uart.o
> >  libsbiutils-objs-y += serial/sifive-uart.o
>
> Please see the above comment. This is not aligned with
> latest OpenSBI sources.
>
> Regards,
> Anup
>
> > --
> > 2.25.1
> >
> >
> >
> > --
> > opensbi mailing list
> > opensbi at lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/opensbi


From anup at brainfault.org  Fri Jul  8 05:40:23 2022
From: anup at brainfault.org (Anup Patel)
Date: Fri, 8 Jul 2022 18:10:23 +0530
Subject: [PATCH] sbi: add check for ipi device for hsm start
In-Reply-To: <20220708092729.246862-1-ben.dooks@sifive.com>
References: <20220708092729.246862-1-ben.dooks@sifive.com>
Message-ID: <CAAhSdy1+W3+0pzkarzokrOiP15J4a5xw0BJyAXwKqA3KjfxB_Q@mail.gmail.com>

On Fri, Jul 8, 2022 at 2:57 PM Ben Dooks <ben.dooks at sifive.com> wrote:
>
> If the ecall SBI_EXT_HSM_HART_START is called it might try to wake the
> secondary hart using sbi_ipi_raw_send() to send an IPI to the hart.
> This can fail if there is no IPI device but no error is returned from
> sbi_ipi_raw_send() so the ecall returns as if the action completed and
> the caller continues without noticing (in the case of Linux it just hangs
> waiting for the secondary hart to become active)
>
> Fix this by changing sbi_ipi_raw_send() to return and error, and if an
> error is returned, then return it via SBI_EXT_HSM_HART_START call.
>
> Signed-off-by: Ben Dooks <ben.dooks at sifive.com>

For consistency use "lib: sbi:" as patch subject prefix.

> ---
>  include/sbi/sbi_ipi.h |  2 +-
>  lib/sbi/sbi_hsm.c     |  4 +++-
>  lib/sbi/sbi_ipi.c     | 11 ++++++++---
>  3 files changed, 12 insertions(+), 5 deletions(-)
>
> diff --git a/include/sbi/sbi_ipi.h b/include/sbi/sbi_ipi.h
> index fb7d658..f6ac807 100644
> --- a/include/sbi/sbi_ipi.h
> +++ b/include/sbi/sbi_ipi.h
> @@ -75,7 +75,7 @@ int sbi_ipi_send_halt(ulong hmask, ulong hbase);
>
>  void sbi_ipi_process(void);
>
> -void sbi_ipi_raw_send(u32 target_hart);
> +int sbi_ipi_raw_send(u32 target_hart);
>
>  const struct sbi_ipi_device *sbi_ipi_get_device(void);
>
> diff --git a/lib/sbi/sbi_hsm.c b/lib/sbi/sbi_hsm.c
> index 1165acc..836008f 100644
> --- a/lib/sbi/sbi_hsm.c
> +++ b/lib/sbi/sbi_hsm.c
> @@ -289,7 +289,9 @@ int sbi_hsm_hart_start(struct sbi_scratch *scratch,
>            (hsm_device_has_hart_secondary_boot() && !init_count)) {
>                 return hsm_device_hart_start(hartid, scratch->warmboot_addr);
>         } else {
> -               sbi_ipi_raw_send(hartid);
> +               int rc = sbi_ipi_raw_send(hartid);
> +               if (rc)
> +                   return rc;
>         }
>
>         return 0;
> diff --git a/lib/sbi/sbi_ipi.c b/lib/sbi/sbi_ipi.c
> index 1014909..c2ede2d 100644
> --- a/lib/sbi/sbi_ipi.c
> +++ b/lib/sbi/sbi_ipi.c
> @@ -208,10 +208,15 @@ skip:
>         };
>  }
>
> -void sbi_ipi_raw_send(u32 target_hart)
> +int sbi_ipi_raw_send(u32 target_hart)
>  {
> -       if (ipi_dev && ipi_dev->ipi_send)
> -               ipi_dev->ipi_send(target_hart);
> +       if (!ipi_dev)
> +               return SBI_EINVAL;
> +       if (!ipi_dev->ipi_send)
> +               return SBI_EINVAL;

Instead of two separate "if ()" you can short-circuit into
one "if ()" like below:

if (!ipi_dev || !ipi_dev->ipi_send)
    return SBI_EINVAL;

> +
> +       ipi_dev->ipi_send(target_hart);
> +       return 0;
>  }
>
>  const struct sbi_ipi_device *sbi_ipi_get_device(void)
> --
> 2.35.1
>
>
> --
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi

Apart from minor comments above, this looks good to me.

Reviewed-by: Anup Patel <anup at brainfault.org>

Regards,
Anup


From ben.dooks at sifive.com  Fri Jul  8 07:40:25 2022
From: ben.dooks at sifive.com (Ben Dooks)
Date: Fri, 8 Jul 2022 15:40:25 +0100
Subject: [PATCH] sbi: add check for ipi device for hsm start
In-Reply-To: <CAAhSdy1+W3+0pzkarzokrOiP15J4a5xw0BJyAXwKqA3KjfxB_Q@mail.gmail.com>
References: <20220708092729.246862-1-ben.dooks@sifive.com>
 <CAAhSdy1+W3+0pzkarzokrOiP15J4a5xw0BJyAXwKqA3KjfxB_Q@mail.gmail.com>
Message-ID: <317f7249-7bd8-2350-4a26-1fcbe39cbe60@sifive.com>

On 08/07/2022 13:40, Anup Patel wrote:
> On Fri, Jul 8, 2022 at 2:57 PM Ben Dooks <ben.dooks at sifive.com> wrote:
>>
>> If the ecall SBI_EXT_HSM_HART_START is called it might try to wake the
>> secondary hart using sbi_ipi_raw_send() to send an IPI to the hart.
>> This can fail if there is no IPI device but no error is returned from
>> sbi_ipi_raw_send() so the ecall returns as if the action completed and
>> the caller continues without noticing (in the case of Linux it just hangs
>> waiting for the secondary hart to become active)
>>
>> Fix this by changing sbi_ipi_raw_send() to return and error, and if an
>> error is returned, then return it via SBI_EXT_HSM_HART_START call.
>>
>> Signed-off-by: Ben Dooks <ben.dooks at sifive.com>
> 
> For consistency use "lib: sbi:" as patch subject prefix.

ok, will fix.

>> ---
>>   include/sbi/sbi_ipi.h |  2 +-
>>   lib/sbi/sbi_hsm.c     |  4 +++-
>>   lib/sbi/sbi_ipi.c     | 11 ++++++++---
>>   3 files changed, 12 insertions(+), 5 deletions(-)
>>
>> diff --git a/include/sbi/sbi_ipi.h b/include/sbi/sbi_ipi.h
>> index fb7d658..f6ac807 100644
>> --- a/include/sbi/sbi_ipi.h
>> +++ b/include/sbi/sbi_ipi.h
>> @@ -75,7 +75,7 @@ int sbi_ipi_send_halt(ulong hmask, ulong hbase);
>>
>>   void sbi_ipi_process(void);
>>
>> -void sbi_ipi_raw_send(u32 target_hart);
>> +int sbi_ipi_raw_send(u32 target_hart);
>>
>>   const struct sbi_ipi_device *sbi_ipi_get_device(void);
>>
>> diff --git a/lib/sbi/sbi_hsm.c b/lib/sbi/sbi_hsm.c
>> index 1165acc..836008f 100644
>> --- a/lib/sbi/sbi_hsm.c
>> +++ b/lib/sbi/sbi_hsm.c
>> @@ -289,7 +289,9 @@ int sbi_hsm_hart_start(struct sbi_scratch *scratch,
>>             (hsm_device_has_hart_secondary_boot() && !init_count)) {
>>                  return hsm_device_hart_start(hartid, scratch->warmboot_addr);
>>          } else {
>> -               sbi_ipi_raw_send(hartid);
>> +               int rc = sbi_ipi_raw_send(hartid);
>> +               if (rc)
>> +                   return rc;
>>          }
>>
>>          return 0;
>> diff --git a/lib/sbi/sbi_ipi.c b/lib/sbi/sbi_ipi.c
>> index 1014909..c2ede2d 100644
>> --- a/lib/sbi/sbi_ipi.c
>> +++ b/lib/sbi/sbi_ipi.c
>> @@ -208,10 +208,15 @@ skip:
>>          };
>>   }
>>
>> -void sbi_ipi_raw_send(u32 target_hart)
>> +int sbi_ipi_raw_send(u32 target_hart)
>>   {
>> -       if (ipi_dev && ipi_dev->ipi_send)
>> -               ipi_dev->ipi_send(target_hart);
>> +       if (!ipi_dev)
>> +               return SBI_EINVAL;
>> +       if (!ipi_dev->ipi_send)
>> +               return SBI_EINVAL;
> 
> Instead of two separate "if ()" you can short-circuit into
> one "if ()" like below:
> 
> if (!ipi_dev || !ipi_dev->ipi_send)
>      return SBI_EINVAL;

ok, fixed.
I really don't like compound if statements like this, but will sort out.

>> +
>> +       ipi_dev->ipi_send(target_hart);
>> +       return 0;
>>   }
>>
>>   const struct sbi_ipi_device *sbi_ipi_get_device(void)
>> --
>> 2.35.1
>>
>>
>> --
>> opensbi mailing list
>> opensbi at lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/opensbi
> 
> Apart from minor comments above, this looks good to me.
> 
> Reviewed-by: Anup Patel <anup at brainfault.org>
> 
> Regards,
> Anup
> 

I'll repost this Monday.


From jrtc27 at jrtc27.com  Fri Jul  8 10:41:52 2022
From: jrtc27 at jrtc27.com (Jessica Clarke)
Date: Fri, 8 Jul 2022 18:41:52 +0100
Subject: [PATCH v3] lib: utils/serial: Update Shakti UART based on latest
 implementation
In-Reply-To: <20220708115244.556074-1-apatel@ventanamicro.com>
References: <20220708115244.556074-1-apatel@ventanamicro.com>
Message-ID: <17C8D809-438B-4C78-B507-7AE6029CCD2A@jrtc27.com>

On 8 Jul 2022, at 12:52, Anup Patel <apatel at ventanamicro.com> wrote:
> 
> From: Prasanna T <ptprasanna at gmail.com>
> 
> The age old version of Shakti UART was upgraded long back, but we missed
> updating the driver in OpenSBI. The old version of UART is not supported
> anymore, hence removed the inline comment which is also outdated now.

This part of the description is referencing a comment that was
*introduced* by an earlier version of this patch; there is no comment
in the source being removed and so this part of the commit message is
inaccurate.

I?m also rather concerned about reusing an existing compatible string
for an incompatible device, it seems like even if you?re dropping
support for an old UART the new one should have its own compatible with
the version suffix bumped. They have versions in them for a reason.

Jess

> Signed-off-by: Prasanna T <ptprasanna at gmail.com>
> Signed-off-by: Anup Patel <apatel at ventanamicro.com>
> ---
> Changes since v2:
> - Updated patch subject and description
> - Minor fix in shakti_uart_getc()
> ---
> lib/utils/serial/shakti-uart.c | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/lib/utils/serial/shakti-uart.c b/lib/utils/serial/shakti-uart.c
> index 5f2fe75..3556935 100644
> --- a/lib/utils/serial/shakti-uart.c
> +++ b/lib/utils/serial/shakti-uart.c
> @@ -19,21 +19,21 @@
> #define REG_RX_THRES	0x20
> 
> #define UART_TX_FULL  0x2
> +#define UART_RX_NOT_EMPTY 0x4
> #define UART_RX_FULL  0x8
> 
> static volatile char *uart_base;
> 
> static void shakti_uart_putc(char ch)
> {
> -	while((readw(uart_base + REG_STATUS) & UART_TX_FULL))
> +	while ((readb(uart_base + REG_STATUS) & UART_TX_FULL))
> 		;
> 	writeb(ch, uart_base + REG_TX);
> }
> 
> static int shakti_uart_getc(void)
> {
> -	u16 status = readw(uart_base + REG_STATUS);
> -	if (status & UART_RX_FULL)
> +	if (readb(uart_base + REG_STATUS) & UART_RX_NOT_EMPTY)
> 		return readb(uart_base + REG_RX);
> 	return -1;
> }
> -- 
> 2.34.1
> 
> 
> -- 
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi



From anup at brainfault.org  Fri Jul  8 10:49:33 2022
From: anup at brainfault.org (Anup Patel)
Date: Fri, 8 Jul 2022 23:19:33 +0530
Subject: [PATCH v3] lib: utils/serial: Update Shakti UART based on latest implementation
In-Reply-To: <17C8D809-438B-4C78-B507-7AE6029CCD2A@jrtc27.com>
References: <20220708115244.556074-1-apatel@ventanamicro.com> <17C8D809-438B-4C78-B507-7AE6029CCD2A@jrtc27.com>
Message-ID: <CAAhSdy17F5=jYBsZyYhcvcRt3Sp3zSO6r54yOPSBq8EtYZbUTw@mail.gmail.com>

On Fri, Jul 8, 2022 at 11:11 PM Jessica Clarke <jrtc27 at jrtc27.com> wrote:
>
> On 8 Jul 2022, at 12:52, Anup Patel <apatel at ventanamicro.com> wrote:
> >
> > From: Prasanna T <ptprasanna at gmail.com>
> >
> > The age old version of Shakti UART was upgraded long back, but we missed
> > updating the driver in OpenSBI. The old version of UART is not supported
> > anymore, hence removed the inline comment which is also outdated now.
>
> This part of the description is referencing a comment that was
> *introduced* by an earlier version of this patch; there is no comment
> in the source being removed and so this part of the commit message is
> inaccurate.
>
> I?m also rather concerned about reusing an existing compatible string
> for an incompatible device, it seems like even if you?re dropping
> support for an old UART the new one should have its own compatible with
> the version suffix bumped. They have versions in them for a reason.

My understanding is that they (Shakti folks) never used the old version
of UART on any actual silicon. It seems they are already using the newer
UART everywhere and they just forgot to change the code here.

@Prasanna Thandabani Is the above understanding correct ?

Regards,
Anup

>
> Jess
>
> > Signed-off-by: Prasanna T <ptprasanna at gmail.com>
> > Signed-off-by: Anup Patel <apatel at ventanamicro.com>
> > ---
> > Changes since v2:
> > - Updated patch subject and description
> > - Minor fix in shakti_uart_getc()
> > ---
> > lib/utils/serial/shakti-uart.c | 6 +++---
> > 1 file changed, 3 insertions(+), 3 deletions(-)
> >
> > diff --git a/lib/utils/serial/shakti-uart.c b/lib/utils/serial/shakti-uart.c
> > index 5f2fe75..3556935 100644
> > --- a/lib/utils/serial/shakti-uart.c
> > +++ b/lib/utils/serial/shakti-uart.c
> > @@ -19,21 +19,21 @@
> > #define REG_RX_THRES  0x20
> >
> > #define UART_TX_FULL  0x2
> > +#define UART_RX_NOT_EMPTY 0x4
> > #define UART_RX_FULL  0x8
> >
> > static volatile char *uart_base;
> >
> > static void shakti_uart_putc(char ch)
> > {
> > -     while((readw(uart_base + REG_STATUS) & UART_TX_FULL))
> > +     while ((readb(uart_base + REG_STATUS) & UART_TX_FULL))
> >               ;
> >       writeb(ch, uart_base + REG_TX);
> > }
> >
> > static int shakti_uart_getc(void)
> > {
> > -     u16 status = readw(uart_base + REG_STATUS);
> > -     if (status & UART_RX_FULL)
> > +     if (readb(uart_base + REG_STATUS) & UART_RX_NOT_EMPTY)
> >               return readb(uart_base + REG_RX);
> >       return -1;
> > }
> > --
> > 2.34.1
> >
> >
> > --
> > opensbi mailing list
> > opensbi at lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/opensbi
>


From ptprasanna at gmail.com  Sat Jul  9 09:43:14 2022
From: ptprasanna at gmail.com (Prasanna Thandabani)
Date: Sat, 9 Jul 2022 22:13:14 +0530
Subject: [PATCH v3] lib: utils/serial: Update Shakti UART based on latest implementation
In-Reply-To: <CAAhSdy17F5=jYBsZyYhcvcRt3Sp3zSO6r54yOPSBq8EtYZbUTw@mail.gmail.com>
References: <20220708115244.556074-1-apatel@ventanamicro.com>
 <17C8D809-438B-4C78-B507-7AE6029CCD2A@jrtc27.com> <CAAhSdy17F5=jYBsZyYhcvcRt3Sp3zSO6r54yOPSBq8EtYZbUTw@mail.gmail.com>
Message-ID: <CAKMAqee6_HmUJHi1NA9hgFgFXmdzNTSU2bGMB=bTciOVCd-XJg@mail.gmail.com>

Yes Anup,

That's right. The current UART is being used everywhere, and we have
realised the gap in OpenSBI V1.1 release ( realised that we didn't
update the OpenSBI Repo). Hence the patch.

Please let me know if more information would help.

Thanks
Prasanna
----Resending on Plain Text Mode----


On Fri, Jul 8, 2022 at 11:19 PM Anup Patel <anup at brainfault.org> wrote:
>
> On Fri, Jul 8, 2022 at 11:11 PM Jessica Clarke <jrtc27 at jrtc27.com> wrote:
> >
> > On 8 Jul 2022, at 12:52, Anup Patel <apatel at ventanamicro.com> wrote:
> > >
> > > From: Prasanna T <ptprasanna at gmail.com>
> > >
> > > The age old version of Shakti UART was upgraded long back, but we missed
> > > updating the driver in OpenSBI. The old version of UART is not supported
> > > anymore, hence removed the inline comment which is also outdated now.
> >
> > This part of the description is referencing a comment that was
> > *introduced* by an earlier version of this patch; there is no comment
> > in the source being removed and so this part of the commit message is
> > inaccurate.
> >
> > I?m also rather concerned about reusing an existing compatible string
> > for an incompatible device, it seems like even if you?re dropping
> > support for an old UART the new one should have its own compatible with
> > the version suffix bumped. They have versions in them for a reason.
>
> My understanding is that they (Shakti folks) never used the old version
> of UART on any actual silicon. It seems they are already using the newer
> UART everywhere and they just forgot to change the code here.
>
> @Prasanna Thandabani Is the above understanding correct ?
>
> Regards,
> Anup
>
> >
> > Jess
> >
> > > Signed-off-by: Prasanna T <ptprasanna at gmail.com>
> > > Signed-off-by: Anup Patel <apatel at ventanamicro.com>
> > > ---
> > > Changes since v2:
> > > - Updated patch subject and description
> > > - Minor fix in shakti_uart_getc()
> > > ---
> > > lib/utils/serial/shakti-uart.c | 6 +++---
> > > 1 file changed, 3 insertions(+), 3 deletions(-)
> > >
> > > diff --git a/lib/utils/serial/shakti-uart.c b/lib/utils/serial/shakti-uart.c
> > > index 5f2fe75..3556935 100644
> > > --- a/lib/utils/serial/shakti-uart.c
> > > +++ b/lib/utils/serial/shakti-uart.c
> > > @@ -19,21 +19,21 @@
> > > #define REG_RX_THRES  0x20
> > >
> > > #define UART_TX_FULL  0x2
> > > +#define UART_RX_NOT_EMPTY 0x4
> > > #define UART_RX_FULL  0x8
> > >
> > > static volatile char *uart_base;
> > >
> > > static void shakti_uart_putc(char ch)
> > > {
> > > -     while((readw(uart_base + REG_STATUS) & UART_TX_FULL))
> > > +     while ((readb(uart_base + REG_STATUS) & UART_TX_FULL))
> > >               ;
> > >       writeb(ch, uart_base + REG_TX);
> > > }
> > >
> > > static int shakti_uart_getc(void)
> > > {
> > > -     u16 status = readw(uart_base + REG_STATUS);
> > > -     if (status & UART_RX_FULL)
> > > +     if (readb(uart_base + REG_STATUS) & UART_RX_NOT_EMPTY)
> > >               return readb(uart_base + REG_RX);
> > >       return -1;
> > > }
> > > --
> > > 2.34.1
> > >
> > >
> > > --
> > > opensbi mailing list
> > > opensbi at lists.infradead.org
> > > http://lists.infradead.org/mailman/listinfo/opensbi
> >


From ben.dooks at sifive.com  Mon Jul 11 00:18:03 2022
From: ben.dooks at sifive.com (Ben Dooks)
Date: Mon, 11 Jul 2022 08:18:03 +0100
Subject: [PATCH v2] lib: sbi: add check for ipi device for hsm start
Message-ID: <20220711071803.408024-1-ben.dooks@sifive.com>

If the ecall SBI_EXT_HSM_HART_START is called it might try to wake the
secondary hart using sbi_ipi_raw_send() to send an IPI to the hart.
This can fail if there is no IPI device but no error is returned from
sbi_ipi_raw_send() so the ecall returns as if the action completed and
the caller continues without noticing (in the case of Linux it just hangs
waiting for the secondary hart to become active)

Fix this by changing sbi_ipi_raw_send() to return and error, and if an
error is returned, then return it via SBI_EXT_HSM_HART_START call.

Signed-off-by: Ben Dooks <ben.dooks at sifive.com>
--
Fixes for v2:
- fixed title by adding lib: prefix
- change two if() to one compound if()
---
 include/sbi/sbi_ipi.h | 2 +-
 lib/sbi/sbi_hsm.c     | 4 +++-
 lib/sbi/sbi_ipi.c     | 9 ++++++---
 3 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/include/sbi/sbi_ipi.h b/include/sbi/sbi_ipi.h
index fb7d658..f6ac807 100644
--- a/include/sbi/sbi_ipi.h
+++ b/include/sbi/sbi_ipi.h
@@ -75,7 +75,7 @@ int sbi_ipi_send_halt(ulong hmask, ulong hbase);
 
 void sbi_ipi_process(void);
 
-void sbi_ipi_raw_send(u32 target_hart);
+int sbi_ipi_raw_send(u32 target_hart);
 
 const struct sbi_ipi_device *sbi_ipi_get_device(void);
 
diff --git a/lib/sbi/sbi_hsm.c b/lib/sbi/sbi_hsm.c
index 1165acc..836008f 100644
--- a/lib/sbi/sbi_hsm.c
+++ b/lib/sbi/sbi_hsm.c
@@ -289,7 +289,9 @@ int sbi_hsm_hart_start(struct sbi_scratch *scratch,
 	   (hsm_device_has_hart_secondary_boot() && !init_count)) {
 		return hsm_device_hart_start(hartid, scratch->warmboot_addr);
 	} else {
-		sbi_ipi_raw_send(hartid);
+		int rc = sbi_ipi_raw_send(hartid);
+		if (rc)
+		    return rc;
 	}
 
 	return 0;
diff --git a/lib/sbi/sbi_ipi.c b/lib/sbi/sbi_ipi.c
index 1014909..7aafbbd 100644
--- a/lib/sbi/sbi_ipi.c
+++ b/lib/sbi/sbi_ipi.c
@@ -208,10 +208,13 @@ skip:
 	};
 }
 
-void sbi_ipi_raw_send(u32 target_hart)
+int sbi_ipi_raw_send(u32 target_hart)
 {
-	if (ipi_dev && ipi_dev->ipi_send)
-		ipi_dev->ipi_send(target_hart);
+	if (!ipi_dev || !ipi_dev->ipi_send)
+		return SBI_EINVAL;
+
+	ipi_dev->ipi_send(target_hart);
+	return 0;
 }
 
 const struct sbi_ipi_device *sbi_ipi_get_device(void)
-- 
2.35.1



From anup at brainfault.org  Mon Jul 11 03:13:59 2022
From: anup at brainfault.org (Anup Patel)
Date: Mon, 11 Jul 2022 15:43:59 +0530
Subject: [PATCH v2] lib: sbi: add check for ipi device for hsm start
In-Reply-To: <20220711071803.408024-1-ben.dooks@sifive.com>
References: <20220711071803.408024-1-ben.dooks@sifive.com>
Message-ID: <CAAhSdy2K01-mmW4BkbZMYtg6GPTm2sFgk=HzvUM4RxRxw_YgJg@mail.gmail.com>

On Mon, Jul 11, 2022 at 12:52 PM Ben Dooks <ben.dooks at sifive.com> wrote:
>
> If the ecall SBI_EXT_HSM_HART_START is called it might try to wake the
> secondary hart using sbi_ipi_raw_send() to send an IPI to the hart.
> This can fail if there is no IPI device but no error is returned from
> sbi_ipi_raw_send() so the ecall returns as if the action completed and
> the caller continues without noticing (in the case of Linux it just hangs
> waiting for the secondary hart to become active)
>
> Fix this by changing sbi_ipi_raw_send() to return and error, and if an
> error is returned, then return it via SBI_EXT_HSM_HART_START call.
>
> Signed-off-by: Ben Dooks <ben.dooks at sifive.com>

Looks good to me.

Reviewed-by: Anup Patel <anup at brainfault.org>

Applied this patch to the riscv/opensbi repo.

Thanks,
Anup

> --
> Fixes for v2:
> - fixed title by adding lib: prefix
> - change two if() to one compound if()
> ---
>  include/sbi/sbi_ipi.h | 2 +-
>  lib/sbi/sbi_hsm.c     | 4 +++-
>  lib/sbi/sbi_ipi.c     | 9 ++++++---
>  3 files changed, 10 insertions(+), 5 deletions(-)
>
> diff --git a/include/sbi/sbi_ipi.h b/include/sbi/sbi_ipi.h
> index fb7d658..f6ac807 100644
> --- a/include/sbi/sbi_ipi.h
> +++ b/include/sbi/sbi_ipi.h
> @@ -75,7 +75,7 @@ int sbi_ipi_send_halt(ulong hmask, ulong hbase);
>
>  void sbi_ipi_process(void);
>
> -void sbi_ipi_raw_send(u32 target_hart);
> +int sbi_ipi_raw_send(u32 target_hart);
>
>  const struct sbi_ipi_device *sbi_ipi_get_device(void);
>
> diff --git a/lib/sbi/sbi_hsm.c b/lib/sbi/sbi_hsm.c
> index 1165acc..836008f 100644
> --- a/lib/sbi/sbi_hsm.c
> +++ b/lib/sbi/sbi_hsm.c
> @@ -289,7 +289,9 @@ int sbi_hsm_hart_start(struct sbi_scratch *scratch,
>            (hsm_device_has_hart_secondary_boot() && !init_count)) {
>                 return hsm_device_hart_start(hartid, scratch->warmboot_addr);
>         } else {
> -               sbi_ipi_raw_send(hartid);
> +               int rc = sbi_ipi_raw_send(hartid);
> +               if (rc)
> +                   return rc;
>         }
>
>         return 0;
> diff --git a/lib/sbi/sbi_ipi.c b/lib/sbi/sbi_ipi.c
> index 1014909..7aafbbd 100644
> --- a/lib/sbi/sbi_ipi.c
> +++ b/lib/sbi/sbi_ipi.c
> @@ -208,10 +208,13 @@ skip:
>         };
>  }
>
> -void sbi_ipi_raw_send(u32 target_hart)
> +int sbi_ipi_raw_send(u32 target_hart)
>  {
> -       if (ipi_dev && ipi_dev->ipi_send)
> -               ipi_dev->ipi_send(target_hart);
> +       if (!ipi_dev || !ipi_dev->ipi_send)
> +               return SBI_EINVAL;
> +
> +       ipi_dev->ipi_send(target_hart);
> +       return 0;
>  }
>
>  const struct sbi_ipi_device *sbi_ipi_get_device(void)
> --
> 2.35.1
>
>
> --
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi


From ben.dooks at sifive.com  Tue Jul 12 02:03:20 2022
From: ben.dooks at sifive.com (Ben Dooks)
Date: Tue, 12 Jul 2022 10:03:20 +0100
Subject: [PATCH] platform: move da9063-reset driver to shareable code
Message-ID: <20220712090320.564729-1-ben.dooks@sifive.com>

The da9063 reset code may be useful for other users, so move this
out to a new file from the current sifive_fu740.c user. This can
then be called from the relevant machines.

Signed-off-by: Ben Dooks <ben.dooks at sifive.com>
---
 platform/generic/da9063_reset.c         | 229 ++++++++++++++++++++++++
 platform/generic/include/da9063_reset.h |  14 ++
 platform/generic/objects.mk             |   3 +
 platform/generic/sifive/fu740.c         | 197 +-------------------
 4 files changed, 248 insertions(+), 195 deletions(-)
 create mode 100644 platform/generic/da9063_reset.c
 create mode 100644 platform/generic/include/da9063_reset.h

diff --git a/platform/generic/da9063_reset.c b/platform/generic/da9063_reset.c
new file mode 100644
index 0000000..50c987d
--- /dev/null
+++ b/platform/generic/da9063_reset.c
@@ -0,0 +1,229 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021 SiFive
+ * Copyright (c) 2021 YADRO
+ *
+ * Split from sifive_fu740.c
+ *
+ * Authors:
+ *   David Abdurachmanov <david.abdurachmanov at sifive.com>
+ *   Nikita Shubin <n.shubin at yadro.com>
+ */
+
+#include <platform_override.h>
+#include <da9063_reset.h>
+#include <libfdt.h>
+#include <sbi/sbi_error.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_system.h>
+#include <sbi/sbi_console.h>
+#include <sbi_utils/fdt/fdt_helper.h>
+#include <sbi_utils/fdt/fdt_fixup.h>
+#include <sbi_utils/reset/fdt_reset.h>
+#include <sbi_utils/i2c/fdt_i2c.h>
+
+#define DA9063_REG_PAGE_CON		0x00
+#define DA9063_REG_CONTROL_A		0x0e
+#define DA9063_REG_CONTROL_D		0x11
+#define DA9063_REG_CONTROL_F		0x13
+#define DA9063_REG_DEVICE_ID		0x81
+
+#define DA9063_CONTROL_A_M_POWER1_EN	(1 << 6)
+#define DA9063_CONTROL_A_M_POWER_EN	(1 << 5)
+#define DA9063_CONTROL_A_STANDBY	(1 << 3)
+
+#define DA9063_CONTROL_D_TWDSCALE_MASK	0x07
+
+#define DA9063_CONTROL_F_WAKEUP	(1 << 2)
+#define DA9063_CONTROL_F_SHUTDOWN	(1 << 1)
+
+#define PMIC_CHIP_ID_DA9063		0x61
+
+static struct {
+	struct i2c_adapter *adapter;
+	uint32_t reg;
+} da9063;
+
+static int da9063_system_reset_check(u32 type, u32 reason)
+{
+	switch (type) {
+	case SBI_SRST_RESET_TYPE_SHUTDOWN:
+		return 1;
+	case SBI_SRST_RESET_TYPE_COLD_REBOOT:
+	case SBI_SRST_RESET_TYPE_WARM_REBOOT:
+		return 255;
+	}
+
+	return 0;
+}
+
+static inline int da9063_sanity_check(struct i2c_adapter *adap, uint32_t reg)
+{
+	uint8_t val;
+	int rc = i2c_adapter_reg_write(adap, reg, DA9063_REG_PAGE_CON, 0x02);
+
+	if (rc)
+		return rc;
+
+	/* check set page*/
+	rc = i2c_adapter_reg_read(adap, reg, 0x0, &val);
+	if (rc)
+		return rc;
+
+	if (val != 0x02)
+		return SBI_ENODEV;
+
+	/* read and check device id */
+	rc = i2c_adapter_reg_read(adap, reg, DA9063_REG_DEVICE_ID, &val);
+	if (rc)
+		return rc;
+
+	if (val != PMIC_CHIP_ID_DA9063)
+		return SBI_ENODEV;
+
+	return 0;
+}
+
+static inline int da9063_stop_watchdog(struct i2c_adapter *adap, uint32_t reg)
+{
+	uint8_t val;
+	int rc = i2c_adapter_reg_write(adap, reg,
+					DA9063_REG_PAGE_CON, 0x00);
+
+	if (rc)
+		return rc;
+
+	rc = i2c_adapter_reg_read(adap, reg, DA9063_REG_CONTROL_D, &val);
+	if (rc)
+		return rc;
+
+	if ((val & DA9063_CONTROL_D_TWDSCALE_MASK) == 0)
+		return 0;
+
+	val &= ~DA9063_CONTROL_D_TWDSCALE_MASK;
+
+	return i2c_adapter_reg_write(adap, reg, DA9063_REG_CONTROL_D, val);
+}
+
+static inline int da9063_shutdown(struct i2c_adapter *adap, uint32_t reg)
+{
+	int rc = i2c_adapter_reg_write(adap, reg,
+					DA9063_REG_PAGE_CON, 0x00);
+
+	if (rc)
+		return rc;
+
+	return i2c_adapter_reg_write(adap, reg,
+				     DA9063_REG_CONTROL_F,
+				     DA9063_CONTROL_F_SHUTDOWN);
+}
+
+static inline int da9063_reset(struct i2c_adapter *adap, uint32_t reg)
+{
+	int rc = i2c_adapter_reg_write(adap, reg,
+					DA9063_REG_PAGE_CON, 0x00);
+
+	if (rc)
+		return rc;
+
+	rc = i2c_adapter_reg_write(adap, reg,
+				   DA9063_REG_CONTROL_F,
+				   DA9063_CONTROL_F_WAKEUP);
+	if (rc)
+		return rc;
+
+	return i2c_adapter_reg_write(adap, reg,
+				DA9063_REG_CONTROL_A,
+				DA9063_CONTROL_A_M_POWER1_EN |
+				DA9063_CONTROL_A_M_POWER_EN |
+				DA9063_CONTROL_A_STANDBY);
+}
+
+static void da9063_system_reset(u32 type, u32 reason)
+{
+	struct i2c_adapter *adap = da9063.adapter;
+	uint32_t reg = da9063.reg;
+	int rc;
+
+	if (adap) {
+		/* sanity check */
+		rc = da9063_sanity_check(adap, reg);
+		if (rc) {
+			sbi_printf("%s: chip is not da9063 PMIC\n", __func__);
+			goto skip_reset;
+		}
+
+		switch (type) {
+		case SBI_SRST_RESET_TYPE_SHUTDOWN:
+			da9063_shutdown(adap, reg);
+			break;
+		case SBI_SRST_RESET_TYPE_COLD_REBOOT:
+		case SBI_SRST_RESET_TYPE_WARM_REBOOT:
+			da9063_stop_watchdog(adap, reg);
+			da9063_reset(adap, reg);
+			break;
+		}
+	}
+
+skip_reset:
+	sbi_hart_hang();
+}
+
+static struct sbi_system_reset_device da9063_reset_i2c = {
+	.name = "da9063-reset",
+	.system_reset_check = da9063_system_reset_check,
+	.system_reset = da9063_system_reset
+};
+
+static int da9063_reset_init(void *fdt, int nodeoff,
+			     const struct fdt_match *match)
+{
+	int rc, i2c_bus;
+	struct i2c_adapter *adapter;
+	uint64_t addr;
+
+	/* we are dlg,da9063 node */
+	rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &addr, NULL);
+	if (rc)
+		return rc;
+
+	da9063.reg = addr;
+
+	/* find i2c bus parent node */
+	i2c_bus = fdt_parent_offset(fdt, nodeoff);
+	if (i2c_bus < 0)
+		return i2c_bus;
+
+	/* i2c adapter get */
+	rc = fdt_i2c_adapter_get(fdt, i2c_bus, &adapter);
+	if (rc)
+		return rc;
+
+	da9063.adapter = adapter;
+
+	sbi_system_reset_add_device(&da9063_reset_i2c);
+
+	return 0;
+}
+
+static const struct fdt_match da9063_reset_match[] = {
+	{ .compatible = "dlg,da9063", .data = (void *)TRUE },
+	{ },
+};
+
+static struct fdt_reset fdt_reset_da9063 = {
+	.match_table = da9063_reset_match,
+	.init = da9063_reset_init,
+};
+
+int da9063_reset_bind(void *fdt)
+{
+	int rc;
+
+	rc = fdt_reset_driver_init(fdt, &fdt_reset_da9063);
+	if (rc)
+		sbi_printf("error: failed to find da9063 for reset\n");
+	return rc;
+}
+
diff --git a/platform/generic/include/da9063_reset.h b/platform/generic/include/da9063_reset.h
new file mode 100644
index 0000000..3075c25
--- /dev/null
+++ b/platform/generic/include/da9063_reset.h
@@ -0,0 +1,14 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021 SiFive
+ * Copyright (c) 2021 YADRO
+ *
+ * Split from sifive_fu740.c
+ *
+ * Authors:
+ *   David Abdurachmanov <david.abdurachmanov at sifive.com>
+ *   Nikita Shubin <n.shubin at yadro.com>
+ */
+
+extern int da9063_reset_bind(void *fdt);
diff --git a/platform/generic/objects.mk b/platform/generic/objects.mk
index 4907754..465a2f6 100644
--- a/platform/generic/objects.mk
+++ b/platform/generic/objects.mk
@@ -9,3 +9,6 @@
 
 platform-objs-y += platform.o
 platform-objs-y += platform_override_modules.o
+
+platform-objs-y += da9063_reset.o
+
diff --git a/platform/generic/sifive/fu740.c b/platform/generic/sifive/fu740.c
index f595c04..7ba4162 100644
--- a/platform/generic/sifive/fu740.c
+++ b/platform/generic/sifive/fu740.c
@@ -10,6 +10,7 @@
  */
 
 #include <platform_override.h>
+#include <da9063_reset.h>
 #include <libfdt.h>
 #include <sbi/sbi_error.h>
 #include <sbi/sbi_hart.h>
@@ -20,200 +21,6 @@
 #include <sbi_utils/reset/fdt_reset.h>
 #include <sbi_utils/i2c/fdt_i2c.h>
 
-#define DA9063_REG_PAGE_CON		0x00
-#define DA9063_REG_CONTROL_A		0x0e
-#define DA9063_REG_CONTROL_D		0x11
-#define DA9063_REG_CONTROL_F		0x13
-#define DA9063_REG_DEVICE_ID		0x81
-
-#define DA9063_CONTROL_A_M_POWER1_EN	(1 << 6)
-#define DA9063_CONTROL_A_M_POWER_EN	(1 << 5)
-#define DA9063_CONTROL_A_STANDBY	(1 << 3)
-
-#define DA9063_CONTROL_D_TWDSCALE_MASK	0x07
-
-#define DA9063_CONTROL_F_WAKEUP	(1 << 2)
-#define DA9063_CONTROL_F_SHUTDOWN	(1 << 1)
-
-#define PMIC_CHIP_ID_DA9063		0x61
-
-static struct {
-	struct i2c_adapter *adapter;
-	uint32_t reg;
-} da9063;
-
-static int da9063_system_reset_check(u32 type, u32 reason)
-{
-	switch (type) {
-	case SBI_SRST_RESET_TYPE_SHUTDOWN:
-		return 1;
-	case SBI_SRST_RESET_TYPE_COLD_REBOOT:
-	case SBI_SRST_RESET_TYPE_WARM_REBOOT:
-		return 255;
-	}
-
-	return 0;
-}
-
-static inline int da9063_sanity_check(struct i2c_adapter *adap, uint32_t reg)
-{
-	uint8_t val;
-	int rc = i2c_adapter_reg_write(adap, reg, DA9063_REG_PAGE_CON, 0x02);
-
-	if (rc)
-		return rc;
-
-	/* check set page*/
-	rc = i2c_adapter_reg_read(adap, reg, 0x0, &val);
-	if (rc)
-		return rc;
-
-	if (val != 0x02)
-		return SBI_ENODEV;
-
-	/* read and check device id */
-	rc = i2c_adapter_reg_read(adap, reg, DA9063_REG_DEVICE_ID, &val);
-	if (rc)
-		return rc;
-
-	if (val != PMIC_CHIP_ID_DA9063)
-		return SBI_ENODEV;
-
-	return 0;
-}
-
-static inline int da9063_stop_watchdog(struct i2c_adapter *adap, uint32_t reg)
-{
-	uint8_t val;
-	int rc = i2c_adapter_reg_write(adap, reg,
-					DA9063_REG_PAGE_CON, 0x00);
-
-	if (rc)
-		return rc;
-
-	rc = i2c_adapter_reg_read(adap, reg, DA9063_REG_CONTROL_D, &val);
-	if (rc)
-		return rc;
-
-	if ((val & DA9063_CONTROL_D_TWDSCALE_MASK) == 0)
-		return 0;
-
-	val &= ~DA9063_CONTROL_D_TWDSCALE_MASK;
-
-	return i2c_adapter_reg_write(adap, reg, DA9063_REG_CONTROL_D, val);
-}
-
-static inline int da9063_shutdown(struct i2c_adapter *adap, uint32_t reg)
-{
-	int rc = i2c_adapter_reg_write(adap, reg,
-					DA9063_REG_PAGE_CON, 0x00);
-
-	if (rc)
-		return rc;
-
-	return i2c_adapter_reg_write(adap, reg,
-				     DA9063_REG_CONTROL_F,
-				     DA9063_CONTROL_F_SHUTDOWN);
-}
-
-static inline int da9063_reset(struct i2c_adapter *adap, uint32_t reg)
-{
-	int rc = i2c_adapter_reg_write(adap, reg,
-					DA9063_REG_PAGE_CON, 0x00);
-
-	if (rc)
-		return rc;
-
-	rc = i2c_adapter_reg_write(adap, reg,
-				   DA9063_REG_CONTROL_F,
-				   DA9063_CONTROL_F_WAKEUP);
-	if (rc)
-		return rc;
-
-	return i2c_adapter_reg_write(adap, reg,
-				DA9063_REG_CONTROL_A,
-				DA9063_CONTROL_A_M_POWER1_EN |
-				DA9063_CONTROL_A_M_POWER_EN |
-				DA9063_CONTROL_A_STANDBY);
-}
-
-static void da9063_system_reset(u32 type, u32 reason)
-{
-	struct i2c_adapter *adap = da9063.adapter;
-	uint32_t reg = da9063.reg;
-	int rc;
-
-	if (adap) {
-		/* sanity check */
-		rc = da9063_sanity_check(adap, reg);
-		if (rc) {
-			sbi_printf("%s: chip is not da9063 PMIC\n", __func__);
-			goto skip_reset;
-		}
-
-		switch (type) {
-		case SBI_SRST_RESET_TYPE_SHUTDOWN:
-			da9063_shutdown(adap, reg);
-			break;
-		case SBI_SRST_RESET_TYPE_COLD_REBOOT:
-		case SBI_SRST_RESET_TYPE_WARM_REBOOT:
-			da9063_stop_watchdog(adap, reg);
-			da9063_reset(adap, reg);
-			break;
-		}
-	}
-
-skip_reset:
-	sbi_hart_hang();
-}
-
-static struct sbi_system_reset_device da9063_reset_i2c = {
-	.name = "da9063-reset",
-	.system_reset_check = da9063_system_reset_check,
-	.system_reset = da9063_system_reset
-};
-
-static int da9063_reset_init(void *fdt, int nodeoff,
-			     const struct fdt_match *match)
-{
-	int rc, i2c_bus;
-	struct i2c_adapter *adapter;
-	uint64_t addr;
-
-	/* we are dlg,da9063 node */
-	rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &addr, NULL);
-	if (rc)
-		return rc;
-
-	da9063.reg = addr;
-
-	/* find i2c bus parent node */
-	i2c_bus = fdt_parent_offset(fdt, nodeoff);
-	if (i2c_bus < 0)
-		return i2c_bus;
-
-	/* i2c adapter get */
-	rc = fdt_i2c_adapter_get(fdt, i2c_bus, &adapter);
-	if (rc)
-		return rc;
-
-	da9063.adapter = adapter;
-
-	sbi_system_reset_add_device(&da9063_reset_i2c);
-
-	return 0;
-}
-
-static const struct fdt_match da9063_reset_match[] = {
-	{ .compatible = "dlg,da9063", .data = (void *)TRUE },
-	{ },
-};
-
-struct fdt_reset fdt_reset_da9063 = {
-	.match_table = da9063_reset_match,
-	.init = da9063_reset_init,
-};
-
 static u64 sifive_fu740_tlbr_flush_limit(const struct fdt_match *match)
 {
 	/*
@@ -233,7 +40,7 @@ static int sifive_fu740_final_init(bool cold_boot,
 	void *fdt = fdt_get_address();
 
 	if (cold_boot) {
-		rc = fdt_reset_driver_init(fdt, &fdt_reset_da9063);
+		rc = da9063_reset_bind(fdt);
 		if (rc)
 			sbi_printf("%s: failed to find da9063 for reset\n",
 				   __func__);
-- 
2.35.1



From anup at brainfault.org  Tue Jul 12 03:18:15 2022
From: anup at brainfault.org (Anup Patel)
Date: Tue, 12 Jul 2022 15:48:15 +0530
Subject: [PATCH] platform: move da9063-reset driver to shareable code
In-Reply-To: <20220712090320.564729-1-ben.dooks@sifive.com>
References: <20220712090320.564729-1-ben.dooks@sifive.com>
Message-ID: <CAAhSdy0peWt-fbXGZzppDDo3VcSBZgya+=gPDhftS9v18F3BBg@mail.gmail.com>

On Tue, Jul 12, 2022 at 2:33 PM Ben Dooks <ben.dooks at sifive.com> wrote:
>
> The da9063 reset code may be useful for other users, so move this
> out to a new file from the current sifive_fu740.c user. This can
> then be called from the relevant machines.
>
> Signed-off-by: Ben Dooks <ben.dooks at sifive.com>

Better to have da9063 reset code under lib/utils/reset and
include/sbi_utils/reset

Regards,
Anup

> ---
>  platform/generic/da9063_reset.c         | 229 ++++++++++++++++++++++++
>  platform/generic/include/da9063_reset.h |  14 ++
>  platform/generic/objects.mk             |   3 +
>  platform/generic/sifive/fu740.c         | 197 +-------------------
>  4 files changed, 248 insertions(+), 195 deletions(-)
>  create mode 100644 platform/generic/da9063_reset.c
>  create mode 100644 platform/generic/include/da9063_reset.h
>
> diff --git a/platform/generic/da9063_reset.c b/platform/generic/da9063_reset.c
> new file mode 100644
> index 0000000..50c987d
> --- /dev/null
> +++ b/platform/generic/da9063_reset.c
> @@ -0,0 +1,229 @@
> +/*
> + * SPDX-License-Identifier: BSD-2-Clause
> + *
> + * Copyright (c) 2021 SiFive
> + * Copyright (c) 2021 YADRO
> + *
> + * Split from sifive_fu740.c
> + *
> + * Authors:
> + *   David Abdurachmanov <david.abdurachmanov at sifive.com>
> + *   Nikita Shubin <n.shubin at yadro.com>
> + */
> +
> +#include <platform_override.h>
> +#include <da9063_reset.h>
> +#include <libfdt.h>
> +#include <sbi/sbi_error.h>
> +#include <sbi/sbi_hart.h>
> +#include <sbi/sbi_system.h>
> +#include <sbi/sbi_console.h>
> +#include <sbi_utils/fdt/fdt_helper.h>
> +#include <sbi_utils/fdt/fdt_fixup.h>
> +#include <sbi_utils/reset/fdt_reset.h>
> +#include <sbi_utils/i2c/fdt_i2c.h>
> +
> +#define DA9063_REG_PAGE_CON            0x00
> +#define DA9063_REG_CONTROL_A           0x0e
> +#define DA9063_REG_CONTROL_D           0x11
> +#define DA9063_REG_CONTROL_F           0x13
> +#define DA9063_REG_DEVICE_ID           0x81
> +
> +#define DA9063_CONTROL_A_M_POWER1_EN   (1 << 6)
> +#define DA9063_CONTROL_A_M_POWER_EN    (1 << 5)
> +#define DA9063_CONTROL_A_STANDBY       (1 << 3)
> +
> +#define DA9063_CONTROL_D_TWDSCALE_MASK 0x07
> +
> +#define DA9063_CONTROL_F_WAKEUP        (1 << 2)
> +#define DA9063_CONTROL_F_SHUTDOWN      (1 << 1)
> +
> +#define PMIC_CHIP_ID_DA9063            0x61
> +
> +static struct {
> +       struct i2c_adapter *adapter;
> +       uint32_t reg;
> +} da9063;
> +
> +static int da9063_system_reset_check(u32 type, u32 reason)
> +{
> +       switch (type) {
> +       case SBI_SRST_RESET_TYPE_SHUTDOWN:
> +               return 1;
> +       case SBI_SRST_RESET_TYPE_COLD_REBOOT:
> +       case SBI_SRST_RESET_TYPE_WARM_REBOOT:
> +               return 255;
> +       }
> +
> +       return 0;
> +}
> +
> +static inline int da9063_sanity_check(struct i2c_adapter *adap, uint32_t reg)
> +{
> +       uint8_t val;
> +       int rc = i2c_adapter_reg_write(adap, reg, DA9063_REG_PAGE_CON, 0x02);
> +
> +       if (rc)
> +               return rc;
> +
> +       /* check set page*/
> +       rc = i2c_adapter_reg_read(adap, reg, 0x0, &val);
> +       if (rc)
> +               return rc;
> +
> +       if (val != 0x02)
> +               return SBI_ENODEV;
> +
> +       /* read and check device id */
> +       rc = i2c_adapter_reg_read(adap, reg, DA9063_REG_DEVICE_ID, &val);
> +       if (rc)
> +               return rc;
> +
> +       if (val != PMIC_CHIP_ID_DA9063)
> +               return SBI_ENODEV;
> +
> +       return 0;
> +}
> +
> +static inline int da9063_stop_watchdog(struct i2c_adapter *adap, uint32_t reg)
> +{
> +       uint8_t val;
> +       int rc = i2c_adapter_reg_write(adap, reg,
> +                                       DA9063_REG_PAGE_CON, 0x00);
> +
> +       if (rc)
> +               return rc;
> +
> +       rc = i2c_adapter_reg_read(adap, reg, DA9063_REG_CONTROL_D, &val);
> +       if (rc)
> +               return rc;
> +
> +       if ((val & DA9063_CONTROL_D_TWDSCALE_MASK) == 0)
> +               return 0;
> +
> +       val &= ~DA9063_CONTROL_D_TWDSCALE_MASK;
> +
> +       return i2c_adapter_reg_write(adap, reg, DA9063_REG_CONTROL_D, val);
> +}
> +
> +static inline int da9063_shutdown(struct i2c_adapter *adap, uint32_t reg)
> +{
> +       int rc = i2c_adapter_reg_write(adap, reg,
> +                                       DA9063_REG_PAGE_CON, 0x00);
> +
> +       if (rc)
> +               return rc;
> +
> +       return i2c_adapter_reg_write(adap, reg,
> +                                    DA9063_REG_CONTROL_F,
> +                                    DA9063_CONTROL_F_SHUTDOWN);
> +}
> +
> +static inline int da9063_reset(struct i2c_adapter *adap, uint32_t reg)
> +{
> +       int rc = i2c_adapter_reg_write(adap, reg,
> +                                       DA9063_REG_PAGE_CON, 0x00);
> +
> +       if (rc)
> +               return rc;
> +
> +       rc = i2c_adapter_reg_write(adap, reg,
> +                                  DA9063_REG_CONTROL_F,
> +                                  DA9063_CONTROL_F_WAKEUP);
> +       if (rc)
> +               return rc;
> +
> +       return i2c_adapter_reg_write(adap, reg,
> +                               DA9063_REG_CONTROL_A,
> +                               DA9063_CONTROL_A_M_POWER1_EN |
> +                               DA9063_CONTROL_A_M_POWER_EN |
> +                               DA9063_CONTROL_A_STANDBY);
> +}
> +
> +static void da9063_system_reset(u32 type, u32 reason)
> +{
> +       struct i2c_adapter *adap = da9063.adapter;
> +       uint32_t reg = da9063.reg;
> +       int rc;
> +
> +       if (adap) {
> +               /* sanity check */
> +               rc = da9063_sanity_check(adap, reg);
> +               if (rc) {
> +                       sbi_printf("%s: chip is not da9063 PMIC\n", __func__);
> +                       goto skip_reset;
> +               }
> +
> +               switch (type) {
> +               case SBI_SRST_RESET_TYPE_SHUTDOWN:
> +                       da9063_shutdown(adap, reg);
> +                       break;
> +               case SBI_SRST_RESET_TYPE_COLD_REBOOT:
> +               case SBI_SRST_RESET_TYPE_WARM_REBOOT:
> +                       da9063_stop_watchdog(adap, reg);
> +                       da9063_reset(adap, reg);
> +                       break;
> +               }
> +       }
> +
> +skip_reset:
> +       sbi_hart_hang();
> +}
> +
> +static struct sbi_system_reset_device da9063_reset_i2c = {
> +       .name = "da9063-reset",
> +       .system_reset_check = da9063_system_reset_check,
> +       .system_reset = da9063_system_reset
> +};
> +
> +static int da9063_reset_init(void *fdt, int nodeoff,
> +                            const struct fdt_match *match)
> +{
> +       int rc, i2c_bus;
> +       struct i2c_adapter *adapter;
> +       uint64_t addr;
> +
> +       /* we are dlg,da9063 node */
> +       rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &addr, NULL);
> +       if (rc)
> +               return rc;
> +
> +       da9063.reg = addr;
> +
> +       /* find i2c bus parent node */
> +       i2c_bus = fdt_parent_offset(fdt, nodeoff);
> +       if (i2c_bus < 0)
> +               return i2c_bus;
> +
> +       /* i2c adapter get */
> +       rc = fdt_i2c_adapter_get(fdt, i2c_bus, &adapter);
> +       if (rc)
> +               return rc;
> +
> +       da9063.adapter = adapter;
> +
> +       sbi_system_reset_add_device(&da9063_reset_i2c);
> +
> +       return 0;
> +}
> +
> +static const struct fdt_match da9063_reset_match[] = {
> +       { .compatible = "dlg,da9063", .data = (void *)TRUE },
> +       { },
> +};
> +
> +static struct fdt_reset fdt_reset_da9063 = {
> +       .match_table = da9063_reset_match,
> +       .init = da9063_reset_init,
> +};
> +
> +int da9063_reset_bind(void *fdt)
> +{
> +       int rc;
> +
> +       rc = fdt_reset_driver_init(fdt, &fdt_reset_da9063);
> +       if (rc)
> +               sbi_printf("error: failed to find da9063 for reset\n");
> +       return rc;
> +}
> +
> diff --git a/platform/generic/include/da9063_reset.h b/platform/generic/include/da9063_reset.h
> new file mode 100644
> index 0000000..3075c25
> --- /dev/null
> +++ b/platform/generic/include/da9063_reset.h
> @@ -0,0 +1,14 @@
> +/*
> + * SPDX-License-Identifier: BSD-2-Clause
> + *
> + * Copyright (c) 2021 SiFive
> + * Copyright (c) 2021 YADRO
> + *
> + * Split from sifive_fu740.c
> + *
> + * Authors:
> + *   David Abdurachmanov <david.abdurachmanov at sifive.com>
> + *   Nikita Shubin <n.shubin at yadro.com>
> + */
> +

Use "#ifndef" protection here.

Something like this:

#ifndef __DA9063_RESET__
#define __DA9063_RESET__

<snip>

#endif

> +extern int da9063_reset_bind(void *fdt);
> diff --git a/platform/generic/objects.mk b/platform/generic/objects.mk
> index 4907754..465a2f6 100644
> --- a/platform/generic/objects.mk
> +++ b/platform/generic/objects.mk
> @@ -9,3 +9,6 @@
>
>  platform-objs-y += platform.o
>  platform-objs-y += platform_override_modules.o
> +
> +platform-objs-y += da9063_reset.o
> +
> diff --git a/platform/generic/sifive/fu740.c b/platform/generic/sifive/fu740.c
> index f595c04..7ba4162 100644
> --- a/platform/generic/sifive/fu740.c
> +++ b/platform/generic/sifive/fu740.c
> @@ -10,6 +10,7 @@
>   */
>
>  #include <platform_override.h>
> +#include <da9063_reset.h>
>  #include <libfdt.h>
>  #include <sbi/sbi_error.h>
>  #include <sbi/sbi_hart.h>
> @@ -20,200 +21,6 @@
>  #include <sbi_utils/reset/fdt_reset.h>
>  #include <sbi_utils/i2c/fdt_i2c.h>
>
> -#define DA9063_REG_PAGE_CON            0x00
> -#define DA9063_REG_CONTROL_A           0x0e
> -#define DA9063_REG_CONTROL_D           0x11
> -#define DA9063_REG_CONTROL_F           0x13
> -#define DA9063_REG_DEVICE_ID           0x81
> -
> -#define DA9063_CONTROL_A_M_POWER1_EN   (1 << 6)
> -#define DA9063_CONTROL_A_M_POWER_EN    (1 << 5)
> -#define DA9063_CONTROL_A_STANDBY       (1 << 3)
> -
> -#define DA9063_CONTROL_D_TWDSCALE_MASK 0x07
> -
> -#define DA9063_CONTROL_F_WAKEUP        (1 << 2)
> -#define DA9063_CONTROL_F_SHUTDOWN      (1 << 1)
> -
> -#define PMIC_CHIP_ID_DA9063            0x61
> -
> -static struct {
> -       struct i2c_adapter *adapter;
> -       uint32_t reg;
> -} da9063;
> -
> -static int da9063_system_reset_check(u32 type, u32 reason)
> -{
> -       switch (type) {
> -       case SBI_SRST_RESET_TYPE_SHUTDOWN:
> -               return 1;
> -       case SBI_SRST_RESET_TYPE_COLD_REBOOT:
> -       case SBI_SRST_RESET_TYPE_WARM_REBOOT:
> -               return 255;
> -       }
> -
> -       return 0;
> -}
> -
> -static inline int da9063_sanity_check(struct i2c_adapter *adap, uint32_t reg)
> -{
> -       uint8_t val;
> -       int rc = i2c_adapter_reg_write(adap, reg, DA9063_REG_PAGE_CON, 0x02);
> -
> -       if (rc)
> -               return rc;
> -
> -       /* check set page*/
> -       rc = i2c_adapter_reg_read(adap, reg, 0x0, &val);
> -       if (rc)
> -               return rc;
> -
> -       if (val != 0x02)
> -               return SBI_ENODEV;
> -
> -       /* read and check device id */
> -       rc = i2c_adapter_reg_read(adap, reg, DA9063_REG_DEVICE_ID, &val);
> -       if (rc)
> -               return rc;
> -
> -       if (val != PMIC_CHIP_ID_DA9063)
> -               return SBI_ENODEV;
> -
> -       return 0;
> -}
> -
> -static inline int da9063_stop_watchdog(struct i2c_adapter *adap, uint32_t reg)
> -{
> -       uint8_t val;
> -       int rc = i2c_adapter_reg_write(adap, reg,
> -                                       DA9063_REG_PAGE_CON, 0x00);
> -
> -       if (rc)
> -               return rc;
> -
> -       rc = i2c_adapter_reg_read(adap, reg, DA9063_REG_CONTROL_D, &val);
> -       if (rc)
> -               return rc;
> -
> -       if ((val & DA9063_CONTROL_D_TWDSCALE_MASK) == 0)
> -               return 0;
> -
> -       val &= ~DA9063_CONTROL_D_TWDSCALE_MASK;
> -
> -       return i2c_adapter_reg_write(adap, reg, DA9063_REG_CONTROL_D, val);
> -}
> -
> -static inline int da9063_shutdown(struct i2c_adapter *adap, uint32_t reg)
> -{
> -       int rc = i2c_adapter_reg_write(adap, reg,
> -                                       DA9063_REG_PAGE_CON, 0x00);
> -
> -       if (rc)
> -               return rc;
> -
> -       return i2c_adapter_reg_write(adap, reg,
> -                                    DA9063_REG_CONTROL_F,
> -                                    DA9063_CONTROL_F_SHUTDOWN);
> -}
> -
> -static inline int da9063_reset(struct i2c_adapter *adap, uint32_t reg)
> -{
> -       int rc = i2c_adapter_reg_write(adap, reg,
> -                                       DA9063_REG_PAGE_CON, 0x00);
> -
> -       if (rc)
> -               return rc;
> -
> -       rc = i2c_adapter_reg_write(adap, reg,
> -                                  DA9063_REG_CONTROL_F,
> -                                  DA9063_CONTROL_F_WAKEUP);
> -       if (rc)
> -               return rc;
> -
> -       return i2c_adapter_reg_write(adap, reg,
> -                               DA9063_REG_CONTROL_A,
> -                               DA9063_CONTROL_A_M_POWER1_EN |
> -                               DA9063_CONTROL_A_M_POWER_EN |
> -                               DA9063_CONTROL_A_STANDBY);
> -}
> -
> -static void da9063_system_reset(u32 type, u32 reason)
> -{
> -       struct i2c_adapter *adap = da9063.adapter;
> -       uint32_t reg = da9063.reg;
> -       int rc;
> -
> -       if (adap) {
> -               /* sanity check */
> -               rc = da9063_sanity_check(adap, reg);
> -               if (rc) {
> -                       sbi_printf("%s: chip is not da9063 PMIC\n", __func__);
> -                       goto skip_reset;
> -               }
> -
> -               switch (type) {
> -               case SBI_SRST_RESET_TYPE_SHUTDOWN:
> -                       da9063_shutdown(adap, reg);
> -                       break;
> -               case SBI_SRST_RESET_TYPE_COLD_REBOOT:
> -               case SBI_SRST_RESET_TYPE_WARM_REBOOT:
> -                       da9063_stop_watchdog(adap, reg);
> -                       da9063_reset(adap, reg);
> -                       break;
> -               }
> -       }
> -
> -skip_reset:
> -       sbi_hart_hang();
> -}
> -
> -static struct sbi_system_reset_device da9063_reset_i2c = {
> -       .name = "da9063-reset",
> -       .system_reset_check = da9063_system_reset_check,
> -       .system_reset = da9063_system_reset
> -};
> -
> -static int da9063_reset_init(void *fdt, int nodeoff,
> -                            const struct fdt_match *match)
> -{
> -       int rc, i2c_bus;
> -       struct i2c_adapter *adapter;
> -       uint64_t addr;
> -
> -       /* we are dlg,da9063 node */
> -       rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &addr, NULL);
> -       if (rc)
> -               return rc;
> -
> -       da9063.reg = addr;
> -
> -       /* find i2c bus parent node */
> -       i2c_bus = fdt_parent_offset(fdt, nodeoff);
> -       if (i2c_bus < 0)
> -               return i2c_bus;
> -
> -       /* i2c adapter get */
> -       rc = fdt_i2c_adapter_get(fdt, i2c_bus, &adapter);
> -       if (rc)
> -               return rc;
> -
> -       da9063.adapter = adapter;
> -
> -       sbi_system_reset_add_device(&da9063_reset_i2c);
> -
> -       return 0;
> -}
> -
> -static const struct fdt_match da9063_reset_match[] = {
> -       { .compatible = "dlg,da9063", .data = (void *)TRUE },
> -       { },
> -};
> -
> -struct fdt_reset fdt_reset_da9063 = {
> -       .match_table = da9063_reset_match,
> -       .init = da9063_reset_init,
> -};
> -
>  static u64 sifive_fu740_tlbr_flush_limit(const struct fdt_match *match)
>  {
>         /*
> @@ -233,7 +40,7 @@ static int sifive_fu740_final_init(bool cold_boot,
>         void *fdt = fdt_get_address();
>
>         if (cold_boot) {
> -               rc = fdt_reset_driver_init(fdt, &fdt_reset_da9063);
> +               rc = da9063_reset_bind(fdt);
>                 if (rc)
>                         sbi_printf("%s: failed to find da9063 for reset\n",
>                                    __func__);
> --
> 2.35.1
>
>
> --
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi

Regards,
Anup


From jrtc27 at jrtc27.com  Tue Jul 12 06:07:46 2022
From: jrtc27 at jrtc27.com (Jessica Clarke)
Date: Tue, 12 Jul 2022 14:07:46 +0100
Subject: [PATCH] platform: move da9063-reset driver to shareable code
In-Reply-To: <20220712090320.564729-1-ben.dooks@sifive.com>
References: <20220712090320.564729-1-ben.dooks@sifive.com>
Message-ID: <70C6A544-CA69-44C4-837A-20450FF9DA86@jrtc27.com>

On 12 Jul 2022, at 10:03, Ben Dooks <ben.dooks at sifive.com> wrote:
> 
> The da9063 reset code may be useful for other users, so move this
> out to a new file from the current sifive_fu740.c user. This can
> then be called from the relevant machines.

I sincerely hope not, the da9063 code is a massive hack to work around
an extremely deficient design, one that removes a feature the previous
generation had. Nobody should be looking at it as a feature they can
take advantage of, they should have some form of dedicated reset
hardware in the SoC itself.

Jess



From palmer at dabbelt.com  Tue Jul 12 19:28:03 2022
From: palmer at dabbelt.com (Palmer Dabbelt)
Date: Tue, 12 Jul 2022 19:28:03 -0700 (PDT)
Subject: Is anyone seeing OpenSBI v1.0 flakiness?
Message-ID: <mhng-3a4b2b8b-da0b-4e32-875c-5f52dcc53f20@palmer-mbp2014>

I recently bumped QEMU to v7.0.0 in my main test repos, which is causing 
some flakiness: around 1% of kernels are failing to boot, it seems to 
not really depend on the Kconfig or QEMU config (though I'm always 
running virt tests).  It's just an early boot hang, I haven't poked 
around much deeper as I've just been trying to bisect the problem.  I 
think I've tracked this down to the OpenSBI update (from 0.9 to 1.0), 
but it seems like the boot failures come and go so I'm not 100% sure on 
that -- at least the commit before the OpenSBI update seems safe, I ran 
about ten thousand boots without any issues.  That's about as far as 
I've getten bisecting, the low failure rate is causing headaches.

I'm back on v6.1.0 so I can try and get some Linux code merged this 
week, I'm way behind so I probably won't be able to look all that much.  
I'm going to try and get OpenSBI master into my testing flow more 
cleanly, but I figured I'd start a thread in case anyone else is seeing 
issues.


From anup at brainfault.org  Tue Jul 12 22:13:52 2022
From: anup at brainfault.org (Anup Patel)
Date: Wed, 13 Jul 2022 10:43:52 +0530
Subject: Is anyone seeing OpenSBI v1.0 flakiness?
In-Reply-To: <mhng-3a4b2b8b-da0b-4e32-875c-5f52dcc53f20@palmer-mbp2014>
References: <mhng-3a4b2b8b-da0b-4e32-875c-5f52dcc53f20@palmer-mbp2014>
Message-ID: <CAAhSdy1u0Pp+xY=DSoiMXM-T0cwhpcYcHQYkoE7hxxkqBUmXNA@mail.gmail.com>

On Wed, Jul 13, 2022 at 7:58 AM Palmer Dabbelt <palmer at dabbelt.com> wrote:
>
> I recently bumped QEMU to v7.0.0 in my main test repos, which is causing
> some flakiness: around 1% of kernels are failing to boot, it seems to
> not really depend on the Kconfig or QEMU config (though I'm always
> running virt tests).  It's just an early boot hang, I haven't poked
> around much deeper as I've just been trying to bisect the problem.  I
> think I've tracked this down to the OpenSBI update (from 0.9 to 1.0),
> but it seems like the boot failures come and go so I'm not 100% sure on
> that -- at least the commit before the OpenSBI update seems safe, I ran
> about ten thousand boots without any issues.  That's about as far as
> I've getten bisecting, the low failure rate is causing headaches.

When it fails to boot, is it only the kernel or opensbi as well ?
If it is just the kernel failing to boot then the issue could be with
kernel as well.

In any case, I suggest you take a register dump from the QEMU
monitor using "info registers -a" whenever you see any early boot
failure. At least, this will tell us what the CPUs were doing when
the failure happened.

>
> I'm back on v6.1.0 so I can try and get some Linux code merged this
> week, I'm way behind so I probably won't be able to look all that much.
> I'm going to try and get OpenSBI master into my testing flow more
> cleanly, but I figured I'd start a thread in case anyone else is seeing
> issues.

I would also recommend trying the latest OpenSBI v1.1 to check
whether similar failures are seen with the latest OpenSBI or not ?

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

Regards,
Anup


From dramforever at live.com  Wed Jul 13 00:51:53 2022
From: dramforever at live.com (dramforever)
Date: Wed, 13 Jul 2022 15:51:53 +0800
Subject: Is anyone seeing OpenSBI v1.0 flakiness?
In-Reply-To: <mhng-3a4b2b8b-da0b-4e32-875c-5f52dcc53f20@palmer-mbp2014>
References: <mhng-3a4b2b8b-da0b-4e32-875c-5f52dcc53f20@palmer-mbp2014>
Message-ID: <TYYP286MB1439E4718D5B2F685BFE2022C6899@TYYP286MB1439.JPNP286.PROD.OUTLOOK.COM>

On 7/13/22 10:28, Palmer Dabbelt wrote:
> I recently bumped QEMU to v7.0.0 in my main test repos, which is
> causing some flakiness: around 1% of kernels are failing to boot, it
> seems to not really depend on the Kconfig or QEMU config (though I'm
> always running virt tests).? It's just an early boot hang, I haven't
> poked around much deeper as I've just been trying to bisect the
> problem.? I think I've tracked this down to the OpenSBI update (from
> 0.9 to 1.0), but it seems like the boot failures come and go so I'm
> not 100% sure on that -- at least the commit before the OpenSBI update
> seems safe, I ran about ten thousand boots without any issues.? That's
> about as far as I've getten bisecting, the low failure rate is causing
> headaches.
>
> I'm back on v6.1.0 so I can try and get some Linux code merged this
> week, I'm way behind so I probably won't be able to look all that
> much.? I'm going to try and get OpenSBI master into my testing flow
> more cleanly, but I figured I'd start a thread in case anyone else is
> seeing issues.
>
This is just a guess, but QEMU 7.0.0 has a bug in setting mtval/stval
for illegal instruction handling [1]. The behavior is dependent on the
history of illegal instructions executed, and OpenSBI does install an
illegal instruction handler, so there might be some interaction with
whatever Linux is doing, like emulating the wrong instruction.

I have a workaround patch in my own thing [2] but since it basically
negates the benefit of mtvec/stvec on illegal instruction it's not
upstreamed.

Regards,
dram

[1]: https://gitlab.com/qemu-project/qemu/-/issues/1060
[2]: https://github.com/dramforever/opensbi-h/commit/e580cb14139b4cf5792e3d9adf2d9356670a3039



From vivahavey at gmail.com  Thu Jul 14 09:20:59 2022
From: vivahavey at gmail.com (gagachang)
Date: Fri, 15 Jul 2022 00:20:59 +0800
Subject: [PATCH] lib: sbi_hart: Shorten the code to set MPV bit
Message-ID: <20220714162059.3294-1-vivahavey@gmail.com>

MPV bit is set when the value of next_virt boolean variable equals
true. Since the value of next_virt is either 0 or 1, we can set
MPV bit without if-else logic.

Signed-off-by: Che-Chia Chang <alvinga at andestech.com>
---
 lib/sbi/sbi_hart.c | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c
index de86fee..23ad341 100644
--- a/lib/sbi/sbi_hart.c
+++ b/lib/sbi/sbi_hart.c
@@ -769,18 +769,12 @@ sbi_hart_switch_mode(unsigned long arg0, unsigned long arg1,
 #if __riscv_xlen == 32
 	if (misa_extension('H')) {
 		valH = csr_read(CSR_MSTATUSH);
-		if (next_virt)
-			valH = INSERT_FIELD(valH, MSTATUSH_MPV, 1);
-		else
-			valH = INSERT_FIELD(valH, MSTATUSH_MPV, 0);
+		valH = INSERT_FIELD(valH, MSTATUSH_MPV, next_virt);
 		csr_write(CSR_MSTATUSH, valH);
 	}
 #else
 	if (misa_extension('H')) {
-		if (next_virt)
-			val = INSERT_FIELD(val, MSTATUS_MPV, 1);
-		else
-			val = INSERT_FIELD(val, MSTATUS_MPV, 0);
+		val = INSERT_FIELD(val, MSTATUS_MPV, next_virt);
 	}
 #endif
 	csr_write(CSR_MSTATUS, val);
-- 
2.25.1



From ajones at ventanamicro.com  Thu Jul 14 09:37:11 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Thu, 14 Jul 2022 18:37:11 +0200
Subject: [PATCH] lib: sbi_hart: Shorten the code to set MPV bit
In-Reply-To: <20220714162059.3294-1-vivahavey@gmail.com>
References: <20220714162059.3294-1-vivahavey@gmail.com>
Message-ID: <20220714163711.2d5s22ewh7gdjf5z@kamzik>

On Fri, Jul 15, 2022 at 12:20:59AM +0800, gagachang wrote:
> MPV bit is set when the value of next_virt boolean variable equals
> true. Since the value of next_virt is either 0 or 1, we can set
> MPV bit without if-else logic.
> 
> Signed-off-by: Che-Chia Chang <alvinga at andestech.com>
> ---
>  lib/sbi/sbi_hart.c | 10 ++--------
>  1 file changed, 2 insertions(+), 8 deletions(-)
> 
> diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c
> index de86fee..23ad341 100644
> --- a/lib/sbi/sbi_hart.c
> +++ b/lib/sbi/sbi_hart.c
> @@ -769,18 +769,12 @@ sbi_hart_switch_mode(unsigned long arg0, unsigned long arg1,
>  #if __riscv_xlen == 32
>  	if (misa_extension('H')) {
>  		valH = csr_read(CSR_MSTATUSH);
> -		if (next_virt)
> -			valH = INSERT_FIELD(valH, MSTATUSH_MPV, 1);
> -		else
> -			valH = INSERT_FIELD(valH, MSTATUSH_MPV, 0);
> +		valH = INSERT_FIELD(valH, MSTATUSH_MPV, next_virt);
>  		csr_write(CSR_MSTATUSH, valH);
>  	}
>  #else
>  	if (misa_extension('H')) {
> -		if (next_virt)
> -			val = INSERT_FIELD(val, MSTATUS_MPV, 1);
> -		else
> -			val = INSERT_FIELD(val, MSTATUS_MPV, 0);
> +		val = INSERT_FIELD(val, MSTATUS_MPV, next_virt);
>  	}

We can drop the {} here now too.

>  #endif
>  	csr_write(CSR_MSTATUS, val);
> -- 
> 2.25.1
>

Otherwise

Reviewed-by: Andrew Jones <ajones at ventanamicro.com>

Thanks,
drew


From vivahavey at gmail.com  Thu Jul 14 10:29:41 2022
From: vivahavey at gmail.com (gagachang)
Date: Fri, 15 Jul 2022 01:29:41 +0800
Subject: [PATCH v2] lib: sbi_hart: Shorten the code to set MPV bit
Message-ID: <20220714172941.2967-1-vivahavey@gmail.com>

MPV bit is set when the value of next_virt boolean variable equals
true. Since the value of next_virt is either 0 or 1, we can set
MPV bit without if-else logic.

Signed-off-by: Che-Chia Chang <alvinga at andestech.com>
Reviewed-by: Andrew Jones <ajones at ventanamicro.com>

--
Change since v1:
- Drop the unnecessary brackets
---
 lib/sbi/sbi_hart.c | 13 +++----------
 1 file changed, 3 insertions(+), 10 deletions(-)

diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c
index de86fee..67b685a 100644
--- a/lib/sbi/sbi_hart.c
+++ b/lib/sbi/sbi_hart.c
@@ -769,19 +769,12 @@ sbi_hart_switch_mode(unsigned long arg0, unsigned long arg1,
 #if __riscv_xlen == 32
 	if (misa_extension('H')) {
 		valH = csr_read(CSR_MSTATUSH);
-		if (next_virt)
-			valH = INSERT_FIELD(valH, MSTATUSH_MPV, 1);
-		else
-			valH = INSERT_FIELD(valH, MSTATUSH_MPV, 0);
+		valH = INSERT_FIELD(valH, MSTATUSH_MPV, next_virt);
 		csr_write(CSR_MSTATUSH, valH);
 	}
 #else
-	if (misa_extension('H')) {
-		if (next_virt)
-			val = INSERT_FIELD(val, MSTATUS_MPV, 1);
-		else
-			val = INSERT_FIELD(val, MSTATUS_MPV, 0);
-	}
+	if (misa_extension('H'))
+		val = INSERT_FIELD(val, MSTATUS_MPV, next_virt);
 #endif
 	csr_write(CSR_MSTATUS, val);
 	csr_write(CSR_MEPC, next_addr);
-- 
2.25.1



From junliang.tan at linux.starfivetech.com  Thu Jul 14 19:40:54 2022
From: junliang.tan at linux.starfivetech.com (Jun Liang Tan)
Date: Fri, 15 Jul 2022 10:40:54 +0800
Subject: [PATCH v2] lib: utils: serial: Add Cadence UART driver
Message-ID: <20220715024054.7326-1-junliang.tan@linux.starfivetech.com>

Add Cadence UART driver

Signed-off-by: Jun Liang Tan <junliang.tan at linux.starfivetech.com>
Signed-off-by: Wei Liang Lim <weiliang.lim at linux.starfivetech.com>
---
 include/sbi_utils/fdt/fdt_helper.h      |   4 +-
 include/sbi_utils/serial/cadence-uart.h |  16 +++
 lib/utils/fdt/fdt_helper.c              |   6 +-
 lib/utils/serial/cadence-uart.c         | 125 ++++++++++++++++++++++++
 lib/utils/serial/fdt_serial_cadence.c   |  35 +++++++
 lib/utils/serial/fdt_serial_uart8250.c  |   2 +-
 lib/utils/serial/objects.mk             |   4 +
 7 files changed, 186 insertions(+), 6 deletions(-)
 create mode 100644 include/sbi_utils/serial/cadence-uart.h
 create mode 100644 lib/utils/serial/cadence-uart.c
 create mode 100644 lib/utils/serial/fdt_serial_cadence.c

diff --git a/include/sbi_utils/fdt/fdt_helper.h b/include/sbi_utils/fdt/fdt_helper.h
index c60af35..bcd4996 100644
--- a/include/sbi_utils/fdt/fdt_helper.h
+++ b/include/sbi_utils/fdt/fdt_helper.h
@@ -65,8 +65,8 @@ int fdt_parse_shakti_uart_node(void *fdt, int nodeoffset,
 int fdt_parse_sifive_uart_node(void *fdt, int nodeoffset,
 			       struct platform_uart_data *uart);
 
-int fdt_parse_uart8250_node(void *fdt, int nodeoffset,
-			    struct platform_uart_data *uart);
+int fdt_parse_uart_node(void *fdt, int nodeoffset,
+			struct platform_uart_data *uart);
 
 int fdt_parse_uart8250(void *fdt, struct platform_uart_data *uart,
 		       const char *compatible);
diff --git a/include/sbi_utils/serial/cadence-uart.h b/include/sbi_utils/serial/cadence-uart.h
new file mode 100644
index 0000000..e75fb95
--- /dev/null
+++ b/include/sbi_utils/serial/cadence-uart.h
@@ -0,0 +1,16 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2022 StarFive Technology Co., Ltd.
+ *
+ * Author: Jun Liang Tan <junliang.tan at linux.starfivetech.com>
+ */
+
+#ifndef __SERIAL_CADENCE_UART_H__
+#define __SERIAL_CADENCE_UART_H__
+
+#include <sbi/sbi_types.h>
+
+int cadence_uart_init(unsigned long base, u32 in_freq, u32 baudrate);
+
+#endif
diff --git a/lib/utils/fdt/fdt_helper.c b/lib/utils/fdt/fdt_helper.c
index 2bdb1ef..1e136fe 100644
--- a/lib/utils/fdt/fdt_helper.c
+++ b/lib/utils/fdt/fdt_helper.c
@@ -427,8 +427,8 @@ int fdt_parse_sifive_uart_node(void *fdt, int nodeoffset,
 	return 0;
 }
 
-int fdt_parse_uart8250_node(void *fdt, int nodeoffset,
-			    struct platform_uart_data *uart)
+int fdt_parse_uart_node(void *fdt, int nodeoffset,
+			struct platform_uart_data *uart)
 {
 	int len, rc;
 	const fdt32_t *val;
@@ -492,7 +492,7 @@ int fdt_parse_uart8250(void *fdt, struct platform_uart_data *uart,
 	if (nodeoffset < 0)
 		return nodeoffset;
 
-	return fdt_parse_uart8250_node(fdt, nodeoffset, uart);
+	return fdt_parse_uart_node(fdt, nodeoffset, uart);
 }
 
 int fdt_parse_xlnx_uartlite_node(void *fdt, int nodeoffset,
diff --git a/lib/utils/serial/cadence-uart.c b/lib/utils/serial/cadence-uart.c
new file mode 100644
index 0000000..ffa79f8
--- /dev/null
+++ b/lib/utils/serial/cadence-uart.c
@@ -0,0 +1,125 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2022 StarFive Technology Co., Ltd.
+ *
+ * Author: Jun Liang Tan <junliang.tan at linux.starfivetech.com>
+ */
+
+#include <sbi/riscv_io.h>
+#include <sbi/sbi_console.h>
+#include <sbi_utils/serial/cadence-uart.h>
+
+/* clang-format off */
+
+#define UART_REG_OFFSET_CTRL		0x00	/* Control register */
+#define UART_REG_OFFSET_MODE		0x04	/* Mode register */
+#define UART_REG_OFFSET_CH_STS		0x2C	/* Channel status register */
+#define UART_REG_OFFSET_RX_TX_FIFO	0x30	/* RX and TX FIFO register */
+#define UART_REG_OFFSET_INT_DIS		0x0C	/* Interrupt disable register */
+#define UART_REG_OFFSET_BRG	0x18	/* Baud rate generator register */
+#define UART_REG_OFFSET_BRD	0x34	/* Baud rate divider register */
+
+/* Control register, 0x00 */
+#define UART_CR_TX_DIS	0x00000020	/* TX disabled, bit 5 */
+#define UART_CR_TX_EN	0x00000010	/* TX enabled, bit 4 */
+#define UART_CR_RX_DIS	0x00000008	/* RX disabled, bit 3 */
+#define UART_CR_RX_EN	0x00000004	/* RX enabled, bit 2*/
+#define UART_CR_TXRST	0x00000002	/* TX logic reset, bit 1 */
+#define UART_CR_RXRST	0x00000001	/* RX logic reset, bit 0 */
+
+/* Channel status register, 0x2C */
+#define	UART_CHSR_TXFULL	0x00000010	/* TX FIFO full, bit 4 */
+#define	UART_CHSR_RXEMPTY	0x00000002	/* RX FIFO empty, bit 1 */
+
+/* Mode register, 0x04 */
+#define UART_MR_PARITY_NONE	0x00000020	/* No parity set */
+
+/* Baud rate generator register, 0x18 */
+#define UART_BRG_CLK_DIVISOR	0x00000001	/* baud_sample = sel_clk */
+
+/* clang-format on */
+
+static volatile void *uart_base;
+static u32 uart_in_freq;
+static u32 uart_baudrate;
+
+/**
+ * Find minimum divisor divides in_freq to max_target_hz;
+ * Based on SiFive UART driver (sifive-uart.c)
+ */
+static inline unsigned int uart_min_clk_divisor(uint64_t in_freq,
+						uint64_t max_target_hz)
+{
+	uint64_t quotient = (in_freq + max_target_hz - 1) / (max_target_hz);
+	/* Avoid underflow */
+	if (quotient == 0)
+		return 0;
+	else
+		return quotient - 1;
+}
+
+static u32 get_reg(u32 offset)
+{
+	return readl(uart_base + offset);
+}
+
+static void set_reg(u32 offset, u32 val)
+{
+	writel(val, uart_base + offset);
+}
+
+static void cadence_uart_putc(char ch)
+{
+	while (get_reg(UART_REG_OFFSET_CH_STS) & UART_CHSR_TXFULL)
+		;
+
+	set_reg(UART_REG_OFFSET_RX_TX_FIFO, ch);
+}
+
+static int cadence_uart_getc(void)
+{
+	u32 ret = get_reg(UART_REG_OFFSET_CH_STS);
+
+	if (!(ret & UART_CHSR_RXEMPTY))
+		return get_reg(UART_REG_OFFSET_RX_TX_FIFO);
+	return -1;
+}
+
+static struct sbi_console_device cadence_console = {
+	.name = "cadence_uart",
+	.console_putc = cadence_uart_putc,
+	.console_getc = cadence_uart_getc
+};
+
+int cadence_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
+{
+	uart_base     = (volatile void *)base;
+	uart_in_freq  = in_freq;
+	uart_baudrate = baudrate;
+
+	/* Disable interrupts */
+	set_reg(UART_REG_OFFSET_INT_DIS, 0xFFFFFFFF);
+	/* Disable TX RX */
+	set_reg(UART_REG_OFFSET_CTRL, UART_CR_TX_DIS | UART_CR_RX_DIS);
+	/* Configure baudrate */
+	if (in_freq) {
+		set_reg(UART_REG_OFFSET_BRG, UART_BRG_CLK_DIVISOR);
+		set_reg(UART_REG_OFFSET_BRD,
+			uart_min_clk_divisor(in_freq, baudrate));
+	}
+	/* Software reset TX RX data path and enable TX RX */
+	set_reg(UART_REG_OFFSET_CTRL, UART_CR_TXRST | UART_CR_RXRST
+		| UART_CR_TX_EN | UART_CR_RX_EN);
+	/* Set:
+	 * 1 stop bit, bits[07:06] = 0x00,
+	 * no parity set, bits[05:03] = 0x100,
+	 * 8 bits character length, bits[02:01] = 0x00,
+	 * sel_clk = uart_clk, bit[0] = 0x0
+	 */
+	set_reg(UART_REG_OFFSET_MODE, UART_MR_PARITY_NONE);
+
+	sbi_console_set_device(&cadence_console);
+
+	return 0;
+}
diff --git a/lib/utils/serial/fdt_serial_cadence.c b/lib/utils/serial/fdt_serial_cadence.c
new file mode 100644
index 0000000..2357520
--- /dev/null
+++ b/lib/utils/serial/fdt_serial_cadence.c
@@ -0,0 +1,35 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2022 StarFive Technology Co., Ltd.
+ *
+ * Author: Jun Liang Tan <junliang.tan at linux.starfivetech.com>
+ */
+
+#include <sbi_utils/fdt/fdt_helper.h>
+#include <sbi_utils/serial/fdt_serial.h>
+#include <sbi_utils/serial/cadence-uart.h>
+
+static int serial_cadence_init(void *fdt, int nodeoff,
+			       const struct fdt_match *match)
+{
+	int rc;
+	struct platform_uart_data uart;
+
+	rc = fdt_parse_uart_node(fdt, nodeoff, &uart);
+	if (rc)
+		return rc;
+
+	return cadence_uart_init(uart.addr, uart.freq, uart.baud);
+}
+
+static const struct fdt_match serial_cadence_match[] = {
+	{ .compatible = "cdns,uart-r1p12" },
+	{ .compatible = "starfive,jh8100-uart" },
+	{ },
+};
+
+struct fdt_serial fdt_serial_cadence = {
+	.match_table = serial_cadence_match,
+	.init = serial_cadence_init
+};
diff --git a/lib/utils/serial/fdt_serial_uart8250.c b/lib/utils/serial/fdt_serial_uart8250.c
index 544b741..0287f51 100644
--- a/lib/utils/serial/fdt_serial_uart8250.c
+++ b/lib/utils/serial/fdt_serial_uart8250.c
@@ -17,7 +17,7 @@ static int serial_uart8250_init(void *fdt, int nodeoff,
 	int rc;
 	struct platform_uart_data uart;
 
-	rc = fdt_parse_uart8250_node(fdt, nodeoff, &uart);
+	rc = fdt_parse_uart_node(fdt, nodeoff, &uart);
 	if (rc)
 		return rc;
 
diff --git a/lib/utils/serial/objects.mk b/lib/utils/serial/objects.mk
index d26a74e..76d6f94 100644
--- a/lib/utils/serial/objects.mk
+++ b/lib/utils/serial/objects.mk
@@ -10,6 +10,9 @@
 libsbiutils-objs-y += serial/fdt_serial.o
 libsbiutils-objs-y += serial/fdt_serial_drivers.o
 
+carray-fdt_serial_drivers-y += fdt_serial_cadence
+libsbiutils-objs-y += serial/fdt_serial_cadence.o
+
 carray-fdt_serial_drivers-y += fdt_serial_gaisler
 libsbiutils-objs-y += serial/fdt_serial_gaisler.o
 
@@ -31,6 +34,7 @@ libsbiutils-objs-y += serial/fdt_serial_uart8250.o
 carray-fdt_serial_drivers-y += fdt_serial_xlnx_uartlite
 libsbiutils-objs-y += serial/fdt_serial_xlnx_uartlite.o
 
+libsbiutils-objs-y += serial/cadence-uart.o
 libsbiutils-objs-y += serial/gaisler-uart.o
 libsbiutils-objs-y += serial/shakti-uart.o
 libsbiutils-objs-y += serial/sifive-uart.o
-- 
2.37.0




From ajones at ventanamicro.com  Fri Jul 15 01:32:14 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Fri, 15 Jul 2022 10:32:14 +0200
Subject: [PATCH v2] lib: utils: serial: Add Cadence UART driver
In-Reply-To: <20220715024054.7326-1-junliang.tan@linux.starfivetech.com>
References: <20220715024054.7326-1-junliang.tan@linux.starfivetech.com>
Message-ID: <20220715083214.vs5fvxhgt4m45waa@kamzik>

On Fri, Jul 15, 2022 at 10:40:54AM +0800, Jun Liang Tan wrote:
> Add Cadence UART driver
> 
> Signed-off-by: Jun Liang Tan <junliang.tan at linux.starfivetech.com>
> Signed-off-by: Wei Liang Lim <weiliang.lim at linux.starfivetech.com>
> ---
>  include/sbi_utils/fdt/fdt_helper.h      |   4 +-
>  include/sbi_utils/serial/cadence-uart.h |  16 +++
>  lib/utils/fdt/fdt_helper.c              |   6 +-
>  lib/utils/serial/cadence-uart.c         | 125 ++++++++++++++++++++++++
>  lib/utils/serial/fdt_serial_cadence.c   |  35 +++++++
>  lib/utils/serial/fdt_serial_uart8250.c  |   2 +-
>  lib/utils/serial/objects.mk             |   4 +
>  7 files changed, 186 insertions(+), 6 deletions(-)
>  create mode 100644 include/sbi_utils/serial/cadence-uart.h
>  create mode 100644 lib/utils/serial/cadence-uart.c
>  create mode 100644 lib/utils/serial/fdt_serial_cadence.c
> 
> diff --git a/include/sbi_utils/fdt/fdt_helper.h b/include/sbi_utils/fdt/fdt_helper.h
> index c60af35..bcd4996 100644
> --- a/include/sbi_utils/fdt/fdt_helper.h
> +++ b/include/sbi_utils/fdt/fdt_helper.h
> @@ -65,8 +65,8 @@ int fdt_parse_shakti_uart_node(void *fdt, int nodeoffset,
>  int fdt_parse_sifive_uart_node(void *fdt, int nodeoffset,
>  			       struct platform_uart_data *uart);
>  
> -int fdt_parse_uart8250_node(void *fdt, int nodeoffset,
> -			    struct platform_uart_data *uart);
> +int fdt_parse_uart_node(void *fdt, int nodeoffset,
> +			struct platform_uart_data *uart);
>  
>  int fdt_parse_uart8250(void *fdt, struct platform_uart_data *uart,
>  		       const char *compatible);
> diff --git a/include/sbi_utils/serial/cadence-uart.h b/include/sbi_utils/serial/cadence-uart.h
> new file mode 100644
> index 0000000..e75fb95
> --- /dev/null
> +++ b/include/sbi_utils/serial/cadence-uart.h
> @@ -0,0 +1,16 @@
> +/*
> + * SPDX-License-Identifier: BSD-2-Clause
> + *
> + * Copyright (c) 2022 StarFive Technology Co., Ltd.
> + *
> + * Author: Jun Liang Tan <junliang.tan at linux.starfivetech.com>
> + */
> +
> +#ifndef __SERIAL_CADENCE_UART_H__
> +#define __SERIAL_CADENCE_UART_H__
> +
> +#include <sbi/sbi_types.h>
> +
> +int cadence_uart_init(unsigned long base, u32 in_freq, u32 baudrate);
> +
> +#endif
> diff --git a/lib/utils/fdt/fdt_helper.c b/lib/utils/fdt/fdt_helper.c
> index 2bdb1ef..1e136fe 100644
> --- a/lib/utils/fdt/fdt_helper.c
> +++ b/lib/utils/fdt/fdt_helper.c
> @@ -427,8 +427,8 @@ int fdt_parse_sifive_uart_node(void *fdt, int nodeoffset,
>  	return 0;
>  }
>  
> -int fdt_parse_uart8250_node(void *fdt, int nodeoffset,
> -			    struct platform_uart_data *uart)
> +int fdt_parse_uart_node(void *fdt, int nodeoffset,
> +			struct platform_uart_data *uart)
>  {
>  	int len, rc;
>  	const fdt32_t *val;
> @@ -492,7 +492,7 @@ int fdt_parse_uart8250(void *fdt, struct platform_uart_data *uart,
>  	if (nodeoffset < 0)
>  		return nodeoffset;
>  
> -	return fdt_parse_uart8250_node(fdt, nodeoffset, uart);
> +	return fdt_parse_uart_node(fdt, nodeoffset, uart);
>  }
>  
>  int fdt_parse_xlnx_uartlite_node(void *fdt, int nodeoffset,
> diff --git a/lib/utils/serial/cadence-uart.c b/lib/utils/serial/cadence-uart.c
> new file mode 100644
> index 0000000..ffa79f8
> --- /dev/null
> +++ b/lib/utils/serial/cadence-uart.c
> @@ -0,0 +1,125 @@
> +/*
> + * SPDX-License-Identifier: BSD-2-Clause
> + *
> + * Copyright (c) 2022 StarFive Technology Co., Ltd.
> + *
> + * Author: Jun Liang Tan <junliang.tan at linux.starfivetech.com>
> + */
> +
> +#include <sbi/riscv_io.h>
> +#include <sbi/sbi_console.h>
> +#include <sbi_utils/serial/cadence-uart.h>
> +
> +/* clang-format off */
> +
> +#define UART_REG_OFFSET_CTRL		0x00	/* Control register */
> +#define UART_REG_OFFSET_MODE		0x04	/* Mode register */
> +#define UART_REG_OFFSET_CH_STS		0x2C	/* Channel status register */
> +#define UART_REG_OFFSET_RX_TX_FIFO	0x30	/* RX and TX FIFO register */
> +#define UART_REG_OFFSET_INT_DIS		0x0C	/* Interrupt disable register */
> +#define UART_REG_OFFSET_BRG	0x18	/* Baud rate generator register */
> +#define UART_REG_OFFSET_BRD	0x34	/* Baud rate divider register */

Can we please order these in ascending order by offset? Also, do these
define names match the manual you used for the register names? I'm looking
at B.33 of https://docs.xilinx.com/v/u/en-US/ug585-Zynq-7000-TRM (which I
found from a reference from QEMU code). In that manual they use CR for
control, MR for mode, etc. Names that match the documentation is generally
preferred (but I may be looking at the wrong documentation :-)

> +
> +/* Control register, 0x00 */

If we name the flags in a way that associate them with their register
then we don't need comments like the one above.

> +#define UART_CR_TX_DIS	0x00000020	/* TX disabled, bit 5 */
> +#define UART_CR_TX_EN	0x00000010	/* TX enabled, bit 4 */
> +#define UART_CR_RX_DIS	0x00000008	/* RX disabled, bit 3 */
> +#define UART_CR_RX_EN	0x00000004	/* RX enabled, bit 2*/
> +#define UART_CR_TXRST	0x00000002	/* TX logic reset, bit 1 */
> +#define UART_CR_RXRST	0x00000001	/* RX logic reset, bit 0 */

These names match the manual I'm looking at and make it clear we should
call the control register offset something like UART_REG_OFFSET_CR or
just UART_REG_CR in order for the flags to match up. Also, I would drop
the 'bit N' parts of the comments.

> +
> +/* Channel status register, 0x2C */
> +#define	UART_CHSR_TXFULL	0x00000010	/* TX FIFO full, bit 4 */
> +#define	UART_CHSR_RXEMPTY	0x00000002	/* RX FIFO empty, bit 1 */

Same comments as for the control register flags.

> +
> +/* Mode register, 0x04 */
> +#define UART_MR_PARITY_NONE	0x00000020	/* No parity set */
> +
> +/* Baud rate generator register, 0x18 */
> +#define UART_BRG_CLK_DIVISOR	0x00000001	/* baud_sample = sel_clk */

Please align all define columns above (both registers and flags), i.e.

#define DEFNAME			DEFVAL		/* COMMENT */

should line up all the way down.

> +
> +/* clang-format on */
> +
> +static volatile void *uart_base;
> +static u32 uart_in_freq;
> +static u32 uart_baudrate;
> +
> +/**
> + * Find minimum divisor divides in_freq to max_target_hz;
> + * Based on SiFive UART driver (sifive-uart.c)
> + */
> +static inline unsigned int uart_min_clk_divisor(uint64_t in_freq,
> +						uint64_t max_target_hz)
> +{
> +	uint64_t quotient = (in_freq + max_target_hz - 1) / (max_target_hz);

Neither sifive-uart nor this code is checking max_target_hz is non-zero.
And, since it can be read from the DT, there's a chance it could be.
(Hopefully people building DTs check their nodes have sane values, but
we shouldn't count on it.)

I think we should add

	if (max_target_hz == 0)
		return 0;

to this function and the sifive function.

> +	/* Avoid underflow */
> +	if (quotient == 0)
> +		return 0;
> +	else
> +		return quotient - 1;
> +}
> +
> +static u32 get_reg(u32 offset)
> +{
> +	return readl(uart_base + offset);
> +}
> +
> +static void set_reg(u32 offset, u32 val)
> +{
> +	writel(val, uart_base + offset);
> +}
> +
> +static void cadence_uart_putc(char ch)
> +{
> +	while (get_reg(UART_REG_OFFSET_CH_STS) & UART_CHSR_TXFULL)
> +		;
> +
> +	set_reg(UART_REG_OFFSET_RX_TX_FIFO, ch);
> +}
> +
> +static int cadence_uart_getc(void)
> +{
> +	u32 ret = get_reg(UART_REG_OFFSET_CH_STS);
> +
> +	if (!(ret & UART_CHSR_RXEMPTY))
> +		return get_reg(UART_REG_OFFSET_RX_TX_FIFO);
> +	return -1;
> +}
> +
> +static struct sbi_console_device cadence_console = {
> +	.name = "cadence_uart",
> +	.console_putc = cadence_uart_putc,
> +	.console_getc = cadence_uart_getc
> +};
> +
> +int cadence_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
> +{
> +	uart_base     = (volatile void *)base;
> +	uart_in_freq  = in_freq;
> +	uart_baudrate = baudrate;
> +
> +	/* Disable interrupts */
> +	set_reg(UART_REG_OFFSET_INT_DIS, 0xFFFFFFFF);

Please add a blank line here

> +	/* Disable TX RX */
> +	set_reg(UART_REG_OFFSET_CTRL, UART_CR_TX_DIS | UART_CR_RX_DIS);

and here

> +	/* Configure baudrate */
> +	if (in_freq) {
> +		set_reg(UART_REG_OFFSET_BRG, UART_BRG_CLK_DIVISOR);
> +		set_reg(UART_REG_OFFSET_BRD,
> +			uart_min_clk_divisor(in_freq, baudrate));
> +	}

and here

> +	/* Software reset TX RX data path and enable TX RX */
> +	set_reg(UART_REG_OFFSET_CTRL, UART_CR_TXRST | UART_CR_RXRST
> +		| UART_CR_TX_EN | UART_CR_RX_EN);

and here

> +	/* Set:
> +	 * 1 stop bit, bits[07:06] = 0x00,
> +	 * no parity set, bits[05:03] = 0x100,
> +	 * 8 bits character length, bits[02:01] = 0x00,
> +	 * sel_clk = uart_clk, bit[0] = 0x0
> +	 */
> +	set_reg(UART_REG_OFFSET_MODE, UART_MR_PARITY_NONE);
> +
> +	sbi_console_set_device(&cadence_console);
> +
> +	return 0;
> +}
> diff --git a/lib/utils/serial/fdt_serial_cadence.c b/lib/utils/serial/fdt_serial_cadence.c
> new file mode 100644
> index 0000000..2357520
> --- /dev/null
> +++ b/lib/utils/serial/fdt_serial_cadence.c
> @@ -0,0 +1,35 @@
> +/*
> + * SPDX-License-Identifier: BSD-2-Clause
> + *
> + * Copyright (c) 2022 StarFive Technology Co., Ltd.
> + *
> + * Author: Jun Liang Tan <junliang.tan at linux.starfivetech.com>
> + */
> +
> +#include <sbi_utils/fdt/fdt_helper.h>
> +#include <sbi_utils/serial/fdt_serial.h>
> +#include <sbi_utils/serial/cadence-uart.h>
> +
> +static int serial_cadence_init(void *fdt, int nodeoff,
> +			       const struct fdt_match *match)
> +{
> +	int rc;
> +	struct platform_uart_data uart;
> +
> +	rc = fdt_parse_uart_node(fdt, nodeoff, &uart);
> +	if (rc)
> +		return rc;
> +
> +	return cadence_uart_init(uart.addr, uart.freq, uart.baud);
> +}
> +
> +static const struct fdt_match serial_cadence_match[] = {
> +	{ .compatible = "cdns,uart-r1p12" },
> +	{ .compatible = "starfive,jh8100-uart" },
> +	{ },
> +};
> +
> +struct fdt_serial fdt_serial_cadence = {
> +	.match_table = serial_cadence_match,
> +	.init = serial_cadence_init
> +};
> diff --git a/lib/utils/serial/fdt_serial_uart8250.c b/lib/utils/serial/fdt_serial_uart8250.c
> index 544b741..0287f51 100644
> --- a/lib/utils/serial/fdt_serial_uart8250.c
> +++ b/lib/utils/serial/fdt_serial_uart8250.c
> @@ -17,7 +17,7 @@ static int serial_uart8250_init(void *fdt, int nodeoff,
>  	int rc;
>  	struct platform_uart_data uart;
>  
> -	rc = fdt_parse_uart8250_node(fdt, nodeoff, &uart);
> +	rc = fdt_parse_uart_node(fdt, nodeoff, &uart);
>  	if (rc)
>  		return rc;
>  
> diff --git a/lib/utils/serial/objects.mk b/lib/utils/serial/objects.mk
> index d26a74e..76d6f94 100644
> --- a/lib/utils/serial/objects.mk
> +++ b/lib/utils/serial/objects.mk
> @@ -10,6 +10,9 @@
>  libsbiutils-objs-y += serial/fdt_serial.o
>  libsbiutils-objs-y += serial/fdt_serial_drivers.o
>  
> +carray-fdt_serial_drivers-y += fdt_serial_cadence
> +libsbiutils-objs-y += serial/fdt_serial_cadence.o
> +
>  carray-fdt_serial_drivers-y += fdt_serial_gaisler
>  libsbiutils-objs-y += serial/fdt_serial_gaisler.o
>  
> @@ -31,6 +34,7 @@ libsbiutils-objs-y += serial/fdt_serial_uart8250.o
>  carray-fdt_serial_drivers-y += fdt_serial_xlnx_uartlite
>  libsbiutils-objs-y += serial/fdt_serial_xlnx_uartlite.o
>  
> +libsbiutils-objs-y += serial/cadence-uart.o
>  libsbiutils-objs-y += serial/gaisler-uart.o
>  libsbiutils-objs-y += serial/shakti-uart.o
>  libsbiutils-objs-y += serial/sifive-uart.o
> -- 
> 2.37.0
>

Thanks,
drew 


From apatel at ventanamicro.com  Mon Jul 18 05:41:56 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 18:11:56 +0530
Subject: [PATCH 00/14] OpenSBI Kconfig Support
Message-ID: <20220718124210.2177576-1-apatel@ventanamicro.com>

In future, we will see more drivers in OpenSBI for system reset, serial port,
power management, etc platform specific devices. We need platform specific
compile time configuration to allow users control the size of OpenSBI generic
firmware binaries by only enabling desired drivers. In other words, users
should be allowed to restrict the set of RISC-V platforms on which OpenSBI
generic firmwares built by them can run.

To address above, we add Kconfig support for OpenSBI platforms using the
kconfig-frontends project which is a standalone kconfig parser.

The kconfig-frontends is available as a package/receipe on various distros
(such as Debian, UBuntu, OpenSUSE, etc) and Yocto project.

To try OpenSBI Kconfig support with generic platform, here are few
example commands:
make PLATFORM=generic             <-- builds OpenSBI using "defconfig"
make PLATFORM=generic menuconfig  <-- launches graphical interface for changes

These patches can also be found in the kconfig_v1 brach at:
https://github.com/avpatel/opensbi.git

Anup Patel (14):
  Makefile: Add initial kconfig support for each platform
  Makefile: Compile lib/utils sources separately for each platform
  lib: utils/serial: Use kconfig for enabling/disabling drivers
  lib: utils/reset: Use kconfig for enabling/disabling drivers
  lib: utils/sys: Use kconfig for enabling/disabling drivers
  lib: utils/timer: Use kconfig for enabling/disabling drivers
  lib: utils/ipi: Use kconfig for enabling/disabling drivers
  lib: utils/irqchip: Use kconfig for enabling/disabling drivers
  lib: utils/i2c: Use kconfig for enabling/disabling drivers
  lib: utils/gpio: Use kconfig for enabling/disabling drivers
  lib: utils/fdt: Use kconfig for enabling/disabling
  platform: generic: Use kconfig for enabling/disabling overrides
  platform: Remove redundant config.mk from all platforms
  docs: Update documentation for kconfig support

 Kconfig                                   |  23 +++++
 Makefile                                  | 102 ++++++++++++++++------
 README.md                                 |  25 +++++-
 docs/firmware/fw_dynamic.md               |   2 +-
 docs/firmware/fw_jump.md                  |   4 +-
 docs/firmware/fw_payload.md               |   4 +-
 docs/library_usage.md                     |   6 +-
 docs/platform/platform.md                 |   4 +-
 docs/platform_guide.md                    |   9 +-
 firmware/Kconfig                          |   1 +
 include/sbi_utils/fdt/fdt_domain.h        |   9 ++
 include/sbi_utils/fdt/fdt_pmu.h           |  10 +++
 include/sbi_utils/ipi/fdt_ipi.h           |   9 ++
 include/sbi_utils/irqchip/fdt_irqchip.h   |  10 +++
 include/sbi_utils/irqchip/imsic.h         |  10 +++
 include/sbi_utils/reset/fdt_reset.h       |  12 +++
 include/sbi_utils/serial/fdt_serial.h     |   8 ++
 include/sbi_utils/timer/fdt_timer.h       |   9 ++
 lib/utils/Kconfig                         |  25 ++++++
 lib/utils/fdt/Kconfig                     |  18 ++++
 lib/utils/fdt/objects.mk                  |   8 +-
 lib/utils/gpio/Kconfig                    |  23 +++++
 lib/utils/gpio/objects.mk                 |  10 +--
 lib/utils/i2c/Kconfig                     |  23 +++++
 lib/utils/i2c/objects.mk                  |  10 +--
 lib/utils/ipi/Kconfig                     |  23 +++++
 lib/utils/ipi/objects.mk                  |  10 +--
 lib/utils/irqchip/Kconfig                 |  41 +++++++++
 lib/utils/irqchip/objects.mk              |  22 ++---
 lib/utils/libfdt/Kconfig                  |   5 ++
 lib/utils/libfdt/objects.mk               |   4 +-
 lib/utils/reset/Kconfig                   |  37 ++++++++
 lib/utils/reset/objects.mk                |  28 +++---
 lib/utils/serial/Kconfig                  |  73 ++++++++++++++++
 lib/utils/serial/objects.mk               |  44 +++++-----
 lib/utils/sys/Kconfig                     |  13 +++
 lib/utils/sys/objects.mk                  |   4 +-
 lib/utils/timer/Kconfig                   |  23 +++++
 lib/utils/timer/objects.mk                |  10 +--
 platform/andes/ae350/Kconfig              |   8 ++
 platform/andes/ae350/config.mk            |  36 --------
 platform/andes/ae350/configs/defconfig    |   0
 platform/andes/ae350/objects.mk           |  29 ++++++
 platform/fpga/ariane/Kconfig              |  10 +++
 platform/fpga/ariane/config.mk            |  36 --------
 platform/fpga/ariane/configs/defconfig    |   0
 platform/fpga/ariane/objects.mk           |  35 ++++++++
 platform/fpga/openpiton/Kconfig           |  10 +++
 platform/fpga/openpiton/config.mk         |  35 --------
 platform/fpga/openpiton/configs/defconfig |   0
 platform/fpga/openpiton/objects.mk        |  35 ++++++++
 platform/generic/Kconfig                  |  26 ++++++
 platform/generic/allwinner/objects.mk     |   4 +-
 platform/generic/config.mk                |  40 ---------
 platform/generic/configs/defconfig        |  29 ++++++
 platform/generic/objects.mk               |  33 +++++++
 platform/generic/sifive/objects.mk        |   8 +-
 platform/kendryte/k210/Kconfig            |  10 +++
 platform/kendryte/k210/config.mk          |  19 ----
 platform/kendryte/k210/configs/defconfig  |   0
 platform/kendryte/k210/objects.mk         |  12 +++
 platform/nuclei/ux600/Kconfig             |  10 +++
 platform/nuclei/ux600/config.mk           |  30 -------
 platform/nuclei/ux600/configs/defconfig   |   0
 platform/nuclei/ux600/objects.mk          |  22 +++++
 platform/nuclei/ux600/platform.c          |   1 -
 platform/template/Kconfig                 |   9 ++
 platform/template/config.mk               |  77 ----------------
 platform/template/configs/defconfig       |   0
 platform/template/objects.mk              |  72 +++++++++++++++
 platform/template/platform.c              |   2 +-
 scripts/create-binary-archive.sh          |   2 +-
 72 files changed, 948 insertions(+), 403 deletions(-)
 create mode 100644 Kconfig
 create mode 100644 firmware/Kconfig
 create mode 100644 lib/utils/Kconfig
 create mode 100644 lib/utils/fdt/Kconfig
 create mode 100644 lib/utils/gpio/Kconfig
 create mode 100644 lib/utils/i2c/Kconfig
 create mode 100644 lib/utils/ipi/Kconfig
 create mode 100644 lib/utils/irqchip/Kconfig
 create mode 100644 lib/utils/libfdt/Kconfig
 create mode 100644 lib/utils/reset/Kconfig
 create mode 100644 lib/utils/serial/Kconfig
 create mode 100644 lib/utils/sys/Kconfig
 create mode 100644 lib/utils/timer/Kconfig
 create mode 100644 platform/andes/ae350/Kconfig
 delete mode 100644 platform/andes/ae350/config.mk
 create mode 100644 platform/andes/ae350/configs/defconfig
 create mode 100644 platform/fpga/ariane/Kconfig
 delete mode 100644 platform/fpga/ariane/config.mk
 create mode 100644 platform/fpga/ariane/configs/defconfig
 create mode 100644 platform/fpga/openpiton/Kconfig
 delete mode 100644 platform/fpga/openpiton/config.mk
 create mode 100644 platform/fpga/openpiton/configs/defconfig
 create mode 100644 platform/generic/Kconfig
 delete mode 100644 platform/generic/config.mk
 create mode 100644 platform/generic/configs/defconfig
 create mode 100644 platform/kendryte/k210/Kconfig
 delete mode 100644 platform/kendryte/k210/config.mk
 create mode 100644 platform/kendryte/k210/configs/defconfig
 create mode 100644 platform/nuclei/ux600/Kconfig
 delete mode 100644 platform/nuclei/ux600/config.mk
 create mode 100644 platform/nuclei/ux600/configs/defconfig
 create mode 100644 platform/template/Kconfig
 delete mode 100644 platform/template/config.mk
 create mode 100644 platform/template/configs/defconfig

-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 05:41:57 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 18:11:57 +0530
Subject: [PATCH 01/14] Makefile: Add initial kconfig support for each platform
In-Reply-To: <20220718124210.2177576-1-apatel@ventanamicro.com>
References: <20220718124210.2177576-1-apatel@ventanamicro.com>
Message-ID: <20220718124210.2177576-2-apatel@ventanamicro.com>

We extend the top-level makefile to allow kconfig based configuration
for each platform where each platform has it's own set of configs with
"defconfig" being the default config.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 Kconfig                                   | 23 ++++++++++
 Makefile                                  | 54 +++++++++++++++++++++--
 firmware/Kconfig                          |  1 +
 lib/utils/Kconfig                         |  1 +
 platform/andes/ae350/Kconfig              |  5 +++
 platform/andes/ae350/configs/defconfig    |  0
 platform/fpga/ariane/Kconfig              |  5 +++
 platform/fpga/ariane/configs/defconfig    |  0
 platform/fpga/openpiton/Kconfig           |  5 +++
 platform/fpga/openpiton/configs/defconfig |  0
 platform/generic/Kconfig                  |  5 +++
 platform/generic/configs/defconfig        |  0
 platform/kendryte/k210/Kconfig            |  5 +++
 platform/kendryte/k210/configs/defconfig  |  0
 platform/nuclei/ux600/Kconfig             |  5 +++
 platform/nuclei/ux600/configs/defconfig   |  0
 platform/template/Kconfig                 |  5 +++
 platform/template/configs/defconfig       |  0
 18 files changed, 111 insertions(+), 3 deletions(-)
 create mode 100644 Kconfig
 create mode 100644 firmware/Kconfig
 create mode 100644 lib/utils/Kconfig
 create mode 100644 platform/andes/ae350/Kconfig
 create mode 100644 platform/andes/ae350/configs/defconfig
 create mode 100644 platform/fpga/ariane/Kconfig
 create mode 100644 platform/fpga/ariane/configs/defconfig
 create mode 100644 platform/fpga/openpiton/Kconfig
 create mode 100644 platform/fpga/openpiton/configs/defconfig
 create mode 100644 platform/generic/Kconfig
 create mode 100644 platform/generic/configs/defconfig
 create mode 100644 platform/kendryte/k210/Kconfig
 create mode 100644 platform/kendryte/k210/configs/defconfig
 create mode 100644 platform/nuclei/ux600/Kconfig
 create mode 100644 platform/nuclei/ux600/configs/defconfig
 create mode 100644 platform/template/Kconfig
 create mode 100644 platform/template/configs/defconfig

diff --git a/Kconfig b/Kconfig
new file mode 100644
index 0000000..b8615d2
--- /dev/null
+++ b/Kconfig
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: GPL-2.0
+
+mainmenu "OpenSBI $OPENSBI_PLATFORM Configuration"
+
+config OPENSBI_SRC_DIR
+	string
+	option env="OPENSBI_SRC_DIR"
+
+config OPENSBI_PLATFORM
+	string
+	option env="OPENSBI_PLATFORM"
+
+config OPENSBI_PLATFORM_SRC_DIR
+	string
+	option env="OPENSBI_PLATFORM_SRC_DIR"
+
+menu "Platform Options"
+source "$OPENSBI_PLATFORM_SRC_DIR/Kconfig"
+endmenu
+
+source "$OPENSBI_SRC_DIR/lib/utils/Kconfig"
+
+source "$OPENSBI_SRC_DIR/firmware/Kconfig"
diff --git a/Makefile b/Makefile
index f619ef7..46c7c1c 100644
--- a/Makefile
+++ b/Makefile
@@ -52,6 +52,9 @@ ifdef PLATFORM_DIR
 else
  platform_parent_dir=$(src_dir)/platform
 endif
+ifndef PLATFORM_DEFCONFIG
+PLATFORM_DEFCONFIG=defconfig
+endif
 
 # Check if verbosity is ON for build process
 CMD_PREFIX_DEFAULT := @
@@ -70,6 +73,19 @@ export libsbi_dir=$(CURDIR)/lib/sbi
 export libsbiutils_dir=$(CURDIR)/lib/utils
 export firmware_dir=$(CURDIR)/firmware
 
+# Setup variables for kconfig-frontends
+ifdef PLATFORM
+export KCONFIG_CONFIG=$(platform_build_dir)/.config
+export KCONFIG_AUTOCONFIG=$(platform_build_dir)/auto.conf
+export KCONFIG_TRISTATE=$(platform_build_dir)/tristate.conf
+export KCONFIG_AUTOHEADER=$(platform_build_dir)/autoconf.h
+export KCONFIG_AUTOCONF_CMD=$(platform_build_dir)/include/config/auto.conf.cmd
+# Additional exports for include paths in Kconfig files
+export OPENSBI_SRC_DIR=$(src_dir)
+export OPENSBI_PLATFORM=$(PLATFORM)
+export OPENSBI_PLATFORM_SRC_DIR=$(platform_src_dir)
+endif
+
 # Find library version
 OPENSBI_VERSION_MAJOR=`grep "define OPENSBI_VERSION_MAJOR" $(include_dir)/sbi/sbi_version.h | sed 's/.*MAJOR.*\([0-9][0-9]*\)/\1/'`
 OPENSBI_VERSION_MINOR=`grep "define OPENSBI_VERSION_MINOR" $(include_dir)/sbi/sbi_version.h | sed 's/.*MINOR.*\([0-9][0-9]*\)/\1/'`
@@ -183,8 +199,34 @@ libsbi-object-mks=$(shell if [ -d $(libsbi_dir) ]; then find $(libsbi_dir) -inam
 libsbiutils-object-mks=$(shell if [ -d $(libsbiutils_dir) ]; then find $(libsbiutils_dir) -iname "objects.mk" | sort -r; fi)
 firmware-object-mks=$(shell if [ -d $(firmware_dir) ]; then find $(firmware_dir) -iname "objects.mk" | sort -r; fi)
 
-# Include platform specifig config.mk
+# The "make all" rule should always be first rule
+.PHONY: all
+all:
+
+# Include platform specific .config and config.mk
 ifdef PLATFORM
+.PHONY: menuconfig
+menuconfig: $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
+	$(CMD_PREFIX)mkdir -p `dirname $(KCONFIG_CONFIG)`
+	$(CMD_PREFIX)mkdir -p `dirname $(KCONFIG_AUTOCONF_CMD)`
+	$(CMD_PREFIX)kconfig-mconf $(src_dir)/Kconfig
+	$(CMD_PREFIX)cd $(platform_build_dir) && kconfig-conf --silentoldconfig $(src_dir)/Kconfig
+
+.PHONY: savedefconfig
+savedefconfig: $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
+	$(CMD_PREFIX)mkdir -p `dirname $(KCONFIG_CONFIG)`
+	$(CMD_PREFIX)mkdir -p `dirname $(KCONFIG_AUTOCONF_CMD)`
+	$(CMD_PREFIX)kconfig-conf --savedefconfig `dirname $(KCONFIG_CONFIG)`/defconfig $(src_dir)/Kconfig
+
+$(KCONFIG_CONFIG): $(platform_src_dir)/configs/$(PLATFORM_DEFCONFIG) $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
+	$(CMD_PREFIX)mkdir -p `dirname $(KCONFIG_CONFIG)`
+	$(CMD_PREFIX)mkdir -p `dirname $(KCONFIG_AUTOCONF_CMD)`
+	$(CMD_PREFIX)cp -f $(platform_src_dir)/configs/$(PLATFORM_DEFCONFIG) $(KCONFIG_CONFIG)
+	$(CMD_PREFIX)cd $(platform_build_dir) && kconfig-conf --silentoldconfig --olddefconfig $(src_dir)/Kconfig
+$(KCONFIG_AUTOCONF_CMD): $(KCONFIG_CONFIG)
+include $(KCONFIG_CONFIG)
+include $(KCONFIG_AUTOCONF_CMD)
+
 include $(platform_src_dir)/config.mk
 endif
 
@@ -280,6 +322,9 @@ ifeq ($(BUILD_INFO),y)
 GENFLAGS	+=	-DOPENSBI_BUILD_TIME_STAMP="\"$(OPENSBI_BUILD_TIME_STAMP)\""
 GENFLAGS	+=	-DOPENSBI_BUILD_COMPILER_VERSION="\"$(OPENSBI_BUILD_COMPILER_VERSION)\""
 endif
+ifdef PLATFORM
+GENFLAGS	+=	-include $(KCONFIG_AUTOHEADER)
+endif
 GENFLAGS	+=	$(libsbiutils-genflags-y)
 GENFLAGS	+=	$(platform-genflags-y)
 GENFLAGS	+=	$(firmware-genflags-y)
@@ -419,7 +464,7 @@ targets-y += $(platform_build_dir)/lib/libplatsbi.a
 endif
 targets-y += $(firmware-bins-path-y)
 
-# Default rule "make" should always be first rule
+# The default "make all" rule
 .PHONY: all
 all: $(targets-y)
 
@@ -456,7 +501,7 @@ $(build_dir)/%.o: $(src_dir)/%.S
 	$(call compile_as,$@,$<)
 
 $(build_dir)/%.dep: $(src_dir)/%.carray
-	$(call compile_gen_dep,$@,.c,$<)
+	$(call compile_gen_dep,$@,.c,$< $(KCONFIG_CONFIG))
 	$(call compile_gen_dep,$@,.o,$(@:.dep=.c))
 
 $(build_dir)/%.c: $(src_dir)/%.carray
@@ -523,6 +568,9 @@ docs: $(build_dir)/docs/latex/refman.pdf
 # They should not be included for any "xxxconfig" or "xxxclean" rule
 all-deps-1 = $(if $(findstring config,$(MAKECMDGOALS)),,$(deps-y))
 all-deps-2 = $(if $(findstring clean,$(MAKECMDGOALS)),,$(all-deps-1))
+ifdef PLATFORM
+$(all-deps-2): | $(KCONFIG_CONFIG)
+endif
 -include $(all-deps-2)
 
 # Include external dependency of firmwares after default Makefile rules
diff --git a/firmware/Kconfig b/firmware/Kconfig
new file mode 100644
index 0000000..f66554c
--- /dev/null
+++ b/firmware/Kconfig
@@ -0,0 +1 @@
+# SPDX-License-Identifier: GPL-2.0
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
new file mode 100644
index 0000000..f66554c
--- /dev/null
+++ b/lib/utils/Kconfig
@@ -0,0 +1 @@
+# SPDX-License-Identifier: GPL-2.0
diff --git a/platform/andes/ae350/Kconfig b/platform/andes/ae350/Kconfig
new file mode 100644
index 0000000..349c1d3
--- /dev/null
+++ b/platform/andes/ae350/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config PLATFORM_ANDES_AE350
+	bool
+	default y
diff --git a/platform/andes/ae350/configs/defconfig b/platform/andes/ae350/configs/defconfig
new file mode 100644
index 0000000..e69de29
diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
new file mode 100644
index 0000000..26f9beb
--- /dev/null
+++ b/platform/fpga/ariane/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config PLATFORM_ARIANE_FPGA
+	bool
+	default y
diff --git a/platform/fpga/ariane/configs/defconfig b/platform/fpga/ariane/configs/defconfig
new file mode 100644
index 0000000..e69de29
diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
new file mode 100644
index 0000000..8bfa85a
--- /dev/null
+++ b/platform/fpga/openpiton/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config PLATFORM_OPENPITON_FPGA
+	bool
+	default y
diff --git a/platform/fpga/openpiton/configs/defconfig b/platform/fpga/openpiton/configs/defconfig
new file mode 100644
index 0000000..e69de29
diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
new file mode 100644
index 0000000..a811a05
--- /dev/null
+++ b/platform/generic/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config PLATFORM_GENERIC
+	bool
+	default y
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
new file mode 100644
index 0000000..e69de29
diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
new file mode 100644
index 0000000..726423a
--- /dev/null
+++ b/platform/kendryte/k210/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config PLATFORM_KENDRYTE_K210
+	bool
+	default y
diff --git a/platform/kendryte/k210/configs/defconfig b/platform/kendryte/k210/configs/defconfig
new file mode 100644
index 0000000..e69de29
diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
new file mode 100644
index 0000000..bb8410f
--- /dev/null
+++ b/platform/nuclei/ux600/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config PLATFORM_NUCLEI_UX600
+	bool
+	default y
diff --git a/platform/nuclei/ux600/configs/defconfig b/platform/nuclei/ux600/configs/defconfig
new file mode 100644
index 0000000..e69de29
diff --git a/platform/template/Kconfig b/platform/template/Kconfig
new file mode 100644
index 0000000..bd95ea0
--- /dev/null
+++ b/platform/template/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config PLATFORM_TEMPLATE
+	bool
+	default y
diff --git a/platform/template/configs/defconfig b/platform/template/configs/defconfig
new file mode 100644
index 0000000..e69de29
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 05:41:58 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 18:11:58 +0530
Subject: [PATCH 02/14] Makefile: Compile lib/utils sources separately for each platform
In-Reply-To: <20220718124210.2177576-1-apatel@ventanamicro.com>
References: <20220718124210.2177576-1-apatel@ventanamicro.com>
Message-ID: <20220718124210.2177576-3-apatel@ventanamicro.com>

Currently, if same build directory is used to compile two different
platforms then lib/utils objects are shared for these platforms.

We will be having platform specific configs to enable/disable drivers
in lib/utils and select compile time options for lib/utils sources.
This means lib/utils sources will now be compiled in a platform
specific way.

To tackle above, we update top-level Makefile as follows:
1) Don't create libsbiutils.a anymore because this can't be shared
   between platforms.
2) Compile lib/utils sources separately for each platform.
3) Add comments showing which make rules are for lib/sbi, lib/utils,
   firmware, and platform sources.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 Makefile                         | 48 ++++++++++++++++----------------
 docs/library_usage.md            |  6 +---
 scripts/create-binary-archive.sh |  2 +-
 3 files changed, 26 insertions(+), 30 deletions(-)

diff --git a/Makefile b/Makefile
index 46c7c1c..5fc67db 100644
--- a/Makefile
+++ b/Makefile
@@ -240,8 +240,8 @@ include $(firmware-object-mks)
 
 # Setup list of objects
 libsbi-objs-path-y=$(foreach obj,$(libsbi-objs-y),$(build_dir)/lib/sbi/$(obj))
-libsbiutils-objs-path-y=$(foreach obj,$(libsbiutils-objs-y),$(build_dir)/lib/utils/$(obj))
 ifdef PLATFORM
+libsbiutils-objs-path-y=$(foreach obj,$(libsbiutils-objs-y),$(platform_build_dir)/lib/utils/$(obj))
 platform-objs-path-y=$(foreach obj,$(platform-objs-y),$(platform_build_dir)/$(obj))
 firmware-bins-path-y=$(foreach bin,$(firmware-bins-y),$(platform_build_dir)/firmware/$(bin))
 endif
@@ -458,7 +458,6 @@ compile_gen_dep = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
 	     echo "$(1:.dep=$(2)): $(3)" >> $(1)
 
 targets-y  = $(build_dir)/lib/libsbi.a
-targets-y  += $(build_dir)/lib/libsbiutils.a
 ifdef PLATFORM
 targets-y += $(platform_build_dir)/lib/libplatsbi.a
 endif
@@ -471,12 +470,10 @@ all: $(targets-y)
 # Preserve all intermediate files
 .SECONDARY:
 
+# Rules for lib/sbi sources
 $(build_dir)/lib/libsbi.a: $(libsbi-objs-path-y)
 	$(call compile_ar,$@,$^)
 
-$(build_dir)/lib/libsbiutils.a: $(libsbi-objs-path-y) $(libsbiutils-objs-path-y)
-	$(call compile_ar,$@,$^)
-
 $(platform_build_dir)/lib/libplatsbi.a: $(libsbi-objs-path-y) $(libsbiutils-objs-path-y) $(platform-objs-path-y)
 	$(call compile_ar,$@,$^)
 
@@ -500,22 +497,14 @@ $(build_dir)/%.dep: $(src_dir)/%.S
 $(build_dir)/%.o: $(src_dir)/%.S
 	$(call compile_as,$@,$<)
 
-$(build_dir)/%.dep: $(src_dir)/%.carray
+# Rules for platform sources
+$(platform_build_dir)/%.dep: $(platform_src_dir)/%.carray
 	$(call compile_gen_dep,$@,.c,$< $(KCONFIG_CONFIG))
 	$(call compile_gen_dep,$@,.o,$(@:.dep=.c))
 
-$(build_dir)/%.c: $(src_dir)/%.carray
+$(platform_build_dir)/%.c: $(platform_src_dir)/%.carray
 	$(call compile_carray,$@,$<)
 
-$(platform_build_dir)/%.bin: $(platform_build_dir)/%.elf
-	$(call compile_objcopy,$@,$<)
-
-$(platform_build_dir)/%.elf: $(platform_build_dir)/%.o $(platform_build_dir)/%.elf.ld $(platform_build_dir)/lib/libplatsbi.a
-	$(call compile_elf,$@,$@.ld,$< $(platform_build_dir)/lib/libplatsbi.a)
-
-$(platform_build_dir)/%.ld: $(src_dir)/%.ldS
-	$(call compile_cpp,$@,$<)
-
 $(platform_build_dir)/%.dep: $(platform_src_dir)/%.c
 	$(call compile_cc_dep,$@,$<)
 
@@ -539,6 +528,23 @@ $(platform_build_dir)/%.c: $(platform_build_dir)/%.dtb
 $(platform_build_dir)/%.dtb: $(platform_src_dir)/%.dts
 	$(call compile_dts,$@,$<)
 
+# Rules for lib/utils and firmware sources
+$(platform_build_dir)/%.bin: $(platform_build_dir)/%.elf
+	$(call compile_objcopy,$@,$<)
+
+$(platform_build_dir)/%.elf: $(platform_build_dir)/%.o $(platform_build_dir)/%.elf.ld $(platform_build_dir)/lib/libplatsbi.a
+	$(call compile_elf,$@,$@.ld,$< $(platform_build_dir)/lib/libplatsbi.a)
+
+$(platform_build_dir)/%.ld: $(src_dir)/%.ldS
+	$(call compile_cpp,$@,$<)
+
+$(platform_build_dir)/%.dep: $(src_dir)/%.carray
+	$(call compile_gen_dep,$@,.c,$< $(KCONFIG_CONFIG))
+	$(call compile_gen_dep,$@,.o,$(@:.dep=.c))
+
+$(platform_build_dir)/%.c: $(src_dir)/%.carray
+	$(call compile_carray,$@,$<)
+
 $(platform_build_dir)/%.dep: $(src_dir)/%.c
 	$(call compile_cc_dep,$@,$<)
 
@@ -592,7 +598,6 @@ endif
 endif
 
 install_targets-y  = install_libsbi
-install_targets-y  += install_libsbiutils
 ifdef PLATFORM
 install_targets-y += install_libplatsbi
 install_targets-y += install_firmwares
@@ -607,17 +612,12 @@ install_libsbi: $(build_dir)/lib/libsbi.a
 	$(call inst_header_dir,$(install_root_dir)/$(install_include_path),$(include_dir)/sbi)
 	$(call inst_file,$(install_root_dir)/$(install_lib_path)/libsbi.a,$(build_dir)/lib/libsbi.a)
 
-.PHONY: install_libsbiutils
-install_libsbiutils: $(build_dir)/lib/libsbiutils.a
-	$(call inst_header_dir,$(install_root_dir)/$(install_include_path),$(include_dir)/sbi_utils)
-	$(call inst_file,$(install_root_dir)/$(install_lib_path)/libsbiutils.a,$(build_dir)/lib/libsbiutils.a)
-
 .PHONY: install_libplatsbi
-install_libplatsbi: $(platform_build_dir)/lib/libplatsbi.a $(build_dir)/lib/libsbi.a $(build_dir)/lib/libsbiutils.a
+install_libplatsbi: $(platform_build_dir)/lib/libplatsbi.a $(build_dir)/lib/libsbi.a
 	$(call inst_file,$(install_root_dir)/$(install_lib_path)/opensbi/$(platform_subdir)/lib/libplatsbi.a,$(platform_build_dir)/lib/libplatsbi.a)
 
 .PHONY: install_firmwares
-install_firmwares: $(platform_build_dir)/lib/libplatsbi.a $(build_dir)/lib/libsbi.a $(build_dir)/lib/libsbiutils.a $(firmware-bins-path-y)
+install_firmwares: $(platform_build_dir)/lib/libplatsbi.a $(build_dir)/lib/libsbi.a $(firmware-bins-path-y)
 	$(call inst_file_list,$(install_root_dir),$(build_dir),$(install_firmware_path)/$(platform_subdir)/firmware,$(firmware-elfs-path-y))
 	$(call inst_file_list,$(install_root_dir),$(build_dir),$(install_firmware_path)/$(platform_subdir)/firmware,$(firmware-bins-path-y))
 
diff --git a/docs/library_usage.md b/docs/library_usage.md
index ff99801..d5d2ba9 100644
--- a/docs/library_usage.md
+++ b/docs/library_usage.md
@@ -8,11 +8,7 @@ OpenSBI provides two types of static libraries:
    hooks for the execution of this interface must be provided by the firmware or
    bootloader linking with this library. This library is installed as
    *<install_directory>/lib/libsbi.a*
-2. *libsbiutils.a* - A static library that will contain all common code required
-   by any platform supported in OpenSBI. It will be built by default and included
-   in libplatsbi.a. This library is installed as
-   *<install_directory>/lib/libsbiutils.a*.
-3. *libplatsbi.a* - An example platform-specific static library integrating
+2. *libplatsbi.a* - An example platform-specific static library integrating
    *libsbi.a* with platform-specific hooks. This library is available only for
    the platforms supported by OpenSBI. This library is installed as
    *<install_directory>/platform/<platform_subdir>/lib/libplatsbi.a*
diff --git a/scripts/create-binary-archive.sh b/scripts/create-binary-archive.sh
index 43c5452..261a45a 100755
--- a/scripts/create-binary-archive.sh
+++ b/scripts/create-binary-archive.sh
@@ -118,7 +118,7 @@ build_opensbi() {
 	# Build and install generic library
 	echo "Build and install generic library XLEN=${BUILD_RISCV_XLEN}"
 	echo ""
-	make -C "${BUILD_OPENSBI_SOURCE_PATH}" O="${BUILD_OUTPUT_PATH}/${BUILD_NAME}" I="${BUILD_OUTPUT_PATH}/${BUILD_ARCHIVE_NAME}" PLATFORM_RISCV_XLEN="${BUILD_RISCV_XLEN}" install_libsbi install_libsbiutils -j "${BUILD_NUM_THREADS}"
+	make -C "${BUILD_OPENSBI_SOURCE_PATH}" O="${BUILD_OUTPUT_PATH}/${BUILD_NAME}" I="${BUILD_OUTPUT_PATH}/${BUILD_ARCHIVE_NAME}" PLATFORM_RISCV_XLEN="${BUILD_RISCV_XLEN}" install_libsbi -j "${BUILD_NUM_THREADS}"
 	echo ""
 
 	# Build and install relevant platforms
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 05:41:59 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 18:11:59 +0530
Subject: [PATCH 03/14] lib: utils/serial: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220718124210.2177576-1-apatel@ventanamicro.com>
References: <20220718124210.2177576-1-apatel@ventanamicro.com>
Message-ID: <20220718124210.2177576-4-apatel@ventanamicro.com>

We update serial drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate serial
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 include/sbi_utils/serial/fdt_serial.h |  8 +++
 lib/utils/Kconfig                     |  6 +++
 lib/utils/serial/Kconfig              | 71 +++++++++++++++++++++++++++
 lib/utils/serial/objects.mk           | 44 ++++++++---------
 platform/andes/ae350/Kconfig          |  1 +
 platform/fpga/ariane/Kconfig          |  1 +
 platform/fpga/openpiton/Kconfig       |  1 +
 platform/generic/configs/defconfig    |  8 +++
 platform/kendryte/k210/Kconfig        |  1 +
 platform/nuclei/ux600/Kconfig         |  1 +
 platform/template/Kconfig             |  1 +
 11 files changed, 121 insertions(+), 22 deletions(-)
 create mode 100644 lib/utils/serial/Kconfig

diff --git a/include/sbi_utils/serial/fdt_serial.h b/include/sbi_utils/serial/fdt_serial.h
index 6451c23..daa2e4f 100644
--- a/include/sbi_utils/serial/fdt_serial.h
+++ b/include/sbi_utils/serial/fdt_serial.h
@@ -12,6 +12,8 @@
 
 #include <sbi/sbi_types.h>
 
+#ifdef CONFIG_FDT_SERIAL
+
 struct fdt_serial {
 	const struct fdt_match *match_table;
 	int (*init)(void *fdt, int nodeoff, const struct fdt_match *match);
@@ -19,4 +21,10 @@ struct fdt_serial {
 
 int fdt_serial_init(void);
 
+#else
+
+static inline int fdt_serial_init(void) { return 0; }
+
+#endif
+
 #endif
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index f66554c..464bd03 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -1 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
+
+menu "Utils and Drivers Support"
+
+source "$OPENSBI_SRC_DIR/lib/utils/serial/Kconfig"
+
+endmenu
diff --git a/lib/utils/serial/Kconfig b/lib/utils/serial/Kconfig
new file mode 100644
index 0000000..9e6b6fc
--- /dev/null
+++ b/lib/utils/serial/Kconfig
@@ -0,0 +1,71 @@
+# SPDX-License-Identifier: GPL-2.0
+
+menu "Serial Device Support"
+
+config FDT_SERIAL
+	bool "FDT based serial drivers"
+	default n
+
+if FDT_SERIAL
+
+config FDT_SERIAL_GAISLER
+	bool "Gaisler UART FDT driver"
+	select SERIAL_GAISLER
+	default n
+
+config FDT_SERIAL_HTIF
+	bool "Host transfer interface (HTIF) UART FDT driver"
+	default n
+
+config FDT_SERIAL_SHAKTI
+	bool "Shakti UART FDT driver"
+	select SERIAL_SHAKTI
+	default n
+
+config FDT_SERIAL_SIFIVE
+	bool "SiFive UART FDT driver"
+	select SERIAL_SIFIVE
+	default n
+
+config FDT_SERIAL_LITEX
+	bool "LiteX UART FDT driver"
+	select SERIAL_LITEX
+	default n
+
+config FDT_SERIAL_UART8250
+	bool "8250 UART FDT driver"
+	select SERIAL_UART8250
+	default n
+
+config FDT_SERIAL_XILINX_UARTLITE
+	bool "Xilinx UART Lite FDT driver"
+	select SERIAL_XILINX_UARTLITE
+	default n
+
+endif
+
+config SERIAL_GAISLER
+	bool "Gaisler UART support"
+	default n
+
+config SERIAL_SHAKTI
+	bool "Shakti UART support"
+	default n
+
+config SERIAL_SIFIVE
+	bool "SiFive UART support"
+	default n
+
+config SERIAL_LITEX
+	bool "LiteX UART support"
+	default n
+
+config SERIAL_UART8250
+	bool "8250 UART support"
+	default n
+
+config SERIAL_XILINX_UARTLITE
+	bool "Xilinx UART Lite support"
+	default n
+
+endmenu
diff --git a/lib/utils/serial/objects.mk b/lib/utils/serial/objects.mk
index d26a74e..fa9f5a3 100644
--- a/lib/utils/serial/objects.mk
+++ b/lib/utils/serial/objects.mk
@@ -7,33 +7,33 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += serial/fdt_serial.o
-libsbiutils-objs-y += serial/fdt_serial_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_SERIAL) += serial/fdt_serial.o
+libsbiutils-objs-$(CONFIG_FDT_SERIAL) += serial/fdt_serial_drivers.o
 
-carray-fdt_serial_drivers-y += fdt_serial_gaisler
-libsbiutils-objs-y += serial/fdt_serial_gaisler.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_GAISLER) += fdt_serial_gaisler
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_GAISLER) += serial/fdt_serial_gaisler.o
 
-carray-fdt_serial_drivers-y += fdt_serial_htif
-libsbiutils-objs-y += serial/fdt_serial_htif.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_HTIF) += fdt_serial_htif
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_HTIF) += serial/fdt_serial_htif.o
 
-carray-fdt_serial_drivers-y += fdt_serial_shakti
-libsbiutils-objs-y += serial/fdt_serial_shakti.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_SHAKTI) += fdt_serial_shakti
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_SHAKTI) += serial/fdt_serial_shakti.o
 
-carray-fdt_serial_drivers-y += fdt_serial_sifive
-libsbiutils-objs-y += serial/fdt_serial_sifive.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_SIFIVE) += fdt_serial_sifive
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_SIFIVE) += serial/fdt_serial_sifive.o
 
-carray-fdt_serial_drivers-y += fdt_serial_litex
-libsbiutils-objs-y += serial/fdt_serial_litex.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_LITEX) += fdt_serial_litex
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_LITEX) += serial/fdt_serial_litex.o
 
-carray-fdt_serial_drivers-y += fdt_serial_uart8250
-libsbiutils-objs-y += serial/fdt_serial_uart8250.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_UART8250) += fdt_serial_uart8250
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_UART8250) += serial/fdt_serial_uart8250.o
 
-carray-fdt_serial_drivers-y += fdt_serial_xlnx_uartlite
-libsbiutils-objs-y += serial/fdt_serial_xlnx_uartlite.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_XILINX_UARTLITE) += fdt_serial_xlnx_uartlite
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_XILINX_UARTLITE) += serial/fdt_serial_xlnx_uartlite.o
 
-libsbiutils-objs-y += serial/gaisler-uart.o
-libsbiutils-objs-y += serial/shakti-uart.o
-libsbiutils-objs-y += serial/sifive-uart.o
-libsbiutils-objs-y += serial/litex-uart.o
-libsbiutils-objs-y += serial/uart8250.o
-libsbiutils-objs-y += serial/xlnx-uartlite.o
+libsbiutils-objs-$(CONFIG_SERIAL_GAISLER) += serial/gaisler-uart.o
+libsbiutils-objs-$(CONFIG_SERIAL_SHAKTI) += serial/shakti-uart.o
+libsbiutils-objs-$(CONFIG_SERIAL_SIFIVE) += serial/sifive-uart.o
+libsbiutils-objs-$(CONFIG_SERIAL_LITEX) += serial/litex-uart.o
+libsbiutils-objs-$(CONFIG_SERIAL_UART8250) += serial/uart8250.o
+libsbiutils-objs-$(CONFIG_SERIAL_XILINX_UARTLITE) += serial/xlnx-uartlite.o
diff --git a/platform/andes/ae350/Kconfig b/platform/andes/ae350/Kconfig
index 349c1d3..0048c1b 100644
--- a/platform/andes/ae350/Kconfig
+++ b/platform/andes/ae350/Kconfig
@@ -2,4 +2,5 @@
 
 config PLATFORM_ANDES_AE350
 	bool
+	select SERIAL_UART8250
 	default y
diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
index 26f9beb..797af74 100644
--- a/platform/fpga/ariane/Kconfig
+++ b/platform/fpga/ariane/Kconfig
@@ -2,4 +2,5 @@
 
 config PLATFORM_ARIANE_FPGA
 	bool
+	select SERIAL_UART8250
 	default y
diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
index 8bfa85a..09b658a 100644
--- a/platform/fpga/openpiton/Kconfig
+++ b/platform/fpga/openpiton/Kconfig
@@ -2,4 +2,5 @@
 
 config PLATFORM_OPENPITON_FPGA
 	bool
+	select SERIAL_UART8250
 	default y
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index e69de29..ce70c59 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -0,0 +1,8 @@
+CONFIG_FDT_SERIAL=y
+CONFIG_FDT_SERIAL_GAISLER=y
+CONFIG_FDT_SERIAL_HTIF=y
+CONFIG_FDT_SERIAL_SHAKTI=y
+CONFIG_FDT_SERIAL_SIFIVE=y
+CONFIG_FDT_SERIAL_LITEX=y
+CONFIG_FDT_SERIAL_UART8250=y
+CONFIG_FDT_SERIAL_XILINX_UARTLITE=y
diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
index 726423a..bc921c8 100644
--- a/platform/kendryte/k210/Kconfig
+++ b/platform/kendryte/k210/Kconfig
@@ -2,4 +2,5 @@
 
 config PLATFORM_KENDRYTE_K210
 	bool
+	select SERIAL_SIFIVE
 	default y
diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
index bb8410f..c4da266 100644
--- a/platform/nuclei/ux600/Kconfig
+++ b/platform/nuclei/ux600/Kconfig
@@ -2,4 +2,5 @@
 
 config PLATFORM_NUCLEI_UX600
 	bool
+	select SERIAL_SIFIVE
 	default y
diff --git a/platform/template/Kconfig b/platform/template/Kconfig
index bd95ea0..e01fb31 100644
--- a/platform/template/Kconfig
+++ b/platform/template/Kconfig
@@ -2,4 +2,5 @@
 
 config PLATFORM_TEMPLATE
 	bool
+	select SERIAL_UART8250
 	default y
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 05:42:00 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 18:12:00 +0530
Subject: [PATCH 04/14] lib: utils/reset: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220718124210.2177576-1-apatel@ventanamicro.com>
References: <20220718124210.2177576-1-apatel@ventanamicro.com>
Message-ID: <20220718124210.2177576-5-apatel@ventanamicro.com>

We update reset drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate reset
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 include/sbi_utils/reset/fdt_reset.h | 12 +++++++++++
 lib/utils/Kconfig                   |  2 ++
 lib/utils/reset/Kconfig             | 33 +++++++++++++++++++++++++++++
 lib/utils/reset/objects.mk          | 28 ++++++++++++------------
 platform/generic/configs/defconfig  |  6 ++++++
 5 files changed, 67 insertions(+), 14 deletions(-)
 create mode 100644 lib/utils/reset/Kconfig

diff --git a/include/sbi_utils/reset/fdt_reset.h b/include/sbi_utils/reset/fdt_reset.h
index e7f7350..ea8063b 100644
--- a/include/sbi_utils/reset/fdt_reset.h
+++ b/include/sbi_utils/reset/fdt_reset.h
@@ -17,6 +17,8 @@ struct fdt_reset {
 	int (*init)(void *fdt, int nodeoff, const struct fdt_match *match);
 };
 
+#ifdef CONFIG_FDT_RESET
+
 /**
  * fdt_reset_driver_init() - initialize reset driver based on the device-tree
  */
@@ -29,4 +31,14 @@ int fdt_reset_driver_init(void *fdt, struct fdt_reset *drv);
  */
 void fdt_reset_init(void);
 
+#else
+
+static inline int fdt_reset_driver_init(void *fdt, struct fdt_reset *drv)
+{
+	return 0;
+}
+static inline void fdt_reset_init(void) { }
+
+#endif
+
 #endif
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 464bd03..5330e87 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -2,6 +2,8 @@
 
 menu "Utils and Drivers Support"
 
+source "$OPENSBI_SRC_DIR/lib/utils/reset/Kconfig"
+
 source "$OPENSBI_SRC_DIR/lib/utils/serial/Kconfig"
 
 endmenu
diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig
new file mode 100644
index 0000000..d4a32a0
--- /dev/null
+++ b/lib/utils/reset/Kconfig
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: GPL-2.0
+
+menu "System Reset Support"
+
+config FDT_RESET
+	bool "FDT based reset drivers"
+	default n
+
+if FDT_RESET
+
+config FDT_RESET_GPIO
+	bool "GPIO FDT reset driver"
+	default n
+
+config FDT_RESET_HTIF
+	bool "Host transfer interface (HTIF) FDT reset driver"
+	default n
+
+config FDT_RESET_SIFIVE_TEST
+	bool "SiFive Test FDT reset driver"
+	default n
+
+config FDT_RESET_SUNXI_WDT
+	bool "Sunxi WDT FDT reset driver"
+	default n
+
+config FDT_RESET_THEAD
+	bool "T-HEAD FDT reset driver"
+	default n
+
+endif
+
+endmenu
diff --git a/lib/utils/reset/objects.mk b/lib/utils/reset/objects.mk
index 8cddcdf..8a50dd0 100644
--- a/lib/utils/reset/objects.mk
+++ b/lib/utils/reset/objects.mk
@@ -7,22 +7,22 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += reset/fdt_reset.o
-libsbiutils-objs-y += reset/fdt_reset_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_RESET) += reset/fdt_reset.o
+libsbiutils-objs-$(CONFIG_FDT_RESET) += reset/fdt_reset_drivers.o
 
-carray-fdt_reset_drivers-y += fdt_poweroff_gpio
-carray-fdt_reset_drivers-y += fdt_reset_gpio
-libsbiutils-objs-y += reset/fdt_reset_gpio.o
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_GPIO) += fdt_poweroff_gpio
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_GPIO) += fdt_reset_gpio
+libsbiutils-objs-$(CONFIG_FDT_RESET_GPIO) += reset/fdt_reset_gpio.o
 
-carray-fdt_reset_drivers-y += fdt_reset_htif
-libsbiutils-objs-y += reset/fdt_reset_htif.o
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_HTIF) += fdt_reset_htif
+libsbiutils-objs-$(CONFIG_FDT_RESET_HTIF) += reset/fdt_reset_htif.o
 
-carray-fdt_reset_drivers-y += fdt_reset_sifive_test
-libsbiutils-objs-y += reset/fdt_reset_sifive_test.o
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_SIFIVE_TEST) += fdt_reset_sifive_test
+libsbiutils-objs-$(CONFIG_FDT_RESET_SIFIVE_TEST) += reset/fdt_reset_sifive_test.o
 
-carray-fdt_reset_drivers-y += fdt_reset_sunxi_wdt
-libsbiutils-objs-y += reset/fdt_reset_sunxi_wdt.o
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_SUNXI_WDT) += fdt_reset_sunxi_wdt
+libsbiutils-objs-$(CONFIG_FDT_RESET_SUNXI_WDT) += reset/fdt_reset_sunxi_wdt.o
 
-carray-fdt_reset_drivers-y += fdt_reset_thead
-libsbiutils-objs-y += reset/fdt_reset_thead.o
-libsbiutils-objs-y += reset/fdt_reset_thead_asm.o
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_THEAD) += fdt_reset_thead
+libsbiutils-objs-$(CONFIG_FDT_RESET_THEAD) += reset/fdt_reset_thead.o
+libsbiutils-objs-$(CONFIG_FDT_RESET_THEAD) += reset/fdt_reset_thead_asm.o
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index ce70c59..9778665 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -1,3 +1,9 @@
+CONFIG_FDT_RESET=y
+CONFIG_FDT_RESET_GPIO=y
+CONFIG_FDT_RESET_HTIF=y
+CONFIG_FDT_RESET_SIFIVE_TEST=y
+CONFIG_FDT_RESET_SUNXI_WDT=y
+CONFIG_FDT_RESET_THEAD=y
 CONFIG_FDT_SERIAL=y
 CONFIG_FDT_SERIAL_GAISLER=y
 CONFIG_FDT_SERIAL_HTIF=y
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 05:42:01 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 18:12:01 +0530
Subject: [PATCH 05/14] lib: utils/sys: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220718124210.2177576-1-apatel@ventanamicro.com>
References: <20220718124210.2177576-1-apatel@ventanamicro.com>
Message-ID: <20220718124210.2177576-6-apatel@ventanamicro.com>

We update system drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate system
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 lib/utils/Kconfig        |  2 ++
 lib/utils/reset/Kconfig  |  2 ++
 lib/utils/serial/Kconfig |  1 +
 lib/utils/sys/Kconfig    | 13 +++++++++++++
 lib/utils/sys/objects.mk |  4 ++--
 5 files changed, 20 insertions(+), 2 deletions(-)
 create mode 100644 lib/utils/sys/Kconfig

diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 5330e87..6442b75 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -6,4 +6,6 @@ source "$OPENSBI_SRC_DIR/lib/utils/reset/Kconfig"
 
 source "$OPENSBI_SRC_DIR/lib/utils/serial/Kconfig"
 
+source "$OPENSBI_SRC_DIR/lib/utils/sys/Kconfig"
+
 endmenu
diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig
index d4a32a0..4f04d8c 100644
--- a/lib/utils/reset/Kconfig
+++ b/lib/utils/reset/Kconfig
@@ -14,10 +14,12 @@ config FDT_RESET_GPIO
 
 config FDT_RESET_HTIF
 	bool "Host transfer interface (HTIF) FDT reset driver"
+	select SYS_HTIF
 	default n
 
 config FDT_RESET_SIFIVE_TEST
 	bool "SiFive Test FDT reset driver"
+	select SYS_SIFIVE_TEST
 	default n
 
 config FDT_RESET_SUNXI_WDT
diff --git a/lib/utils/serial/Kconfig b/lib/utils/serial/Kconfig
index 9e6b6fc..a425b13 100644
--- a/lib/utils/serial/Kconfig
+++ b/lib/utils/serial/Kconfig
@@ -15,6 +15,7 @@ config FDT_SERIAL_GAISLER
 
 config FDT_SERIAL_HTIF
 	bool "Host transfer interface (HTIF) UART FDT driver"
+	select SYS_HTIF
 	default n
 
 config FDT_SERIAL_SHAKTI
diff --git a/lib/utils/sys/Kconfig b/lib/utils/sys/Kconfig
new file mode 100644
index 0000000..0fa6b6d
--- /dev/null
+++ b/lib/utils/sys/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+
+menu "System Device Support"
+
+config SYS_HTIF
+	bool "Host transfere interface (HTIF) support"
+	default n
+
+config SYS_SIFIVE_TEST
+	bool "SiFive test support"
+	default n
+
+endmenu
diff --git a/lib/utils/sys/objects.mk b/lib/utils/sys/objects.mk
index 06be322..9f67aee 100644
--- a/lib/utils/sys/objects.mk
+++ b/lib/utils/sys/objects.mk
@@ -7,5 +7,5 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += sys/htif.o
-libsbiutils-objs-y += sys/sifive_test.o
+libsbiutils-objs-$(CONFIG_SYS_HTIF) += sys/htif.o
+libsbiutils-objs-$(CONFIG_SYS_SIFIVE_TEST) += sys/sifive_test.o
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 05:42:02 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 18:12:02 +0530
Subject: [PATCH 06/14] lib: utils/timer: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220718124210.2177576-1-apatel@ventanamicro.com>
References: <20220718124210.2177576-1-apatel@ventanamicro.com>
Message-ID: <20220718124210.2177576-7-apatel@ventanamicro.com>

We update timer drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate timer
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 include/sbi_utils/timer/fdt_timer.h |  9 +++++++++
 lib/utils/Kconfig                   |  2 ++
 lib/utils/timer/Kconfig             | 22 ++++++++++++++++++++++
 lib/utils/timer/objects.mk          | 10 +++++-----
 platform/fpga/ariane/Kconfig        |  1 +
 platform/fpga/openpiton/Kconfig     |  1 +
 platform/generic/configs/defconfig  |  2 ++
 platform/kendryte/k210/Kconfig      |  1 +
 platform/nuclei/ux600/Kconfig       |  1 +
 platform/template/Kconfig           |  1 +
 10 files changed, 45 insertions(+), 5 deletions(-)
 create mode 100644 lib/utils/timer/Kconfig

diff --git a/include/sbi_utils/timer/fdt_timer.h b/include/sbi_utils/timer/fdt_timer.h
index 36202a4..cf105fe 100644
--- a/include/sbi_utils/timer/fdt_timer.h
+++ b/include/sbi_utils/timer/fdt_timer.h
@@ -12,6 +12,8 @@
 
 #include <sbi/sbi_types.h>
 
+#ifdef CONFIG_FDT_TIMER
+
 struct fdt_timer {
 	const struct fdt_match *match_table;
 	int (*cold_init)(void *fdt, int nodeoff, const struct fdt_match *match);
@@ -23,4 +25,11 @@ void fdt_timer_exit(void);
 
 int fdt_timer_init(bool cold_boot);
 
+#else
+
+static inline void fdt_timer_exit(void) { }
+static inline int fdt_timer_init(bool cold_boot) { return 0; }
+
+#endif
+
 #endif
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 6442b75..e169ed4 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -8,4 +8,6 @@ source "$OPENSBI_SRC_DIR/lib/utils/serial/Kconfig"
 
 source "$OPENSBI_SRC_DIR/lib/utils/sys/Kconfig"
 
+source "$OPENSBI_SRC_DIR/lib/utils/timer/Kconfig"
+
 endmenu
diff --git a/lib/utils/timer/Kconfig b/lib/utils/timer/Kconfig
new file mode 100644
index 0000000..3b1221a
--- /dev/null
+++ b/lib/utils/timer/Kconfig
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0
+
+menu "Timer Device Support"
+
+config FDT_TIMER
+	bool "FDT based timer drivers"
+	default n
+
+if FDT_TIMER
+
+config FDT_TIMER_MTIMER
+	bool "ACLINT MTIMER FDT driver"
+	select TIMER_MTIMER
+	default n
+
+endif
+
+config TIMER_MTIMER
+	bool "ACLINT MTIMER support"
+	default n
+
+endmenu
diff --git a/lib/utils/timer/objects.mk b/lib/utils/timer/objects.mk
index bc4073d..7f5f3ce 100644
--- a/lib/utils/timer/objects.mk
+++ b/lib/utils/timer/objects.mk
@@ -7,10 +7,10 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += timer/aclint_mtimer.o
+libsbiutils-objs-$(CONFIG_TIMER_MTIMER) += timer/aclint_mtimer.o
 
-libsbiutils-objs-y += timer/fdt_timer.o
-libsbiutils-objs-y += timer/fdt_timer_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_TIMER) += timer/fdt_timer.o
+libsbiutils-objs-$(CONFIG_FDT_TIMER) += timer/fdt_timer_drivers.o
 
-carray-fdt_timer_drivers-y += fdt_timer_mtimer
-libsbiutils-objs-y += timer/fdt_timer_mtimer.o
+carray-fdt_timer_drivers-$(CONFIG_FDT_TIMER_MTIMER) += fdt_timer_mtimer
+libsbiutils-objs-$(CONFIG_FDT_TIMER_MTIMER) += timer/fdt_timer_mtimer.o
diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
index 797af74..cf60f99 100644
--- a/platform/fpga/ariane/Kconfig
+++ b/platform/fpga/ariane/Kconfig
@@ -3,4 +3,5 @@
 config PLATFORM_ARIANE_FPGA
 	bool
 	select SERIAL_UART8250
+	select TIMER_MTIMER
 	default y
diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
index 09b658a..b0e1321 100644
--- a/platform/fpga/openpiton/Kconfig
+++ b/platform/fpga/openpiton/Kconfig
@@ -3,4 +3,5 @@
 config PLATFORM_OPENPITON_FPGA
 	bool
 	select SERIAL_UART8250
+	select TIMER_MTIMER
 	default y
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index 9778665..9b96a4f 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -12,3 +12,5 @@ CONFIG_FDT_SERIAL_SIFIVE=y
 CONFIG_FDT_SERIAL_LITEX=y
 CONFIG_FDT_SERIAL_UART8250=y
 CONFIG_FDT_SERIAL_XILINX_UARTLITE=y
+CONFIG_FDT_TIMER=y
+CONFIG_FDT_TIMER_MTIMER=y
diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
index bc921c8..a0e6116 100644
--- a/platform/kendryte/k210/Kconfig
+++ b/platform/kendryte/k210/Kconfig
@@ -3,4 +3,5 @@
 config PLATFORM_KENDRYTE_K210
 	bool
 	select SERIAL_SIFIVE
+	select TIMER_MTIMER
 	default y
diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
index c4da266..b05c6a9 100644
--- a/platform/nuclei/ux600/Kconfig
+++ b/platform/nuclei/ux600/Kconfig
@@ -3,4 +3,5 @@
 config PLATFORM_NUCLEI_UX600
 	bool
 	select SERIAL_SIFIVE
+	select TIMER_MTIMER
 	default y
diff --git a/platform/template/Kconfig b/platform/template/Kconfig
index e01fb31..933b9e7 100644
--- a/platform/template/Kconfig
+++ b/platform/template/Kconfig
@@ -3,4 +3,5 @@
 config PLATFORM_TEMPLATE
 	bool
 	select SERIAL_UART8250
+	select TIMER_MTIMER
 	default y
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 05:42:03 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 18:12:03 +0530
Subject: [PATCH 07/14] lib: utils/ipi: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220718124210.2177576-1-apatel@ventanamicro.com>
References: <20220718124210.2177576-1-apatel@ventanamicro.com>
Message-ID: <20220718124210.2177576-8-apatel@ventanamicro.com>

We update ipi drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate ipi
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 include/sbi_utils/ipi/fdt_ipi.h    |  9 +++++++++
 lib/utils/Kconfig                  |  2 ++
 lib/utils/ipi/Kconfig              | 22 ++++++++++++++++++++++
 lib/utils/ipi/objects.mk           | 10 +++++-----
 platform/fpga/ariane/Kconfig       |  1 +
 platform/fpga/openpiton/Kconfig    |  1 +
 platform/generic/configs/defconfig |  2 ++
 platform/kendryte/k210/Kconfig     |  1 +
 platform/nuclei/ux600/Kconfig      |  1 +
 platform/template/Kconfig          |  1 +
 10 files changed, 45 insertions(+), 5 deletions(-)
 create mode 100644 lib/utils/ipi/Kconfig

diff --git a/include/sbi_utils/ipi/fdt_ipi.h b/include/sbi_utils/ipi/fdt_ipi.h
index 9337353..1dd9062 100644
--- a/include/sbi_utils/ipi/fdt_ipi.h
+++ b/include/sbi_utils/ipi/fdt_ipi.h
@@ -12,6 +12,8 @@
 
 #include <sbi/sbi_types.h>
 
+#ifdef CONFIG_FDT_IPI
+
 struct fdt_ipi {
 	const struct fdt_match *match_table;
 	int (*cold_init)(void *fdt, int nodeoff, const struct fdt_match *match);
@@ -23,4 +25,11 @@ void fdt_ipi_exit(void);
 
 int fdt_ipi_init(bool cold_boot);
 
+#else
+
+static inline void fdt_ipi_exit(void) { }
+static inline int fdt_ipi_init(bool cold_boot) { return 0; }
+
+#endif
+
 #endif
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index e169ed4..4daeb1d 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -2,6 +2,8 @@
 
 menu "Utils and Drivers Support"
 
+source "$OPENSBI_SRC_DIR/lib/utils/ipi/Kconfig"
+
 source "$OPENSBI_SRC_DIR/lib/utils/reset/Kconfig"
 
 source "$OPENSBI_SRC_DIR/lib/utils/serial/Kconfig"
diff --git a/lib/utils/ipi/Kconfig b/lib/utils/ipi/Kconfig
new file mode 100644
index 0000000..e8b1446
--- /dev/null
+++ b/lib/utils/ipi/Kconfig
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0
+
+menu "IPI Device Support"
+
+config FDT_IPI
+	bool "FDT based ipi drivers"
+	default n
+
+if FDT_IPI
+
+config FDT_IPI_MSWI
+	bool "ACLINT MSWI FDT driver"
+	select IPI_MSWI
+	default n
+
+endif
+
+config IPI_MSWI
+	bool "ACLINT MSWI support"
+	default n
+
+endmenu
diff --git a/lib/utils/ipi/objects.mk b/lib/utils/ipi/objects.mk
index 0b0bc2d..0600cac 100644
--- a/lib/utils/ipi/objects.mk
+++ b/lib/utils/ipi/objects.mk
@@ -7,10 +7,10 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += ipi/aclint_mswi.o
+libsbiutils-objs-$(CONFIG_IPI_MSWI) += ipi/aclint_mswi.o
 
-libsbiutils-objs-y += ipi/fdt_ipi.o
-libsbiutils-objs-y += ipi/fdt_ipi_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_IPI) += ipi/fdt_ipi.o
+libsbiutils-objs-$(CONFIG_FDT_IPI) += ipi/fdt_ipi_drivers.o
 
-carray-fdt_ipi_drivers-y += fdt_ipi_mswi
-libsbiutils-objs-y += ipi/fdt_ipi_mswi.o
+carray-fdt_ipi_drivers-$(CONFIG_FDT_IPI_MSWI) += fdt_ipi_mswi
+libsbiutils-objs-$(CONFIG_FDT_IPI_MSWI) += ipi/fdt_ipi_mswi.o
diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
index cf60f99..c9601f0 100644
--- a/platform/fpga/ariane/Kconfig
+++ b/platform/fpga/ariane/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_ARIANE_FPGA
 	bool
+	select IPI_MSWI
 	select SERIAL_UART8250
 	select TIMER_MTIMER
 	default y
diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
index b0e1321..aefd2e0 100644
--- a/platform/fpga/openpiton/Kconfig
+++ b/platform/fpga/openpiton/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_OPENPITON_FPGA
 	bool
+	select IPI_MSWI
 	select SERIAL_UART8250
 	select TIMER_MTIMER
 	default y
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index 9b96a4f..e218c32 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -1,3 +1,5 @@
+CONFIG_FDT_IPI=y
+CONFIG_FDT_IPI_MSWI=y
 CONFIG_FDT_RESET=y
 CONFIG_FDT_RESET_GPIO=y
 CONFIG_FDT_RESET_HTIF=y
diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
index a0e6116..da675dc 100644
--- a/platform/kendryte/k210/Kconfig
+++ b/platform/kendryte/k210/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_KENDRYTE_K210
 	bool
+	select IPI_MSWI
 	select SERIAL_SIFIVE
 	select TIMER_MTIMER
 	default y
diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
index b05c6a9..44b3425 100644
--- a/platform/nuclei/ux600/Kconfig
+++ b/platform/nuclei/ux600/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_NUCLEI_UX600
 	bool
+	select IPI_MSWI
 	select SERIAL_SIFIVE
 	select TIMER_MTIMER
 	default y
diff --git a/platform/template/Kconfig b/platform/template/Kconfig
index 933b9e7..2f97871 100644
--- a/platform/template/Kconfig
+++ b/platform/template/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_TEMPLATE
 	bool
+	select IPI_MSWI
 	select SERIAL_UART8250
 	select TIMER_MTIMER
 	default y
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 05:42:04 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 18:12:04 +0530
Subject: [PATCH 08/14] lib: utils/irqchip: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220718124210.2177576-1-apatel@ventanamicro.com>
References: <20220718124210.2177576-1-apatel@ventanamicro.com>
Message-ID: <20220718124210.2177576-9-apatel@ventanamicro.com>

We update irqchip drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate irqchip
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 include/sbi_utils/irqchip/fdt_irqchip.h | 10 +++++++
 include/sbi_utils/irqchip/imsic.h       | 10 +++++++
 lib/utils/Kconfig                       |  2 ++
 lib/utils/irqchip/Kconfig               | 40 +++++++++++++++++++++++++
 lib/utils/irqchip/objects.mk            | 22 +++++++-------
 platform/andes/ae350/Kconfig            |  1 +
 platform/fpga/ariane/Kconfig            |  1 +
 platform/fpga/openpiton/Kconfig         |  1 +
 platform/generic/configs/defconfig      |  4 +++
 platform/kendryte/k210/Kconfig          |  1 +
 platform/nuclei/ux600/Kconfig           |  1 +
 platform/template/Kconfig               |  1 +
 12 files changed, 83 insertions(+), 11 deletions(-)
 create mode 100644 lib/utils/irqchip/Kconfig

diff --git a/include/sbi_utils/irqchip/fdt_irqchip.h b/include/sbi_utils/irqchip/fdt_irqchip.h
index 13ef6f7..63e9fd9 100644
--- a/include/sbi_utils/irqchip/fdt_irqchip.h
+++ b/include/sbi_utils/irqchip/fdt_irqchip.h
@@ -12,6 +12,8 @@
 
 #include <sbi/sbi_types.h>
 
+#ifdef CONFIG_FDT_IRQCHIP
+
 struct fdt_irqchip {
 	const struct fdt_match *match_table;
 	int (*cold_init)(void *fdt, int nodeoff, const struct fdt_match *match);
@@ -23,4 +25,12 @@ void fdt_irqchip_exit(void);
 
 int fdt_irqchip_init(bool cold_boot);
 
+#else
+
+static inline void fdt_irqchip_exit(void) { }
+
+static inline int fdt_irqchip_init(bool cold_boot) { return 0; }
+
+#endif
+
 #endif
diff --git a/include/sbi_utils/irqchip/imsic.h b/include/sbi_utils/irqchip/imsic.h
index cffcb5a..e295771 100644
--- a/include/sbi_utils/irqchip/imsic.h
+++ b/include/sbi_utils/irqchip/imsic.h
@@ -33,6 +33,8 @@ struct imsic_data {
 	struct imsic_regs regs[IMSIC_MAX_REGS];
 };
 
+#ifdef CONFIG_IRQCHIP_IMSIC
+
 int imsic_map_hartid_to_data(u32 hartid, struct imsic_data *imsic, int file);
 
 struct imsic_data *imsic_get_data(u32 hartid);
@@ -47,4 +49,12 @@ int imsic_data_check(struct imsic_data *imsic);
 
 int imsic_cold_irqchip_init(struct imsic_data *imsic);
 
+#else
+
+static inline void imsic_local_irqchip_init(void) { }
+
+static inline int imsic_data_check(struct imsic_data *imsic) { return 0; }
+
+#endif
+
 #endif
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 4daeb1d..e384cb8 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -4,6 +4,8 @@ menu "Utils and Drivers Support"
 
 source "$OPENSBI_SRC_DIR/lib/utils/ipi/Kconfig"
 
+source "$OPENSBI_SRC_DIR/lib/utils/irqchip/Kconfig"
+
 source "$OPENSBI_SRC_DIR/lib/utils/reset/Kconfig"
 
 source "$OPENSBI_SRC_DIR/lib/utils/serial/Kconfig"
diff --git a/lib/utils/irqchip/Kconfig b/lib/utils/irqchip/Kconfig
new file mode 100644
index 0000000..cc07c8e
--- /dev/null
+++ b/lib/utils/irqchip/Kconfig
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: GPL-2.0
+
+menu "Interrupt Controller Support"
+
+config FDT_IRQCHIP
+	bool "FDT based interrupt controller drivers"
+	default n
+
+if FDT_IRQCHIP
+
+config FDT_IRQCHIP_APLIC
+	bool "Advanced Platform Level Interrupt Controller (APLIC) FDT driver"
+	select IRQCHIP_APLIC
+	default n
+
+config FDT_IRQCHIP_IMSIC
+	bool "Incoming Message Signalled Interrupt Controller (IMSIC) FDT driver"
+	select IRQCHIP_IMSIC
+	default n
+
+config FDT_IRQCHIP_PLIC
+	bool "Platform Level Interrupt Controller (PLIC) FDT driver"
+	select IRQCHIP_PLIC
+	default n
+
+endif
+
+config IRQCHIP_APLIC
+	bool "Advanced Platform Level Interrupt Controller (APLIC) support"
+	default n
+
+config IRQCHIP_IMSIC
+	bool "Incoming Message Signalled Interrupt Controller (IMSIC) support"
+	default n
+
+config IRQCHIP_PLIC
+	bool "Platform Level Interrupt Controller (PLIC) support"
+	default n
+
+endmenu
diff --git a/lib/utils/irqchip/objects.mk b/lib/utils/irqchip/objects.mk
index 7775bc4..e458891 100644
--- a/lib/utils/irqchip/objects.mk
+++ b/lib/utils/irqchip/objects.mk
@@ -7,18 +7,18 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += irqchip/fdt_irqchip.o
-libsbiutils-objs-y += irqchip/fdt_irqchip_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_IRQCHIP) += irqchip/fdt_irqchip.o
+libsbiutils-objs-$(CONFIG_FDT_IRQCHIP) += irqchip/fdt_irqchip_drivers.o
 
-carray-fdt_irqchip_drivers-y += fdt_irqchip_aplic
-libsbiutils-objs-y += irqchip/fdt_irqchip_aplic.o
+carray-fdt_irqchip_drivers-$(CONFIG_FDT_IRQCHIP_APLIC) += fdt_irqchip_aplic
+libsbiutils-objs-$(CONFIG_FDT_IRQCHIP_APLIC) += irqchip/fdt_irqchip_aplic.o
 
-carray-fdt_irqchip_drivers-y += fdt_irqchip_imsic
-libsbiutils-objs-y += irqchip/fdt_irqchip_imsic.o
+carray-fdt_irqchip_drivers-$(CONFIG_FDT_IRQCHIP_IMSIC) += fdt_irqchip_imsic
+libsbiutils-objs-$(CONFIG_FDT_IRQCHIP_IMSIC) += irqchip/fdt_irqchip_imsic.o
 
-carray-fdt_irqchip_drivers-y += fdt_irqchip_plic
-libsbiutils-objs-y += irqchip/fdt_irqchip_plic.o
+carray-fdt_irqchip_drivers-$(CONFIG_FDT_IRQCHIP_PLIC) += fdt_irqchip_plic
+libsbiutils-objs-$(CONFIG_FDT_IRQCHIP_PLIC) += irqchip/fdt_irqchip_plic.o
 
-libsbiutils-objs-y += irqchip/aplic.o
-libsbiutils-objs-y += irqchip/imsic.o
-libsbiutils-objs-y += irqchip/plic.o
+libsbiutils-objs-$(CONFIG_IRQCHIP_APLIC) += irqchip/aplic.o
+libsbiutils-objs-$(CONFIG_IRQCHIP_IMSIC) += irqchip/imsic.o
+libsbiutils-objs-$(CONFIG_IRQCHIP_PLIC) += irqchip/plic.o
diff --git a/platform/andes/ae350/Kconfig b/platform/andes/ae350/Kconfig
index 0048c1b..69cb65e 100644
--- a/platform/andes/ae350/Kconfig
+++ b/platform/andes/ae350/Kconfig
@@ -2,5 +2,6 @@
 
 config PLATFORM_ANDES_AE350
 	bool
+	select IRQCHIP_PLIC
 	select SERIAL_UART8250
 	default y
diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
index c9601f0..3303689 100644
--- a/platform/fpga/ariane/Kconfig
+++ b/platform/fpga/ariane/Kconfig
@@ -3,6 +3,7 @@
 config PLATFORM_ARIANE_FPGA
 	bool
 	select IPI_MSWI
+	select IRQCHIP_PLIC
 	select SERIAL_UART8250
 	select TIMER_MTIMER
 	default y
diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
index aefd2e0..5bf77ae 100644
--- a/platform/fpga/openpiton/Kconfig
+++ b/platform/fpga/openpiton/Kconfig
@@ -3,6 +3,7 @@
 config PLATFORM_OPENPITON_FPGA
 	bool
 	select IPI_MSWI
+	select IRQCHIP_PLIC
 	select SERIAL_UART8250
 	select TIMER_MTIMER
 	default y
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index e218c32..ad66a63 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -1,5 +1,9 @@
 CONFIG_FDT_IPI=y
 CONFIG_FDT_IPI_MSWI=y
+CONFIG_FDT_IRQCHIP=y
+CONFIG_FDT_IRQCHIP_APLIC=y
+CONFIG_FDT_IRQCHIP_IMSIC=y
+CONFIG_FDT_IRQCHIP_PLIC=y
 CONFIG_FDT_RESET=y
 CONFIG_FDT_RESET_GPIO=y
 CONFIG_FDT_RESET_HTIF=y
diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
index da675dc..80b5ae9 100644
--- a/platform/kendryte/k210/Kconfig
+++ b/platform/kendryte/k210/Kconfig
@@ -3,6 +3,7 @@
 config PLATFORM_KENDRYTE_K210
 	bool
 	select IPI_MSWI
+	select IRQCHIP_PLIC
 	select SERIAL_SIFIVE
 	select TIMER_MTIMER
 	default y
diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
index 44b3425..02766a6 100644
--- a/platform/nuclei/ux600/Kconfig
+++ b/platform/nuclei/ux600/Kconfig
@@ -3,6 +3,7 @@
 config PLATFORM_NUCLEI_UX600
 	bool
 	select IPI_MSWI
+	select IRQCHIP_PLIC
 	select SERIAL_SIFIVE
 	select TIMER_MTIMER
 	default y
diff --git a/platform/template/Kconfig b/platform/template/Kconfig
index 2f97871..5c59915 100644
--- a/platform/template/Kconfig
+++ b/platform/template/Kconfig
@@ -3,6 +3,7 @@
 config PLATFORM_TEMPLATE
 	bool
 	select IPI_MSWI
+	select IRQCHIP_PLIC
 	select SERIAL_UART8250
 	select TIMER_MTIMER
 	default y
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 05:42:05 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 18:12:05 +0530
Subject: [PATCH 09/14] lib: utils/i2c: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220718124210.2177576-1-apatel@ventanamicro.com>
References: <20220718124210.2177576-1-apatel@ventanamicro.com>
Message-ID: <20220718124210.2177576-10-apatel@ventanamicro.com>

We update i2c drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate i2c
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 lib/utils/Kconfig                  |  2 ++
 lib/utils/i2c/Kconfig              | 22 ++++++++++++++++++++++
 lib/utils/i2c/objects.mk           | 10 +++++-----
 platform/generic/configs/defconfig |  2 ++
 4 files changed, 31 insertions(+), 5 deletions(-)
 create mode 100644 lib/utils/i2c/Kconfig

diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index e384cb8..9477ec7 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -2,6 +2,8 @@
 
 menu "Utils and Drivers Support"
 
+source "$OPENSBI_SRC_DIR/lib/utils/i2c/Kconfig"
+
 source "$OPENSBI_SRC_DIR/lib/utils/ipi/Kconfig"
 
 source "$OPENSBI_SRC_DIR/lib/utils/irqchip/Kconfig"
diff --git a/lib/utils/i2c/Kconfig b/lib/utils/i2c/Kconfig
new file mode 100644
index 0000000..bb29cd8
--- /dev/null
+++ b/lib/utils/i2c/Kconfig
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0
+
+menu "I2C Support"
+
+config FDT_I2C
+	bool "FDT based I2C drivers"
+	select I2C
+	default n
+
+if FDT_I2C
+
+config FDT_I2C_SIFIVE
+	bool "SiFive I2C FDT driver"
+	default n
+
+endif
+
+config I2C
+	bool "I2C support"
+	default n
+
+endmenu
diff --git a/lib/utils/i2c/objects.mk b/lib/utils/i2c/objects.mk
index bb0d6d5..a0fbbb5 100644
--- a/lib/utils/i2c/objects.mk
+++ b/lib/utils/i2c/objects.mk
@@ -7,10 +7,10 @@
 #   Nikita Shubin <n.shubin at yadro.com>
 #
 
-libsbiutils-objs-y += i2c/i2c.o
+libsbiutils-objs-$(CONFIG_I2C) += i2c/i2c.o
 
-libsbiutils-objs-y += i2c/fdt_i2c.o
-libsbiutils-objs-y += i2c/fdt_i2c_adapter_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_I2C) += i2c/fdt_i2c.o
+libsbiutils-objs-$(CONFIG_FDT_I2C) += i2c/fdt_i2c_adapter_drivers.o
 
-carray-fdt_i2c_adapter_drivers-y += fdt_i2c_adapter_sifive
-libsbiutils-objs-y += i2c/fdt_i2c_sifive.o
+carray-fdt_i2c_adapter_drivers-$(CONFIG_FDT_I2C_SIFIVE) += fdt_i2c_adapter_sifive
+libsbiutils-objs-$(CONFIG_FDT_I2C_SIFIVE) += i2c/fdt_i2c_sifive.o
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index ad66a63..c9c221e 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -1,3 +1,5 @@
+CONFIG_FDT_I2C=y
+CONFIG_FDT_I2C_SIFIVE=y
 CONFIG_FDT_IPI=y
 CONFIG_FDT_IPI_MSWI=y
 CONFIG_FDT_IRQCHIP=y
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 05:42:06 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 18:12:06 +0530
Subject: [PATCH 10/14] lib: utils/gpio: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220718124210.2177576-1-apatel@ventanamicro.com>
References: <20220718124210.2177576-1-apatel@ventanamicro.com>
Message-ID: <20220718124210.2177576-11-apatel@ventanamicro.com>

We update gpio drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate gpio
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 lib/utils/Kconfig                  |  2 ++
 lib/utils/gpio/Kconfig             | 22 ++++++++++++++++++++++
 lib/utils/gpio/objects.mk          | 10 +++++-----
 lib/utils/reset/Kconfig            |  1 +
 platform/generic/configs/defconfig |  2 ++
 5 files changed, 32 insertions(+), 5 deletions(-)
 create mode 100644 lib/utils/gpio/Kconfig

diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 9477ec7..57a6509 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -2,6 +2,8 @@
 
 menu "Utils and Drivers Support"
 
+source "$OPENSBI_SRC_DIR/lib/utils/gpio/Kconfig"
+
 source "$OPENSBI_SRC_DIR/lib/utils/i2c/Kconfig"
 
 source "$OPENSBI_SRC_DIR/lib/utils/ipi/Kconfig"
diff --git a/lib/utils/gpio/Kconfig b/lib/utils/gpio/Kconfig
new file mode 100644
index 0000000..73673a3
--- /dev/null
+++ b/lib/utils/gpio/Kconfig
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0
+
+menu "GPIO Support"
+
+config FDT_GPIO
+	bool "FDT based GPIO drivers"
+	select GPIO
+	default n
+
+if FDT_GPIO
+
+config FDT_GPIO_SIFIVE
+	bool "SiFive GPIO FDT driver"
+	default n
+
+endif
+
+config GPIO
+	bool "GPIO support"
+	default n
+
+endmenu
diff --git a/lib/utils/gpio/objects.mk b/lib/utils/gpio/objects.mk
index a5e131b..eedd699 100644
--- a/lib/utils/gpio/objects.mk
+++ b/lib/utils/gpio/objects.mk
@@ -7,10 +7,10 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += gpio/fdt_gpio.o
-libsbiutils-objs-y += gpio/fdt_gpio_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_GPIO) += gpio/fdt_gpio.o
+libsbiutils-objs-$(CONFIG_FDT_GPIO) += gpio/fdt_gpio_drivers.o
 
-carray-fdt_gpio_drivers-y += fdt_gpio_sifive
-libsbiutils-objs-y += gpio/fdt_gpio_sifive.o
+carray-fdt_gpio_drivers-$(CONFIG_FDT_GPIO_SIFIVE) += fdt_gpio_sifive
+libsbiutils-objs-$(CONFIG_FDT_GPIO_SIFIVE) += gpio/fdt_gpio_sifive.o
 
-libsbiutils-objs-y += gpio/gpio.o
+libsbiutils-objs-$(CONFIG_GPIO) += gpio/gpio.o
diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig
index 4f04d8c..ead9e2a 100644
--- a/lib/utils/reset/Kconfig
+++ b/lib/utils/reset/Kconfig
@@ -10,6 +10,7 @@ if FDT_RESET
 
 config FDT_RESET_GPIO
 	bool "GPIO FDT reset driver"
+	depends on FDT_GPIO
 	default n
 
 config FDT_RESET_HTIF
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index c9c221e..d56cef2 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -1,3 +1,5 @@
+CONFIG_FDT_GPIO=y
+CONFIG_FDT_GPIO_SIFIVE=y
 CONFIG_FDT_I2C=y
 CONFIG_FDT_I2C_SIFIVE=y
 CONFIG_FDT_IPI=y
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 05:42:07 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 18:12:07 +0530
Subject: [PATCH 11/14] lib: utils/fdt: Use kconfig for enabling/disabling
In-Reply-To: <20220718124210.2177576-1-apatel@ventanamicro.com>
References: <20220718124210.2177576-1-apatel@ventanamicro.com>
Message-ID: <20220718124210.2177576-12-apatel@ventanamicro.com>

We update FDT support makefile to use kconfig for enabling/disabling.
To avoid compilation errors, we also enable FDT for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 include/sbi_utils/fdt/fdt_domain.h             |  9 +++++++++
 include/sbi_utils/fdt/fdt_pmu.h                | 10 ++++++++++
 lib/utils/Kconfig                              |  4 ++++
 lib/utils/fdt/Kconfig                          | 18 ++++++++++++++++++
 lib/utils/fdt/objects.mk                       |  8 ++++----
 lib/utils/gpio/Kconfig                         |  1 +
 lib/utils/i2c/Kconfig                          |  1 +
 lib/utils/ipi/Kconfig                          |  1 +
 lib/utils/irqchip/Kconfig                      |  1 +
 {platform/generic => lib/utils/libfdt}/Kconfig |  4 ++--
 lib/utils/libfdt/objects.mk                    |  4 ++--
 lib/utils/reset/Kconfig                        |  1 +
 lib/utils/serial/Kconfig                       |  1 +
 lib/utils/timer/Kconfig                        |  1 +
 platform/andes/ae350/Kconfig                   |  1 +
 platform/fpga/ariane/Kconfig                   |  1 +
 platform/fpga/openpiton/Kconfig                |  1 +
 platform/generic/Kconfig                       |  3 +++
 platform/kendryte/k210/Kconfig                 |  1 +
 platform/nuclei/ux600/Kconfig                  |  1 +
 platform/nuclei/ux600/platform.c               |  1 -
 21 files changed, 64 insertions(+), 9 deletions(-)
 create mode 100644 lib/utils/fdt/Kconfig
 copy {platform/generic => lib/utils/libfdt}/Kconfig (54%)

diff --git a/include/sbi_utils/fdt/fdt_domain.h b/include/sbi_utils/fdt/fdt_domain.h
index 5448eb4..ee09d1b 100644
--- a/include/sbi_utils/fdt/fdt_domain.h
+++ b/include/sbi_utils/fdt/fdt_domain.h
@@ -13,6 +13,8 @@
 
 #include <sbi/sbi_types.h>
 
+#ifdef CONFIG_FDT_DOMAIN
+
 struct sbi_domain;
 
 /**
@@ -70,4 +72,11 @@ void fdt_domain_fixup(void *fdt);
  */
 int fdt_domains_populate(void *fdt);
 
+#else
+
+static inline void fdt_domain_fixup(void *fdt) { }
+static inline int fdt_domains_populate(void *fdt) { return 0; }
+
+#endif
+
 #endif /* __FDT_DOMAIN_H__ */
diff --git a/include/sbi_utils/fdt/fdt_pmu.h b/include/sbi_utils/fdt/fdt_pmu.h
index 2fa01ed..c65cad7 100644
--- a/include/sbi_utils/fdt/fdt_pmu.h
+++ b/include/sbi_utils/fdt/fdt_pmu.h
@@ -13,6 +13,8 @@
 
 #include <sbi/sbi_types.h>
 
+#ifdef CONFIG_FDT_PMU
+
 /**
  * Fix up the PMU node in the device tree
  *
@@ -43,4 +45,12 @@ int fdt_pmu_setup(void *fdt);
  */
 uint64_t fdt_pmu_get_select_value(uint32_t event_idx);
 
+#else
+
+static inline void fdt_pmu_fixup(void *fdt) { }
+static inline int fdt_pmu_setup(void *fdt) { return 0; }
+static inline uint64_t fdt_pmu_get_select_value(uint32_t event_idx) { return 0; }
+
+#endif
+
 #endif
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 57a6509..97f97b5 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -2,6 +2,8 @@
 
 menu "Utils and Drivers Support"
 
+source "$OPENSBI_SRC_DIR/lib/utils/fdt/Kconfig"
+
 source "$OPENSBI_SRC_DIR/lib/utils/gpio/Kconfig"
 
 source "$OPENSBI_SRC_DIR/lib/utils/i2c/Kconfig"
@@ -10,6 +12,8 @@ source "$OPENSBI_SRC_DIR/lib/utils/ipi/Kconfig"
 
 source "$OPENSBI_SRC_DIR/lib/utils/irqchip/Kconfig"
 
+source "$OPENSBI_SRC_DIR/lib/utils/libfdt/Kconfig"
+
 source "$OPENSBI_SRC_DIR/lib/utils/reset/Kconfig"
 
 source "$OPENSBI_SRC_DIR/lib/utils/serial/Kconfig"
diff --git a/lib/utils/fdt/Kconfig b/lib/utils/fdt/Kconfig
new file mode 100644
index 0000000..a8022a0
--- /dev/null
+++ b/lib/utils/fdt/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0
+
+menuconfig FDT
+	bool "Flattened Device Tree (FDT) support"
+	select LIBFDT
+	default n
+
+if FDT
+
+config FDT_DOMAIN
+	bool "FDT domain support"
+	default n
+
+config FDT_PMU
+	bool "FDT performance monitoring unit (PMU) support"
+	default n
+
+endif
diff --git a/lib/utils/fdt/objects.mk b/lib/utils/fdt/objects.mk
index 03800f9..5cede81 100644
--- a/lib/utils/fdt/objects.mk
+++ b/lib/utils/fdt/objects.mk
@@ -4,7 +4,7 @@
 # Copyright (C) 2020 Bin Meng <bmeng.cn at gmail.com>
 #
 
-libsbiutils-objs-y += fdt/fdt_domain.o
-libsbiutils-objs-y += fdt/fdt_pmu.o
-libsbiutils-objs-y += fdt/fdt_helper.o
-libsbiutils-objs-y += fdt/fdt_fixup.o
+libsbiutils-objs-$(CONFIG_FDT_DOMAIN) += fdt/fdt_domain.o
+libsbiutils-objs-$(CONFIG_FDT_PMU) += fdt/fdt_pmu.o
+libsbiutils-objs-$(CONFIG_FDT) += fdt/fdt_helper.o
+libsbiutils-objs-$(CONFIG_FDT) += fdt/fdt_fixup.o
diff --git a/lib/utils/gpio/Kconfig b/lib/utils/gpio/Kconfig
index 73673a3..ebf1b2c 100644
--- a/lib/utils/gpio/Kconfig
+++ b/lib/utils/gpio/Kconfig
@@ -4,6 +4,7 @@ menu "GPIO Support"
 
 config FDT_GPIO
 	bool "FDT based GPIO drivers"
+	depends on FDT
 	select GPIO
 	default n
 
diff --git a/lib/utils/i2c/Kconfig b/lib/utils/i2c/Kconfig
index bb29cd8..0313588 100644
--- a/lib/utils/i2c/Kconfig
+++ b/lib/utils/i2c/Kconfig
@@ -4,6 +4,7 @@ menu "I2C Support"
 
 config FDT_I2C
 	bool "FDT based I2C drivers"
+	depends on FDT
 	select I2C
 	default n
 
diff --git a/lib/utils/ipi/Kconfig b/lib/utils/ipi/Kconfig
index e8b1446..2a84ecd 100644
--- a/lib/utils/ipi/Kconfig
+++ b/lib/utils/ipi/Kconfig
@@ -4,6 +4,7 @@ menu "IPI Device Support"
 
 config FDT_IPI
 	bool "FDT based ipi drivers"
+	depends on FDT
 	default n
 
 if FDT_IPI
diff --git a/lib/utils/irqchip/Kconfig b/lib/utils/irqchip/Kconfig
index cc07c8e..9090b18 100644
--- a/lib/utils/irqchip/Kconfig
+++ b/lib/utils/irqchip/Kconfig
@@ -4,6 +4,7 @@ menu "Interrupt Controller Support"
 
 config FDT_IRQCHIP
 	bool "FDT based interrupt controller drivers"
+	depends on FDT
 	default n
 
 if FDT_IRQCHIP
diff --git a/platform/generic/Kconfig b/lib/utils/libfdt/Kconfig
similarity index 54%
copy from platform/generic/Kconfig
copy to lib/utils/libfdt/Kconfig
index a811a05..096345b 100644
--- a/platform/generic/Kconfig
+++ b/lib/utils/libfdt/Kconfig
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
 
-config PLATFORM_GENERIC
+config LIBFDT
 	bool
-	default y
+	default n
diff --git a/lib/utils/libfdt/objects.mk b/lib/utils/libfdt/objects.mk
index 8c060df..a5b4001 100644
--- a/lib/utils/libfdt/objects.mk
+++ b/lib/utils/libfdt/objects.mk
@@ -12,5 +12,5 @@ libfdt_files = fdt.o fdt_addresses.o fdt_check.o fdt_empty_tree.o fdt_ro.o fdt_r
 $(foreach file, $(libfdt_files), \
         $(eval CFLAGS_$(file) = -I$(src)/../../utils/libfdt))
 
-libsbiutils-objs-y += $(addprefix libfdt/,$(libfdt_files))
-libsbiutils-genflags-y  += -I$(libsbiutils_dir)/libfdt/
+libsbiutils-objs-$(CONFIG_LIBFDT) += $(addprefix libfdt/,$(libfdt_files))
+libsbiutils-genflags-$(CONFIG_LIBFDT)  += -I$(libsbiutils_dir)/libfdt/
diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig
index ead9e2a..bcfb419 100644
--- a/lib/utils/reset/Kconfig
+++ b/lib/utils/reset/Kconfig
@@ -4,6 +4,7 @@ menu "System Reset Support"
 
 config FDT_RESET
 	bool "FDT based reset drivers"
+	depends on FDT
 	default n
 
 if FDT_RESET
diff --git a/lib/utils/serial/Kconfig b/lib/utils/serial/Kconfig
index a425b13..34d72ff 100644
--- a/lib/utils/serial/Kconfig
+++ b/lib/utils/serial/Kconfig
@@ -4,6 +4,7 @@ menu "Serial Device Support"
 
 config FDT_SERIAL
 	bool "FDT based serial drivers"
+	depends on FDT
 	default n
 
 if FDT_SERIAL
diff --git a/lib/utils/timer/Kconfig b/lib/utils/timer/Kconfig
index 3b1221a..5266f1d 100644
--- a/lib/utils/timer/Kconfig
+++ b/lib/utils/timer/Kconfig
@@ -4,6 +4,7 @@ menu "Timer Device Support"
 
 config FDT_TIMER
 	bool "FDT based timer drivers"
+	depends on FDT
 	default n
 
 if FDT_TIMER
diff --git a/platform/andes/ae350/Kconfig b/platform/andes/ae350/Kconfig
index 69cb65e..a9bbf46 100644
--- a/platform/andes/ae350/Kconfig
+++ b/platform/andes/ae350/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_ANDES_AE350
 	bool
+	select FDT
 	select IRQCHIP_PLIC
 	select SERIAL_UART8250
 	default y
diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
index 3303689..40d6ad6 100644
--- a/platform/fpga/ariane/Kconfig
+++ b/platform/fpga/ariane/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_ARIANE_FPGA
 	bool
+	select FDT
 	select IPI_MSWI
 	select IRQCHIP_PLIC
 	select SERIAL_UART8250
diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
index 5bf77ae..08087fb 100644
--- a/platform/fpga/openpiton/Kconfig
+++ b/platform/fpga/openpiton/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_OPENPITON_FPGA
 	bool
+	select FDT
 	select IPI_MSWI
 	select IRQCHIP_PLIC
 	select SERIAL_UART8250
diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
index a811a05..21ebd6a 100644
--- a/platform/generic/Kconfig
+++ b/platform/generic/Kconfig
@@ -2,4 +2,7 @@
 
 config PLATFORM_GENERIC
 	bool
+	select FDT
+	select FDT_DOMAIN
+	select FDT_PMU
 	default y
diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
index 80b5ae9..58b893e 100644
--- a/platform/kendryte/k210/Kconfig
+++ b/platform/kendryte/k210/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_KENDRYTE_K210
 	bool
+	select FDT
 	select IPI_MSWI
 	select IRQCHIP_PLIC
 	select SERIAL_SIFIVE
diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
index 02766a6..11fabb1 100644
--- a/platform/nuclei/ux600/Kconfig
+++ b/platform/nuclei/ux600/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_NUCLEI_UX600
 	bool
+	select FDT
 	select IPI_MSWI
 	select IRQCHIP_PLIC
 	select SERIAL_SIFIVE
diff --git a/platform/nuclei/ux600/platform.c b/platform/nuclei/ux600/platform.c
index 5498a1f..2b02734 100644
--- a/platform/nuclei/ux600/platform.c
+++ b/platform/nuclei/ux600/platform.c
@@ -8,7 +8,6 @@
  *   hqfang <578567190 at qq.com>
  */
 
-#include <libfdt.h>
 #include <sbi/riscv_asm.h>
 #include <sbi/riscv_io.h>
 #include <sbi/riscv_encoding.h>
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 05:42:08 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 18:12:08 +0530
Subject: [PATCH 12/14] platform: generic: Use kconfig for enabling/disabling overrides
In-Reply-To: <20220718124210.2177576-1-apatel@ventanamicro.com>
References: <20220718124210.2177576-1-apatel@ventanamicro.com>
Message-ID: <20220718124210.2177576-13-apatel@ventanamicro.com>

We update generic platform to use kconfig for enabling/disabling
platform overrides. We also enable all platform overrides in generic
platform defconfig.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 platform/generic/Kconfig              | 18 ++++++++++++++++++
 platform/generic/allwinner/objects.mk |  4 ++--
 platform/generic/configs/defconfig    |  3 +++
 platform/generic/sifive/objects.mk    |  8 ++++----
 4 files changed, 27 insertions(+), 6 deletions(-)

diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
index 21ebd6a..8b00772 100644
--- a/platform/generic/Kconfig
+++ b/platform/generic/Kconfig
@@ -6,3 +6,21 @@ config PLATFORM_GENERIC
 	select FDT_DOMAIN
 	select FDT_PMU
 	default y
+
+if PLATFORM_GENERIC
+
+config PLATFORM_ALLWINNER_D1
+	bool "Allwinner D1 support"
+	depends on FDT_IRQCHIP_PLIC
+	default n
+
+config PLATFORM_SIFIVE_FU540
+	bool "SiFive FU540 support"
+	default n
+
+config PLATFORM_SIFIVE_FU740
+	bool "SiFive FU740 support"
+	depends on FDT_RESET && FDT_I2C
+	default n
+
+endif
diff --git a/platform/generic/allwinner/objects.mk b/platform/generic/allwinner/objects.mk
index 9e36ab6..447cdf1 100644
--- a/platform/generic/allwinner/objects.mk
+++ b/platform/generic/allwinner/objects.mk
@@ -2,5 +2,5 @@
 # SPDX-License-Identifier: BSD-2-Clause
 #
 
-carray-platform_override_modules-y += sun20i_d1
-platform-objs-y += allwinner/sun20i-d1.o
+carray-platform_override_modules-$(CONFIG_PLATFORM_ALLWINNER_D1) += sun20i_d1
+platform-objs-$(CONFIG_PLATFORM_ALLWINNER_D1) += allwinner/sun20i-d1.o
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index d56cef2..2a75394 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -1,3 +1,6 @@
+CONFIG_PLATFORM_ALLWINNER_D1=y
+CONFIG_PLATFORM_SIFIVE_FU540=y
+CONFIG_PLATFORM_SIFIVE_FU740=y
 CONFIG_FDT_GPIO=y
 CONFIG_FDT_GPIO_SIFIVE=y
 CONFIG_FDT_I2C=y
diff --git a/platform/generic/sifive/objects.mk b/platform/generic/sifive/objects.mk
index c17e2df..d75e444 100644
--- a/platform/generic/sifive/objects.mk
+++ b/platform/generic/sifive/objects.mk
@@ -2,8 +2,8 @@
 # SPDX-License-Identifier: BSD-2-Clause
 #
 
-carray-platform_override_modules-y += sifive_fu540
-platform-objs-y += sifive/fu540.o
+carray-platform_override_modules-$(CONFIG_PLATFORM_SIFIVE_FU540) += sifive_fu540
+platform-objs-$(CONFIG_PLATFORM_SIFIVE_FU540) += sifive/fu540.o
 
-carray-platform_override_modules-y += sifive_fu740
-platform-objs-y += sifive/fu740.o
+carray-platform_override_modules-$(CONFIG_PLATFORM_SIFIVE_FU740) += sifive_fu740
+platform-objs-$(CONFIG_PLATFORM_SIFIVE_FU740) += sifive/fu740.o
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 05:42:10 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 18:12:10 +0530
Subject: [PATCH 14/14] docs: Update documentation for kconfig support
In-Reply-To: <20220718124210.2177576-1-apatel@ventanamicro.com>
References: <20220718124210.2177576-1-apatel@ventanamicro.com>
Message-ID: <20220718124210.2177576-15-apatel@ventanamicro.com>

We update all documentation files to:
1) Remove references to platform specific config.mk file since it is
   has been removed.
2) Add details about platform specific configs/defconfig and Kconfig
   files mandatory for each platform.
3) Add required packages in top-level README.md

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 README.md                   | 25 +++++++++++++++++++++++--
 docs/firmware/fw_dynamic.md |  2 +-
 docs/firmware/fw_jump.md    |  4 ++--
 docs/firmware/fw_payload.md |  4 ++--
 docs/platform/platform.md   |  4 ++--
 docs/platform_guide.md      |  9 +++++----
 6 files changed, 35 insertions(+), 13 deletions(-)

diff --git a/README.md b/README.md
index 54fabe4..cd852cb 100644
--- a/README.md
+++ b/README.md
@@ -92,8 +92,8 @@ N.B. Any S-mode boot loader (i.e. U-Boot) doesn't need to support HSM extension,
 as it doesn't need to boot all the harts. The operating system should be
 capable enough to bring up all other non-booting harts using HSM extension.
 
-Required Toolchain
-------------------
+Required Toolchain and Packages
+-------------------------------
 
 OpenSBI can be compiled natively or cross-compiled on a x86 host. For
 cross-compilation, you can build your own toolchain, download a prebuilt one
@@ -115,6 +115,14 @@ triple is used (e.g. *-target riscv64-unknown-elf*).
 Please note that only a 64-bit version of the toolchain is available in
 the Bootlin toolchain repository for now.
 
+In addition to a toolchain, OpenSBI also requires following packages on
+the host:
+
+1. device-tree-compiler: The device tree compiler for compiling device
+   tree sources (DTS files).
+2. kconfig-frontends: A standalone implementation of the Linux Kconfig
+   parser and frontends for OpenSBI platform configuration management.
+
 Building and Installing the OpenSBI Platform-Independent Library
 ----------------------------------------------------------------
 
@@ -196,6 +204,19 @@ top-level make command line. These options, such as *PLATFORM_<xyz>* or
 *docs/platform/<platform_name>.md* files and
 *docs/firmware/<firmware_name>.md* files.
 
+All OpenSBI platforms support Kconfig style build-time configuration. Users
+can change the build-time configuration of a platform using a graphical
+interface as follows:
+```
+make PLATFORM=<platform_subdir> menuconfig
+```
+
+Alternately, an OpenSBI platform can have multiple default configurations
+and users can select a custom default configuration as follows:
+```
+make PLATFORM=<platform_subdir> PLATFORM_DEFCONFIG=<platform_custom_defconfig>
+```
+
 Building 32-bit / 64-bit OpenSBI Images
 ---------------------------------------
 By default, building OpenSBI generates 32-bit or 64-bit images based on the
diff --git a/docs/firmware/fw_dynamic.md b/docs/firmware/fw_dynamic.md
index 7b9b192..e251488 100644
--- a/docs/firmware/fw_dynamic.md
+++ b/docs/firmware/fw_dynamic.md
@@ -20,7 +20,7 @@ the booting stage binary to follow OpenSBI firmware.
 A platform can enable *FW_DYNAMIC* firmware using any of the following methods.
 
 1. Specifying `FW_DYNAMIC=y` on the top level `make` command line.
-2. Specifying `FW_DYNAMIC=y` in the target platform *config.mk* configuration
+2. Specifying `FW_DYNAMIC=y` in the target platform *objects.mk* configuration
 file.
 
 The compiled *FW_DYNAMIC* firmware ELF file is named *fw_dynamic.elf*. It's
diff --git a/docs/firmware/fw_jump.md b/docs/firmware/fw_jump.md
index eea3013..35a4301 100644
--- a/docs/firmware/fw_jump.md
+++ b/docs/firmware/fw_jump.md
@@ -15,7 +15,7 @@ and the booting stage binary to follow the OpenSBI firmware.
 A platform *FW_JUMP* firmware can be enabled by any of the following methods:
 
 1. Specifying `FW_JUMP=y` on the top level `make` command line.
-2. Specifying `FW_JUMP=y` in the target platform *config.mk* configuration file.
+2. Specifying `FW_JUMP=y` in the target platform *objects.mk* configuration file.
 
 The compiled *FW_JUMP* firmware ELF file is named *fw_jump.elf*. Its expanded
 image file is *fw_jump.bin*. Both files are created in the platform-specific
@@ -26,7 +26,7 @@ build directory under the *build/platform/<platform_subdir>/firmware* directory.
 
 To operate correctly, a *FW_JUMP* firmware requires some configuration
 parameters to be defined using either the top level `make` command line or the
-target platform *config.mk* configuration file. The possible parameters are as
+target platform *objects.mk* configuration file. The possible parameters are as
 follows:
 
 * **FW_JUMP_ADDR** - Address of the entry point of the booting stage to be
diff --git a/docs/firmware/fw_payload.md b/docs/firmware/fw_payload.md
index 0947448..3bb918f 100644
--- a/docs/firmware/fw_payload.md
+++ b/docs/firmware/fw_payload.md
@@ -20,7 +20,7 @@ Enabling *FW_PAYLOAD* compilation
 The *FW_PAYLOAD* firmware can be enabled by any of the following methods:
 
 1. Specifying `FW_PAYLOAD=y` on the top level `make` command line.
-2. Specifying `FW_PAYLOAD=y` in the target platform *config.mk* configuration
+2. Specifying `FW_PAYLOAD=y` in the target platform *objects.mk* configuration
    file.
 
 The compiled *FW_PAYLOAD* firmware ELF file is named *fw_jump.elf*. Its
@@ -33,7 +33,7 @@ Configuration Options
 
 A *FW_PAYLOAD* firmware is built according to configuration parameters and
 options. These configuration parameters can be defined using either the top
-level `make` command line or the target platform *config.mk* configuration
+level `make` command line or the target platform *objects.mk* configuration
 file. The parameters currently defined are as follows:
 
 * **FW_PAYLOAD_OFFSET** - Offset from *FW_TEXT_BASE* where the payload binary
diff --git a/docs/platform/platform.md b/docs/platform/platform.md
index cb4bec6..b726e1d 100644
--- a/docs/platform/platform.md
+++ b/docs/platform/platform.md
@@ -42,8 +42,8 @@ OpenSBI currently supports the following virtual and hardware platforms:
 The code for these supported platforms can be used as example to implement
 support for other platforms. The *platform/template* directory also provides
 template files for implementing support for a new platform. The *object.mk*,
-*config.mk* and *platform.c* template files provides enough comments to
-facilitate the implementation.
+*Kconfig*, *configs/defconfig* and *platform.c* template files provides enough
+comments to facilitate the implementation.
 
 [generic.md]: generic.md
 [qemu_virt.md]: qemu_virt.md
diff --git a/docs/platform_guide.md b/docs/platform_guide.md
index b6c2c2e..8ac5aa0 100644
--- a/docs/platform_guide.md
+++ b/docs/platform_guide.md
@@ -28,11 +28,12 @@ Adding support for a new platform
 Support for a new platform named *&lt;xyz&gt;* can be added as follows:
 
 1. Create a directory named *&lt;xyz&gt;* under the *platform/* directory.
-2. Create a platform configuration file named *config.mk* under the
-   *platform/&lt;xyz&gt;/* directory. This configuration file will provide
+2. Create a platform configuration files named *Kconfig* and *configs/defconfig*
+   under the *platform/&lt;xyz&gt;/* directory. This configuration files will
+   provide the build time configuration for the sources to be compiled.
+3. Create a *platform/&lt;xyz&gt;/objects.mk* file for listing the platform
+   object files to be compiled. This file also provides platform-specific
    compiler flags, and select firmware options.
-3. Create a *platform/&lt;xyz&gt;/objects.mk* file for listing the
-   platform-specific object files to be compiled.
 4. Create a *platform/&lt;xyz&gt;/platform.c* file providing a
    *struct sbi_platform* instance.
 
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 05:42:09 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 18:12:09 +0530
Subject: [PATCH 13/14] platform: Remove redundant config.mk from all platforms
In-Reply-To: <20220718124210.2177576-1-apatel@ventanamicro.com>
References: <20220718124210.2177576-1-apatel@ventanamicro.com>
Message-ID: <20220718124210.2177576-14-apatel@ventanamicro.com>

The options defined in config.mk can be specified in objects.mk of each
platform so let us remove config.mk from all platforms.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 Makefile                           |  4 +-
 platform/andes/ae350/config.mk     | 36 --------------
 platform/andes/ae350/objects.mk    | 29 +++++++++++
 platform/fpga/ariane/config.mk     | 36 --------------
 platform/fpga/ariane/objects.mk    | 35 ++++++++++++++
 platform/fpga/openpiton/config.mk  | 35 --------------
 platform/fpga/openpiton/objects.mk | 35 ++++++++++++++
 platform/generic/config.mk         | 40 ----------------
 platform/generic/objects.mk        | 33 +++++++++++++
 platform/kendryte/k210/config.mk   | 19 --------
 platform/kendryte/k210/objects.mk  | 12 +++++
 platform/nuclei/ux600/config.mk    | 30 ------------
 platform/nuclei/ux600/objects.mk   | 22 +++++++++
 platform/template/config.mk        | 77 ------------------------------
 platform/template/objects.mk       | 72 ++++++++++++++++++++++++++++
 platform/template/platform.c       |  2 +-
 16 files changed, 240 insertions(+), 277 deletions(-)
 delete mode 100644 platform/andes/ae350/config.mk
 delete mode 100644 platform/fpga/ariane/config.mk
 delete mode 100644 platform/fpga/openpiton/config.mk
 delete mode 100644 platform/generic/config.mk
 delete mode 100644 platform/kendryte/k210/config.mk
 delete mode 100644 platform/nuclei/ux600/config.mk
 delete mode 100644 platform/template/config.mk

diff --git a/Makefile b/Makefile
index 5fc67db..a6475a7 100644
--- a/Makefile
+++ b/Makefile
@@ -203,7 +203,7 @@ firmware-object-mks=$(shell if [ -d $(firmware_dir) ]; then find $(firmware_dir)
 .PHONY: all
 all:
 
-# Include platform specific .config and config.mk
+# Include platform specific .config
 ifdef PLATFORM
 .PHONY: menuconfig
 menuconfig: $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
@@ -226,8 +226,6 @@ $(KCONFIG_CONFIG): $(platform_src_dir)/configs/$(PLATFORM_DEFCONFIG) $(platform_
 $(KCONFIG_AUTOCONF_CMD): $(KCONFIG_CONFIG)
 include $(KCONFIG_CONFIG)
 include $(KCONFIG_AUTOCONF_CMD)
-
-include $(platform_src_dir)/config.mk
 endif
 
 # Include all object.mk files
diff --git a/platform/andes/ae350/config.mk b/platform/andes/ae350/config.mk
deleted file mode 100644
index f555ef5..0000000
--- a/platform/andes/ae350/config.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2019 Andes Technology Corporation
-#
-# Authors:
-#   Zong Li <zong at andestech.com>
-#   Nylon Chen <nylon7 at andestech.com>
-
-# Compiler flags
-platform-cppflags-y =
-platform-cflags-y =
-platform-asflags-y =
-platform-ldflags-y =
-
-# Blobs to build
-FW_TEXT_START=0x00000000
-
-FW_DYNAMIC=y
-
-FW_JUMP=y
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-  FW_JUMP_ADDR=0x400000
-else
-  FW_JUMP_ADDR=0x200000
-endif
-FW_JUMP_FDT_ADDR=0x2000000
-
-FW_PAYLOAD=y
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-  FW_PAYLOAD_OFFSET=0x400000
-else
-  FW_PAYLOAD_OFFSET=0x200000
-endif
-
-FW_PAYLOAD_FDT_ADDR=0x2000000
diff --git a/platform/andes/ae350/objects.mk b/platform/andes/ae350/objects.mk
index 5369677..80f0737 100644
--- a/platform/andes/ae350/objects.mk
+++ b/platform/andes/ae350/objects.mk
@@ -8,4 +8,33 @@
 #   Nylon Chen <nylon7 at andestech.com>
 #
 
+# Compiler flags
+platform-cppflags-y =
+platform-cflags-y =
+platform-asflags-y =
+platform-ldflags-y =
+
+# Objects to build
 platform-objs-y += cache.o platform.o plicsw.o plmt.o
+
+# Blobs to build
+FW_TEXT_START=0x00000000
+
+FW_DYNAMIC=y
+
+FW_JUMP=y
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+  FW_JUMP_ADDR=0x400000
+else
+  FW_JUMP_ADDR=0x200000
+endif
+FW_JUMP_FDT_ADDR=0x2000000
+
+FW_PAYLOAD=y
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+  FW_PAYLOAD_OFFSET=0x400000
+else
+  FW_PAYLOAD_OFFSET=0x200000
+endif
+
+FW_PAYLOAD_FDT_ADDR=0x2000000
diff --git a/platform/fpga/ariane/config.mk b/platform/fpga/ariane/config.mk
deleted file mode 100644
index 3556461..0000000
--- a/platform/fpga/ariane/config.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (C) 2019 FORTH-ICS/CARV
-#		Panagiotis Peristerakis <perister at ics.forth.gr>
-#
-
-#for more infos, check out /platform/template/config.mk
-
-PLATFORM_RISCV_XLEN = 64
-
-# Blobs to build
-FW_TEXT_START=0x80000000
-FW_JUMP=n
-
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
- # This needs to be 4MB aligned for 32-bit support
- FW_JUMP_ADDR=0x80400000
- else
- # This needs to be 2MB aligned for 64-bit support
- FW_JUMP_ADDR=0x80200000
- endif
-FW_JUMP_FDT_ADDR=0x82200000
-
-# Firmware with payload configuration.
-FW_PAYLOAD=y
-
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-# This needs to be 4MB aligned for 32-bit support
-  FW_PAYLOAD_OFFSET=0x400000
-else
-# This needs to be 2MB aligned for 64-bit support
-  FW_PAYLOAD_OFFSET=0x200000
-endif
-FW_PAYLOAD_FDT_ADDR=0x82200000
-FW_PAYLOAD_ALIGN=0x1000
diff --git a/platform/fpga/ariane/objects.mk b/platform/fpga/ariane/objects.mk
index 814e6da..83581ac 100644
--- a/platform/fpga/ariane/objects.mk
+++ b/platform/fpga/ariane/objects.mk
@@ -5,4 +5,39 @@
 #		Panagiotis Peristerakis <perister at ics.forth.gr>
 #
 
+# Compiler flags
+platform-cppflags-y =
+platform-cflags-y =
+platform-asflags-y =
+platform-ldflags-y =
+
+# Object to build
 platform-objs-y += platform.o
+
+PLATFORM_RISCV_XLEN = 64
+
+# Blobs to build
+FW_TEXT_START=0x80000000
+FW_JUMP=n
+
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+ # This needs to be 4MB aligned for 32-bit support
+ FW_JUMP_ADDR=0x80400000
+ else
+ # This needs to be 2MB aligned for 64-bit support
+ FW_JUMP_ADDR=0x80200000
+ endif
+FW_JUMP_FDT_ADDR=0x82200000
+
+# Firmware with payload configuration.
+FW_PAYLOAD=y
+
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+# This needs to be 4MB aligned for 32-bit support
+  FW_PAYLOAD_OFFSET=0x400000
+else
+# This needs to be 2MB aligned for 64-bit support
+  FW_PAYLOAD_OFFSET=0x200000
+endif
+FW_PAYLOAD_FDT_ADDR=0x82200000
+FW_PAYLOAD_ALIGN=0x1000
diff --git a/platform/fpga/openpiton/config.mk b/platform/fpga/openpiton/config.mk
deleted file mode 100644
index a969b25..0000000
--- a/platform/fpga/openpiton/config.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2020 Western Digital Corporation or its affiliates.
-#
-
-#for more infos, check out /platform/template/config.mk
-
-PLATFORM_RISCV_XLEN = 64
-
-# Blobs to build
-FW_TEXT_START=0x80000000
-FW_JUMP=n
-
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
- # This needs to be 4MB aligned for 32-bit support
- FW_JUMP_ADDR=0x80400000
- else
- # This needs to be 2MB aligned for 64-bit support
- FW_JUMP_ADDR=0x80200000
- endif
-FW_JUMP_FDT_ADDR=0x82200000
-
-# Firmware with payload configuration.
-FW_PAYLOAD=y
-
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-# This needs to be 4MB aligned for 32-bit support
-  FW_PAYLOAD_OFFSET=0x400000
-else
-# This needs to be 2MB aligned for 64-bit support
-  FW_PAYLOAD_OFFSET=0x200000
-endif
-FW_PAYLOAD_FDT_ADDR=0x82200000
-FW_PAYLOAD_ALIGN=0x1000
diff --git a/platform/fpga/openpiton/objects.mk b/platform/fpga/openpiton/objects.mk
index 30a3c4f..c8c345a 100644
--- a/platform/fpga/openpiton/objects.mk
+++ b/platform/fpga/openpiton/objects.mk
@@ -4,4 +4,39 @@
 # Copyright (c) 2020 Western Digital Corporation or its affiliates.
 #
 
+# Compiler flags
+platform-cppflags-y =
+platform-cflags-y =
+platform-asflags-y =
+platform-ldflags-y =
+
+# Objects to build
 platform-objs-y += platform.o
+
+PLATFORM_RISCV_XLEN = 64
+
+# Blobs to build
+FW_TEXT_START=0x80000000
+FW_JUMP=n
+
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+ # This needs to be 4MB aligned for 32-bit support
+ FW_JUMP_ADDR=0x80400000
+ else
+ # This needs to be 2MB aligned for 64-bit support
+ FW_JUMP_ADDR=0x80200000
+ endif
+FW_JUMP_FDT_ADDR=0x82200000
+
+# Firmware with payload configuration.
+FW_PAYLOAD=y
+
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+# This needs to be 4MB aligned for 32-bit support
+  FW_PAYLOAD_OFFSET=0x400000
+else
+# This needs to be 2MB aligned for 64-bit support
+  FW_PAYLOAD_OFFSET=0x200000
+endif
+FW_PAYLOAD_FDT_ADDR=0x82200000
+FW_PAYLOAD_ALIGN=0x1000
diff --git a/platform/generic/config.mk b/platform/generic/config.mk
deleted file mode 100644
index 8151974..0000000
--- a/platform/generic/config.mk
+++ /dev/null
@@ -1,40 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2020 Western Digital Corporation or its affiliates.
-#
-# Authors:
-#   Anup Patel <anup.patel at wdc.com>
-#
-
-# Compiler flags
-platform-cppflags-y =
-platform-cflags-y =
-platform-asflags-y =
-platform-ldflags-y =
-
-# Command for platform specific "make run"
-platform-runcmd = qemu-system-riscv$(PLATFORM_RISCV_XLEN) -M virt -m 256M \
-  -nographic -bios $(build_dir)/platform/generic/firmware/fw_payload.elf
-
-# Blobs to build
-FW_TEXT_START=0x80000000
-FW_DYNAMIC=y
-FW_JUMP=y
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-  # This needs to be 4MB aligned for 32-bit system
-  FW_JUMP_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x400000)))
-else
-  # This needs to be 2MB aligned for 64-bit system
-  FW_JUMP_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x200000)))
-endif
-FW_JUMP_FDT_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x2200000)))
-FW_PAYLOAD=y
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-  # This needs to be 4MB aligned for 32-bit system
-  FW_PAYLOAD_OFFSET=0x400000
-else
-  # This needs to be 2MB aligned for 64-bit system
-  FW_PAYLOAD_OFFSET=0x200000
-endif
-FW_PAYLOAD_FDT_ADDR=$(FW_JUMP_FDT_ADDR)
diff --git a/platform/generic/objects.mk b/platform/generic/objects.mk
index 4907754..136853e 100644
--- a/platform/generic/objects.mk
+++ b/platform/generic/objects.mk
@@ -7,5 +7,38 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
+# Compiler flags
+platform-cppflags-y =
+platform-cflags-y =
+platform-asflags-y =
+platform-ldflags-y =
+
+# Command for platform specific "make run"
+platform-runcmd = qemu-system-riscv$(PLATFORM_RISCV_XLEN) -M virt -m 256M \
+  -nographic -bios $(build_dir)/platform/generic/firmware/fw_payload.elf
+
+# Objects to build
 platform-objs-y += platform.o
 platform-objs-y += platform_override_modules.o
+
+# Blobs to build
+FW_TEXT_START=0x80000000
+FW_DYNAMIC=y
+FW_JUMP=y
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+  # This needs to be 4MB aligned for 32-bit system
+  FW_JUMP_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x400000)))
+else
+  # This needs to be 2MB aligned for 64-bit system
+  FW_JUMP_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x200000)))
+endif
+FW_JUMP_FDT_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x2200000)))
+FW_PAYLOAD=y
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+  # This needs to be 4MB aligned for 32-bit system
+  FW_PAYLOAD_OFFSET=0x400000
+else
+  # This needs to be 2MB aligned for 64-bit system
+  FW_PAYLOAD_OFFSET=0x200000
+endif
+FW_PAYLOAD_FDT_ADDR=$(FW_JUMP_FDT_ADDR)
diff --git a/platform/kendryte/k210/config.mk b/platform/kendryte/k210/config.mk
deleted file mode 100644
index 8a9b814..0000000
--- a/platform/kendryte/k210/config.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2019 Western Digital Corporation or its affiliates.
-#
-# Authors:
-#   Damien Le Moal <damien.lemoal at wdc.com>
-#
-
-# Compiler flags
-platform-cppflags-y =
-platform-cflags-y =
-platform-asflags-y =
-platform-ldflags-y =
-
-# Blobs to build
-FW_TEXT_START=0x80000000
-FW_PAYLOAD=y
-FW_PAYLOAD_ALIGN=0x1000
diff --git a/platform/kendryte/k210/objects.mk b/platform/kendryte/k210/objects.mk
index b74da74..1bfb898 100644
--- a/platform/kendryte/k210/objects.mk
+++ b/platform/kendryte/k210/objects.mk
@@ -7,8 +7,20 @@
 #   Damien Le Moal <damien.lemoal at wdc.com>
 #
 
+# Compiler flags
+platform-cppflags-y =
+platform-cflags-y =
+platform-asflags-y =
+platform-ldflags-y =
+
+# Objects to build
 platform-objs-y += platform.o
 
 platform-objs-y += k210.o
 platform-varprefix-k210.o = dt_k210
 platform-padding-k210.o = 2048
+
+# Blobs to build
+FW_TEXT_START=0x80000000
+FW_PAYLOAD=y
+FW_PAYLOAD_ALIGN=0x1000
diff --git a/platform/nuclei/ux600/config.mk b/platform/nuclei/ux600/config.mk
deleted file mode 100644
index dddcc4e..0000000
--- a/platform/nuclei/ux600/config.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2020 Nuclei Corporation or its affiliates.
-#
-# Authors:
-#   lujun <lujun at nucleisys.com>
-#   hqfang <578567190 at qq.com>
-#
-
-# Compiler flags
-platform-cppflags-y =
-platform-cflags-y =
-platform-asflags-y =
-platform-ldflags-y =
-
-# Command for platform specific "make run"
-platform-runcmd = xl_spike \
-  $(build_dir)/platform/nuclei/ux600/firmware/fw_payload.elf
-
-# Blobs to build
-FW_TEXT_START=0xA0000000
-FW_DYNAMIC=y
-FW_JUMP=y
-
-FW_JUMP_ADDR=0xA0200000
-FW_JUMP_FDT_ADDR=0xA8000000
-FW_PAYLOAD=y
-FW_PAYLOAD_OFFSET=0x200000
-FW_PAYLOAD_FDT_ADDR=0xA8000000
diff --git a/platform/nuclei/ux600/objects.mk b/platform/nuclei/ux600/objects.mk
index 8c36c90..7c429e0 100644
--- a/platform/nuclei/ux600/objects.mk
+++ b/platform/nuclei/ux600/objects.mk
@@ -8,4 +8,26 @@
 #   hqfang <578567190 at qq.com>
 #
 
+# Compiler flags
+platform-cppflags-y =
+platform-cflags-y =
+platform-asflags-y =
+platform-ldflags-y =
+
+# Command for platform specific "make run"
+platform-runcmd = xl_spike \
+  $(build_dir)/platform/nuclei/ux600/firmware/fw_payload.elf
+
+# Objects to build
 platform-objs-y += platform.o
+
+# Blobs to build
+FW_TEXT_START=0xA0000000
+FW_DYNAMIC=y
+FW_JUMP=y
+
+FW_JUMP_ADDR=0xA0200000
+FW_JUMP_FDT_ADDR=0xA8000000
+FW_PAYLOAD=y
+FW_PAYLOAD_OFFSET=0x200000
+FW_PAYLOAD_FDT_ADDR=0xA8000000
diff --git a/platform/template/config.mk b/platform/template/config.mk
deleted file mode 100644
index f817342..0000000
--- a/platform/template/config.mk
+++ /dev/null
@@ -1,77 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2019 Western Digital Corporation or its affiliates.
-#
-
-# Compiler pre-processor flags
-platform-cppflags-y =
-
-# C Compiler and assembler flags.
-platform-cflags-y =
-platform-asflags-y =
-
-# Linker flags: additional libraries and object files that the platform
-# code needs can be added here
-platform-ldflags-y =
-
-#
-# Command for platform specific "make run"
-# Useful for development and debugging on plaftform simulator (such as QEMU)
-#
-# platform-runcmd = your_platform_run.sh
-
-#
-# Platform RISC-V XLEN, ABI, ISA and Code Model configuration.
-# These are optional parameters but platforms can optionaly provide it.
-# Some of these are guessed based on GCC compiler capabilities
-#
-# PLATFORM_RISCV_XLEN = 64
-# PLATFORM_RISCV_ABI = lp64
-# PLATFORM_RISCV_ISA = rv64imafdc
-# PLATFORM_RISCV_CODE_MODEL = medany
-
-# Firmware load address configuration. This is mandatory.
-FW_TEXT_START=0x80000000
-
-# Optional parameter for path to external FDT
-# FW_FDT_PATH="path to platform flattened device tree file"
-
-#
-# Dynamic firmware configuration.
-# Optional parameters are commented out. Uncomment and define these parameters
-# as needed.
-#
-FW_DYNAMIC=<y|n>
-
-#
-# Jump firmware configuration.
-# Optional parameters are commented out. Uncomment and define these parameters
-# as needed.
-#
-FW_JUMP=<y|n>
-# This needs to be 4MB aligned for 32-bit support
-# This needs to be 2MB aligned for 64-bit support
-# ifeq ($(PLATFORM_RISCV_XLEN), 32)
-# FW_JUMP_ADDR=0x80400000
-# else
-# FW_JUMP_ADDR=0x80200000
-# endif
-# FW_JUMP_FDT_ADDR=0x82200000
-
-#
-# Firmware with payload configuration.
-# Optional parameters are commented out. Uncomment and define these parameters
-# as needed.
-#
-FW_PAYLOAD=<y|n>
-# This needs to be 4MB aligned for 32-bit support
-# This needs to be 2MB aligned for 64-bit support
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-FW_PAYLOAD_OFFSET=0x400000
-else
-FW_PAYLOAD_OFFSET=0x200000
-endif
-# FW_PAYLOAD_ALIGN=0x1000
-# FW_PAYLOAD_PATH="path to next boot stage binary image file"
-# FW_PAYLOAD_FDT_ADDR=0x82200000
diff --git a/platform/template/objects.mk b/platform/template/objects.mk
index caaec2b..0025928 100644
--- a/platform/template/objects.mk
+++ b/platform/template/objects.mk
@@ -4,6 +4,33 @@
 # Copyright (c) 2019 Western Digital Corporation or its affiliates.
 #
 
+# Compiler pre-processor flags
+platform-cppflags-y =
+
+# C Compiler and assembler flags.
+platform-cflags-y =
+platform-asflags-y =
+
+# Linker flags: additional libraries and object files that the platform
+# code needs can be added here
+platform-ldflags-y =
+
+#
+# Command for platform specific "make run"
+# Useful for development and debugging on plaftform simulator (such as QEMU)
+#
+# platform-runcmd = your_platform_run.sh
+
+#
+# Platform RISC-V XLEN, ABI, ISA and Code Model configuration.
+# These are optional parameters but platforms can optionaly provide it.
+# Some of these are guessed based on GCC compiler capabilities
+#
+# PLATFORM_RISCV_XLEN = 64
+# PLATFORM_RISCV_ABI = lp64
+# PLATFORM_RISCV_ISA = rv64imafdc
+# PLATFORM_RISCV_CODE_MODEL = medany
+
 # Space separated list of object file names to be compiled for the platform
 platform-objs-y += platform.o
 
@@ -13,3 +40,48 @@ platform-objs-y += platform.o
 # source file be in the form <dt file name>.dts
 #
 # platform-objs-y += <dt file name>.o
+
+# Firmware load address configuration. This is mandatory.
+FW_TEXT_START=0x80000000
+
+# Optional parameter for path to external FDT
+# FW_FDT_PATH="path to platform flattened device tree file"
+
+#
+# Dynamic firmware configuration.
+# Optional parameters are commented out. Uncomment and define these parameters
+# as needed.
+#
+FW_DYNAMIC=<y|n>
+
+#
+# Jump firmware configuration.
+# Optional parameters are commented out. Uncomment and define these parameters
+# as needed.
+#
+FW_JUMP=<y|n>
+# This needs to be 4MB aligned for 32-bit support
+# This needs to be 2MB aligned for 64-bit support
+# ifeq ($(PLATFORM_RISCV_XLEN), 32)
+# FW_JUMP_ADDR=0x80400000
+# else
+# FW_JUMP_ADDR=0x80200000
+# endif
+# FW_JUMP_FDT_ADDR=0x82200000
+
+#
+# Firmware with payload configuration.
+# Optional parameters are commented out. Uncomment and define these parameters
+# as needed.
+#
+FW_PAYLOAD=<y|n>
+# This needs to be 4MB aligned for 32-bit support
+# This needs to be 2MB aligned for 64-bit support
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+FW_PAYLOAD_OFFSET=0x400000
+else
+FW_PAYLOAD_OFFSET=0x200000
+endif
+# FW_PAYLOAD_ALIGN=0x1000
+# FW_PAYLOAD_PATH="path to next boot stage binary image file"
+# FW_PAYLOAD_FDT_ADDR=0x82200000
diff --git a/platform/template/platform.c b/platform/template/platform.c
index 5524847..f3802da 100644
--- a/platform/template/platform.c
+++ b/platform/template/platform.c
@@ -11,7 +11,7 @@
 
 /*
  * Include these files as needed.
- * See config.mk PLATFORM_xxx configuration parameters.
+ * See objects.mk PLATFORM_xxx configuration parameters.
  */
 #include <sbi_utils/ipi/aclint_mswi.h>
 #include <sbi_utils/irqchip/plic.h>
-- 
2.34.1



From jrtc27 at jrtc27.com  Mon Jul 18 06:51:51 2022
From: jrtc27 at jrtc27.com (Jessica Clarke)
Date: Mon, 18 Jul 2022 14:51:51 +0100
Subject: [PATCH 01/14] Makefile: Add initial kconfig support for each
 platform
In-Reply-To: <20220718124210.2177576-2-apatel@ventanamicro.com>
References: <20220718124210.2177576-1-apatel@ventanamicro.com>
 <20220718124210.2177576-2-apatel@ventanamicro.com>
Message-ID: <86B07F7B-2C14-4AFE-AC3D-934DE6F22F5B@jrtc27.com>

On 18 Jul 2022, at 13:41, Anup Patel <apatel at ventanamicro.com> wrote:
> 
> We extend the top-level makefile to allow kconfig based configuration
> for each platform where each platform has it's own set of configs with
> "defconfig" being the default config.
> 
> Signed-off-by: Anup Patel <apatel at ventanamicro.com>
> ---
> Kconfig                                   | 23 ++++++++++
> Makefile                                  | 54 +++++++++++++++++++++--
> firmware/Kconfig                          |  1 +
> lib/utils/Kconfig                         |  1 +
> platform/andes/ae350/Kconfig              |  5 +++
> platform/andes/ae350/configs/defconfig    |  0
> platform/fpga/ariane/Kconfig              |  5 +++
> platform/fpga/ariane/configs/defconfig    |  0
> platform/fpga/openpiton/Kconfig           |  5 +++
> platform/fpga/openpiton/configs/defconfig |  0
> platform/generic/Kconfig                  |  5 +++
> platform/generic/configs/defconfig        |  0
> platform/kendryte/k210/Kconfig            |  5 +++
> platform/kendryte/k210/configs/defconfig  |  0
> platform/nuclei/ux600/Kconfig             |  5 +++
> platform/nuclei/ux600/configs/defconfig   |  0
> platform/template/Kconfig                 |  5 +++
> platform/template/configs/defconfig       |  0
> 18 files changed, 111 insertions(+), 3 deletions(-)
> create mode 100644 Kconfig
> create mode 100644 firmware/Kconfig
> create mode 100644 lib/utils/Kconfig
> create mode 100644 platform/andes/ae350/Kconfig
> create mode 100644 platform/andes/ae350/configs/defconfig
> create mode 100644 platform/fpga/ariane/Kconfig
> create mode 100644 platform/fpga/ariane/configs/defconfig
> create mode 100644 platform/fpga/openpiton/Kconfig
> create mode 100644 platform/fpga/openpiton/configs/defconfig
> create mode 100644 platform/generic/Kconfig
> create mode 100644 platform/generic/configs/defconfig
> create mode 100644 platform/kendryte/k210/Kconfig
> create mode 100644 platform/kendryte/k210/configs/defconfig
> create mode 100644 platform/nuclei/ux600/Kconfig
> create mode 100644 platform/nuclei/ux600/configs/defconfig
> create mode 100644 platform/template/Kconfig
> create mode 100644 platform/template/configs/defconfig
> 
> diff --git a/Kconfig b/Kconfig
> new file mode 100644
> index 0000000..b8615d2
> --- /dev/null
> +++ b/Kconfig
> @@ -0,0 +1,23 @@
> +# SPDX-License-Identifier: GPL-2.0

GPL in OpenSBI? I suspect you blindly copied this boilerplate from
Linux (and repeated it everywhere)?..

Jess

> +mainmenu "OpenSBI $OPENSBI_PLATFORM Configuration"
> +
> +config OPENSBI_SRC_DIR
> +	string
> +	option env="OPENSBI_SRC_DIR"
> +
> +config OPENSBI_PLATFORM
> +	string
> +	option env="OPENSBI_PLATFORM"
> +
> +config OPENSBI_PLATFORM_SRC_DIR
> +	string
> +	option env="OPENSBI_PLATFORM_SRC_DIR"
> +
> +menu "Platform Options"
> +source "$OPENSBI_PLATFORM_SRC_DIR/Kconfig"
> +endmenu
> +
> +source "$OPENSBI_SRC_DIR/lib/utils/Kconfig"
> +
> +source "$OPENSBI_SRC_DIR/firmware/Kconfig"
> diff --git a/Makefile b/Makefile
> index f619ef7..46c7c1c 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -52,6 +52,9 @@ ifdef PLATFORM_DIR
> else
>  platform_parent_dir=$(src_dir)/platform
> endif
> +ifndef PLATFORM_DEFCONFIG
> +PLATFORM_DEFCONFIG=defconfig
> +endif
> 
> # Check if verbosity is ON for build process
> CMD_PREFIX_DEFAULT := @
> @@ -70,6 +73,19 @@ export libsbi_dir=$(CURDIR)/lib/sbi
> export libsbiutils_dir=$(CURDIR)/lib/utils
> export firmware_dir=$(CURDIR)/firmware
> 
> +# Setup variables for kconfig-frontends
> +ifdef PLATFORM
> +export KCONFIG_CONFIG=$(platform_build_dir)/.config
> +export KCONFIG_AUTOCONFIG=$(platform_build_dir)/auto.conf
> +export KCONFIG_TRISTATE=$(platform_build_dir)/tristate.conf
> +export KCONFIG_AUTOHEADER=$(platform_build_dir)/autoconf.h
> +export KCONFIG_AUTOCONF_CMD=$(platform_build_dir)/include/config/auto.conf.cmd
> +# Additional exports for include paths in Kconfig files
> +export OPENSBI_SRC_DIR=$(src_dir)
> +export OPENSBI_PLATFORM=$(PLATFORM)
> +export OPENSBI_PLATFORM_SRC_DIR=$(platform_src_dir)
> +endif
> +
> # Find library version
> OPENSBI_VERSION_MAJOR=`grep "define OPENSBI_VERSION_MAJOR" $(include_dir)/sbi/sbi_version.h | sed 's/.*MAJOR.*\([0-9][0-9]*\)/\1/'`
> OPENSBI_VERSION_MINOR=`grep "define OPENSBI_VERSION_MINOR" $(include_dir)/sbi/sbi_version.h | sed 's/.*MINOR.*\([0-9][0-9]*\)/\1/'`
> @@ -183,8 +199,34 @@ libsbi-object-mks=$(shell if [ -d $(libsbi_dir) ]; then find $(libsbi_dir) -inam
> libsbiutils-object-mks=$(shell if [ -d $(libsbiutils_dir) ]; then find $(libsbiutils_dir) -iname "objects.mk" | sort -r; fi)
> firmware-object-mks=$(shell if [ -d $(firmware_dir) ]; then find $(firmware_dir) -iname "objects.mk" | sort -r; fi)
> 
> -# Include platform specifig config.mk
> +# The "make all" rule should always be first rule
> +.PHONY: all
> +all:
> +
> +# Include platform specific .config and config.mk
> ifdef PLATFORM
> +.PHONY: menuconfig
> +menuconfig: $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
> +	$(CMD_PREFIX)mkdir -p `dirname $(KCONFIG_CONFIG)`
> +	$(CMD_PREFIX)mkdir -p `dirname $(KCONFIG_AUTOCONF_CMD)`
> +	$(CMD_PREFIX)kconfig-mconf $(src_dir)/Kconfig
> +	$(CMD_PREFIX)cd $(platform_build_dir) && kconfig-conf --silentoldconfig $(src_dir)/Kconfig
> +
> +.PHONY: savedefconfig
> +savedefconfig: $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
> +	$(CMD_PREFIX)mkdir -p `dirname $(KCONFIG_CONFIG)`
> +	$(CMD_PREFIX)mkdir -p `dirname $(KCONFIG_AUTOCONF_CMD)`
> +	$(CMD_PREFIX)kconfig-conf --savedefconfig `dirname $(KCONFIG_CONFIG)`/defconfig $(src_dir)/Kconfig
> +
> +$(KCONFIG_CONFIG): $(platform_src_dir)/configs/$(PLATFORM_DEFCONFIG) $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
> +	$(CMD_PREFIX)mkdir -p `dirname $(KCONFIG_CONFIG)`
> +	$(CMD_PREFIX)mkdir -p `dirname $(KCONFIG_AUTOCONF_CMD)`
> +	$(CMD_PREFIX)cp -f $(platform_src_dir)/configs/$(PLATFORM_DEFCONFIG) $(KCONFIG_CONFIG)
> +	$(CMD_PREFIX)cd $(platform_build_dir) && kconfig-conf --silentoldconfig --olddefconfig $(src_dir)/Kconfig
> +$(KCONFIG_AUTOCONF_CMD): $(KCONFIG_CONFIG)
> +include $(KCONFIG_CONFIG)
> +include $(KCONFIG_AUTOCONF_CMD)
> +
> include $(platform_src_dir)/config.mk
> endif
> 
> @@ -280,6 +322,9 @@ ifeq ($(BUILD_INFO),y)
> GENFLAGS	+=	-DOPENSBI_BUILD_TIME_STAMP="\"$(OPENSBI_BUILD_TIME_STAMP)\""
> GENFLAGS	+=	-DOPENSBI_BUILD_COMPILER_VERSION="\"$(OPENSBI_BUILD_COMPILER_VERSION)\""
> endif
> +ifdef PLATFORM
> +GENFLAGS	+=	-include $(KCONFIG_AUTOHEADER)
> +endif
> GENFLAGS	+=	$(libsbiutils-genflags-y)
> GENFLAGS	+=	$(platform-genflags-y)
> GENFLAGS	+=	$(firmware-genflags-y)
> @@ -419,7 +464,7 @@ targets-y += $(platform_build_dir)/lib/libplatsbi.a
> endif
> targets-y += $(firmware-bins-path-y)
> 
> -# Default rule "make" should always be first rule
> +# The default "make all" rule
> .PHONY: all
> all: $(targets-y)
> 
> @@ -456,7 +501,7 @@ $(build_dir)/%.o: $(src_dir)/%.S
> 	$(call compile_as,$@,$<)
> 
> $(build_dir)/%.dep: $(src_dir)/%.carray
> -	$(call compile_gen_dep,$@,.c,$<)
> +	$(call compile_gen_dep,$@,.c,$< $(KCONFIG_CONFIG))
> 	$(call compile_gen_dep,$@,.o,$(@:.dep=.c))
> 
> $(build_dir)/%.c: $(src_dir)/%.carray
> @@ -523,6 +568,9 @@ docs: $(build_dir)/docs/latex/refman.pdf
> # They should not be included for any "xxxconfig" or "xxxclean" rule
> all-deps-1 = $(if $(findstring config,$(MAKECMDGOALS)),,$(deps-y))
> all-deps-2 = $(if $(findstring clean,$(MAKECMDGOALS)),,$(all-deps-1))
> +ifdef PLATFORM
> +$(all-deps-2): | $(KCONFIG_CONFIG)
> +endif
> -include $(all-deps-2)
> 
> # Include external dependency of firmwares after default Makefile rules
> diff --git a/firmware/Kconfig b/firmware/Kconfig
> new file mode 100644
> index 0000000..f66554c
> --- /dev/null
> +++ b/firmware/Kconfig
> @@ -0,0 +1 @@
> +# SPDX-License-Identifier: GPL-2.0
> diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
> new file mode 100644
> index 0000000..f66554c
> --- /dev/null
> +++ b/lib/utils/Kconfig
> @@ -0,0 +1 @@
> +# SPDX-License-Identifier: GPL-2.0
> diff --git a/platform/andes/ae350/Kconfig b/platform/andes/ae350/Kconfig
> new file mode 100644
> index 0000000..349c1d3
> --- /dev/null
> +++ b/platform/andes/ae350/Kconfig
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +config PLATFORM_ANDES_AE350
> +	bool
> +	default y
> diff --git a/platform/andes/ae350/configs/defconfig b/platform/andes/ae350/configs/defconfig
> new file mode 100644
> index 0000000..e69de29
> diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
> new file mode 100644
> index 0000000..26f9beb
> --- /dev/null
> +++ b/platform/fpga/ariane/Kconfig
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +config PLATFORM_ARIANE_FPGA
> +	bool
> +	default y
> diff --git a/platform/fpga/ariane/configs/defconfig b/platform/fpga/ariane/configs/defconfig
> new file mode 100644
> index 0000000..e69de29
> diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
> new file mode 100644
> index 0000000..8bfa85a
> --- /dev/null
> +++ b/platform/fpga/openpiton/Kconfig
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +config PLATFORM_OPENPITON_FPGA
> +	bool
> +	default y
> diff --git a/platform/fpga/openpiton/configs/defconfig b/platform/fpga/openpiton/configs/defconfig
> new file mode 100644
> index 0000000..e69de29
> diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
> new file mode 100644
> index 0000000..a811a05
> --- /dev/null
> +++ b/platform/generic/Kconfig
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +config PLATFORM_GENERIC
> +	bool
> +	default y
> diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
> new file mode 100644
> index 0000000..e69de29
> diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
> new file mode 100644
> index 0000000..726423a
> --- /dev/null
> +++ b/platform/kendryte/k210/Kconfig
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +config PLATFORM_KENDRYTE_K210
> +	bool
> +	default y
> diff --git a/platform/kendryte/k210/configs/defconfig b/platform/kendryte/k210/configs/defconfig
> new file mode 100644
> index 0000000..e69de29
> diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
> new file mode 100644
> index 0000000..bb8410f
> --- /dev/null
> +++ b/platform/nuclei/ux600/Kconfig
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +config PLATFORM_NUCLEI_UX600
> +	bool
> +	default y
> diff --git a/platform/nuclei/ux600/configs/defconfig b/platform/nuclei/ux600/configs/defconfig
> new file mode 100644
> index 0000000..e69de29
> diff --git a/platform/template/Kconfig b/platform/template/Kconfig
> new file mode 100644
> index 0000000..bd95ea0
> --- /dev/null
> +++ b/platform/template/Kconfig
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +config PLATFORM_TEMPLATE
> +	bool
> +	default y
> diff --git a/platform/template/configs/defconfig b/platform/template/configs/defconfig
> new file mode 100644
> index 0000000..e69de29
> -- 
> 2.34.1
> 
> 
> -- 
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi



From apatel at ventanamicro.com  Mon Jul 18 07:44:21 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 20:14:21 +0530
Subject: [PATCH 01/14] Makefile: Add initial kconfig support for each platform
In-Reply-To: <86B07F7B-2C14-4AFE-AC3D-934DE6F22F5B@jrtc27.com>
References: <20220718124210.2177576-1-apatel@ventanamicro.com>
 <20220718124210.2177576-2-apatel@ventanamicro.com> <86B07F7B-2C14-4AFE-AC3D-934DE6F22F5B@jrtc27.com>
Message-ID: <CAK9=C2U3DkDKbKR0mSerBCv1gUsHUSQqYdjhe3H3qCofzTrccA@mail.gmail.com>

On Mon, Jul 18, 2022 at 7:21 PM Jessica Clarke <jrtc27 at jrtc27.com> wrote:
>
> On 18 Jul 2022, at 13:41, Anup Patel <apatel at ventanamicro.com> wrote:
> >
> > We extend the top-level makefile to allow kconfig based configuration
> > for each platform where each platform has it's own set of configs with
> > "defconfig" being the default config.
> >
> > Signed-off-by: Anup Patel <apatel at ventanamicro.com>
> > ---
> > Kconfig                                   | 23 ++++++++++
> > Makefile                                  | 54 +++++++++++++++++++++--
> > firmware/Kconfig                          |  1 +
> > lib/utils/Kconfig                         |  1 +
> > platform/andes/ae350/Kconfig              |  5 +++
> > platform/andes/ae350/configs/defconfig    |  0
> > platform/fpga/ariane/Kconfig              |  5 +++
> > platform/fpga/ariane/configs/defconfig    |  0
> > platform/fpga/openpiton/Kconfig           |  5 +++
> > platform/fpga/openpiton/configs/defconfig |  0
> > platform/generic/Kconfig                  |  5 +++
> > platform/generic/configs/defconfig        |  0
> > platform/kendryte/k210/Kconfig            |  5 +++
> > platform/kendryte/k210/configs/defconfig  |  0
> > platform/nuclei/ux600/Kconfig             |  5 +++
> > platform/nuclei/ux600/configs/defconfig   |  0
> > platform/template/Kconfig                 |  5 +++
> > platform/template/configs/defconfig       |  0
> > 18 files changed, 111 insertions(+), 3 deletions(-)
> > create mode 100644 Kconfig
> > create mode 100644 firmware/Kconfig
> > create mode 100644 lib/utils/Kconfig
> > create mode 100644 platform/andes/ae350/Kconfig
> > create mode 100644 platform/andes/ae350/configs/defconfig
> > create mode 100644 platform/fpga/ariane/Kconfig
> > create mode 100644 platform/fpga/ariane/configs/defconfig
> > create mode 100644 platform/fpga/openpiton/Kconfig
> > create mode 100644 platform/fpga/openpiton/configs/defconfig
> > create mode 100644 platform/generic/Kconfig
> > create mode 100644 platform/generic/configs/defconfig
> > create mode 100644 platform/kendryte/k210/Kconfig
> > create mode 100644 platform/kendryte/k210/configs/defconfig
> > create mode 100644 platform/nuclei/ux600/Kconfig
> > create mode 100644 platform/nuclei/ux600/configs/defconfig
> > create mode 100644 platform/template/Kconfig
> > create mode 100644 platform/template/configs/defconfig
> >
> > diff --git a/Kconfig b/Kconfig
> > new file mode 100644
> > index 0000000..b8615d2
> > --- /dev/null
> > +++ b/Kconfig
> > @@ -0,0 +1,23 @@
> > +# SPDX-License-Identifier: GPL-2.0
>
> GPL in OpenSBI? I suspect you blindly copied this boilerplate from
> Linux (and repeated it everywhere)?..

Aargh !!! This was totally unintentional.

Thanks for catching. I will quickly send a v2.

Regards,
Anup

>
> Jess
>
> > +mainmenu "OpenSBI $OPENSBI_PLATFORM Configuration"
> > +
> > +config OPENSBI_SRC_DIR
> > +     string
> > +     option env="OPENSBI_SRC_DIR"
> > +
> > +config OPENSBI_PLATFORM
> > +     string
> > +     option env="OPENSBI_PLATFORM"
> > +
> > +config OPENSBI_PLATFORM_SRC_DIR
> > +     string
> > +     option env="OPENSBI_PLATFORM_SRC_DIR"
> > +
> > +menu "Platform Options"
> > +source "$OPENSBI_PLATFORM_SRC_DIR/Kconfig"
> > +endmenu
> > +
> > +source "$OPENSBI_SRC_DIR/lib/utils/Kconfig"
> > +
> > +source "$OPENSBI_SRC_DIR/firmware/Kconfig"
> > diff --git a/Makefile b/Makefile
> > index f619ef7..46c7c1c 100644
> > --- a/Makefile
> > +++ b/Makefile
> > @@ -52,6 +52,9 @@ ifdef PLATFORM_DIR
> > else
> >  platform_parent_dir=$(src_dir)/platform
> > endif
> > +ifndef PLATFORM_DEFCONFIG
> > +PLATFORM_DEFCONFIG=defconfig
> > +endif
> >
> > # Check if verbosity is ON for build process
> > CMD_PREFIX_DEFAULT := @
> > @@ -70,6 +73,19 @@ export libsbi_dir=$(CURDIR)/lib/sbi
> > export libsbiutils_dir=$(CURDIR)/lib/utils
> > export firmware_dir=$(CURDIR)/firmware
> >
> > +# Setup variables for kconfig-frontends
> > +ifdef PLATFORM
> > +export KCONFIG_CONFIG=$(platform_build_dir)/.config
> > +export KCONFIG_AUTOCONFIG=$(platform_build_dir)/auto.conf
> > +export KCONFIG_TRISTATE=$(platform_build_dir)/tristate.conf
> > +export KCONFIG_AUTOHEADER=$(platform_build_dir)/autoconf.h
> > +export KCONFIG_AUTOCONF_CMD=$(platform_build_dir)/include/config/auto.conf.cmd
> > +# Additional exports for include paths in Kconfig files
> > +export OPENSBI_SRC_DIR=$(src_dir)
> > +export OPENSBI_PLATFORM=$(PLATFORM)
> > +export OPENSBI_PLATFORM_SRC_DIR=$(platform_src_dir)
> > +endif
> > +
> > # Find library version
> > OPENSBI_VERSION_MAJOR=`grep "define OPENSBI_VERSION_MAJOR" $(include_dir)/sbi/sbi_version.h | sed 's/.*MAJOR.*\([0-9][0-9]*\)/\1/'`
> > OPENSBI_VERSION_MINOR=`grep "define OPENSBI_VERSION_MINOR" $(include_dir)/sbi/sbi_version.h | sed 's/.*MINOR.*\([0-9][0-9]*\)/\1/'`
> > @@ -183,8 +199,34 @@ libsbi-object-mks=$(shell if [ -d $(libsbi_dir) ]; then find $(libsbi_dir) -inam
> > libsbiutils-object-mks=$(shell if [ -d $(libsbiutils_dir) ]; then find $(libsbiutils_dir) -iname "objects.mk" | sort -r; fi)
> > firmware-object-mks=$(shell if [ -d $(firmware_dir) ]; then find $(firmware_dir) -iname "objects.mk" | sort -r; fi)
> >
> > -# Include platform specifig config.mk
> > +# The "make all" rule should always be first rule
> > +.PHONY: all
> > +all:
> > +
> > +# Include platform specific .config and config.mk
> > ifdef PLATFORM
> > +.PHONY: menuconfig
> > +menuconfig: $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
> > +     $(CMD_PREFIX)mkdir -p `dirname $(KCONFIG_CONFIG)`
> > +     $(CMD_PREFIX)mkdir -p `dirname $(KCONFIG_AUTOCONF_CMD)`
> > +     $(CMD_PREFIX)kconfig-mconf $(src_dir)/Kconfig
> > +     $(CMD_PREFIX)cd $(platform_build_dir) && kconfig-conf --silentoldconfig $(src_dir)/Kconfig
> > +
> > +.PHONY: savedefconfig
> > +savedefconfig: $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
> > +     $(CMD_PREFIX)mkdir -p `dirname $(KCONFIG_CONFIG)`
> > +     $(CMD_PREFIX)mkdir -p `dirname $(KCONFIG_AUTOCONF_CMD)`
> > +     $(CMD_PREFIX)kconfig-conf --savedefconfig `dirname $(KCONFIG_CONFIG)`/defconfig $(src_dir)/Kconfig
> > +
> > +$(KCONFIG_CONFIG): $(platform_src_dir)/configs/$(PLATFORM_DEFCONFIG) $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
> > +     $(CMD_PREFIX)mkdir -p `dirname $(KCONFIG_CONFIG)`
> > +     $(CMD_PREFIX)mkdir -p `dirname $(KCONFIG_AUTOCONF_CMD)`
> > +     $(CMD_PREFIX)cp -f $(platform_src_dir)/configs/$(PLATFORM_DEFCONFIG) $(KCONFIG_CONFIG)
> > +     $(CMD_PREFIX)cd $(platform_build_dir) && kconfig-conf --silentoldconfig --olddefconfig $(src_dir)/Kconfig
> > +$(KCONFIG_AUTOCONF_CMD): $(KCONFIG_CONFIG)
> > +include $(KCONFIG_CONFIG)
> > +include $(KCONFIG_AUTOCONF_CMD)
> > +
> > include $(platform_src_dir)/config.mk
> > endif
> >
> > @@ -280,6 +322,9 @@ ifeq ($(BUILD_INFO),y)
> > GENFLAGS      +=      -DOPENSBI_BUILD_TIME_STAMP="\"$(OPENSBI_BUILD_TIME_STAMP)\""
> > GENFLAGS      +=      -DOPENSBI_BUILD_COMPILER_VERSION="\"$(OPENSBI_BUILD_COMPILER_VERSION)\""
> > endif
> > +ifdef PLATFORM
> > +GENFLAGS     +=      -include $(KCONFIG_AUTOHEADER)
> > +endif
> > GENFLAGS      +=      $(libsbiutils-genflags-y)
> > GENFLAGS      +=      $(platform-genflags-y)
> > GENFLAGS      +=      $(firmware-genflags-y)
> > @@ -419,7 +464,7 @@ targets-y += $(platform_build_dir)/lib/libplatsbi.a
> > endif
> > targets-y += $(firmware-bins-path-y)
> >
> > -# Default rule "make" should always be first rule
> > +# The default "make all" rule
> > .PHONY: all
> > all: $(targets-y)
> >
> > @@ -456,7 +501,7 @@ $(build_dir)/%.o: $(src_dir)/%.S
> >       $(call compile_as,$@,$<)
> >
> > $(build_dir)/%.dep: $(src_dir)/%.carray
> > -     $(call compile_gen_dep,$@,.c,$<)
> > +     $(call compile_gen_dep,$@,.c,$< $(KCONFIG_CONFIG))
> >       $(call compile_gen_dep,$@,.o,$(@:.dep=.c))
> >
> > $(build_dir)/%.c: $(src_dir)/%.carray
> > @@ -523,6 +568,9 @@ docs: $(build_dir)/docs/latex/refman.pdf
> > # They should not be included for any "xxxconfig" or "xxxclean" rule
> > all-deps-1 = $(if $(findstring config,$(MAKECMDGOALS)),,$(deps-y))
> > all-deps-2 = $(if $(findstring clean,$(MAKECMDGOALS)),,$(all-deps-1))
> > +ifdef PLATFORM
> > +$(all-deps-2): | $(KCONFIG_CONFIG)
> > +endif
> > -include $(all-deps-2)
> >
> > # Include external dependency of firmwares after default Makefile rules
> > diff --git a/firmware/Kconfig b/firmware/Kconfig
> > new file mode 100644
> > index 0000000..f66554c
> > --- /dev/null
> > +++ b/firmware/Kconfig
> > @@ -0,0 +1 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
> > new file mode 100644
> > index 0000000..f66554c
> > --- /dev/null
> > +++ b/lib/utils/Kconfig
> > @@ -0,0 +1 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > diff --git a/platform/andes/ae350/Kconfig b/platform/andes/ae350/Kconfig
> > new file mode 100644
> > index 0000000..349c1d3
> > --- /dev/null
> > +++ b/platform/andes/ae350/Kconfig
> > @@ -0,0 +1,5 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +config PLATFORM_ANDES_AE350
> > +     bool
> > +     default y
> > diff --git a/platform/andes/ae350/configs/defconfig b/platform/andes/ae350/configs/defconfig
> > new file mode 100644
> > index 0000000..e69de29
> > diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
> > new file mode 100644
> > index 0000000..26f9beb
> > --- /dev/null
> > +++ b/platform/fpga/ariane/Kconfig
> > @@ -0,0 +1,5 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +config PLATFORM_ARIANE_FPGA
> > +     bool
> > +     default y
> > diff --git a/platform/fpga/ariane/configs/defconfig b/platform/fpga/ariane/configs/defconfig
> > new file mode 100644
> > index 0000000..e69de29
> > diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
> > new file mode 100644
> > index 0000000..8bfa85a
> > --- /dev/null
> > +++ b/platform/fpga/openpiton/Kconfig
> > @@ -0,0 +1,5 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +config PLATFORM_OPENPITON_FPGA
> > +     bool
> > +     default y
> > diff --git a/platform/fpga/openpiton/configs/defconfig b/platform/fpga/openpiton/configs/defconfig
> > new file mode 100644
> > index 0000000..e69de29
> > diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
> > new file mode 100644
> > index 0000000..a811a05
> > --- /dev/null
> > +++ b/platform/generic/Kconfig
> > @@ -0,0 +1,5 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +config PLATFORM_GENERIC
> > +     bool
> > +     default y
> > diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
> > new file mode 100644
> > index 0000000..e69de29
> > diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
> > new file mode 100644
> > index 0000000..726423a
> > --- /dev/null
> > +++ b/platform/kendryte/k210/Kconfig
> > @@ -0,0 +1,5 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +config PLATFORM_KENDRYTE_K210
> > +     bool
> > +     default y
> > diff --git a/platform/kendryte/k210/configs/defconfig b/platform/kendryte/k210/configs/defconfig
> > new file mode 100644
> > index 0000000..e69de29
> > diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
> > new file mode 100644
> > index 0000000..bb8410f
> > --- /dev/null
> > +++ b/platform/nuclei/ux600/Kconfig
> > @@ -0,0 +1,5 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +config PLATFORM_NUCLEI_UX600
> > +     bool
> > +     default y
> > diff --git a/platform/nuclei/ux600/configs/defconfig b/platform/nuclei/ux600/configs/defconfig
> > new file mode 100644
> > index 0000000..e69de29
> > diff --git a/platform/template/Kconfig b/platform/template/Kconfig
> > new file mode 100644
> > index 0000000..bd95ea0
> > --- /dev/null
> > +++ b/platform/template/Kconfig
> > @@ -0,0 +1,5 @@
> > +# SPDX-License-Identifier: GPL-2.0
> > +
> > +config PLATFORM_TEMPLATE
> > +     bool
> > +     default y
> > diff --git a/platform/template/configs/defconfig b/platform/template/configs/defconfig
> > new file mode 100644
> > index 0000000..e69de29
> > --
> > 2.34.1
> >
> >
> > --
> > opensbi mailing list
> > opensbi at lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/opensbi
>


From apatel at ventanamicro.com  Mon Jul 18 07:55:57 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 20:25:57 +0530
Subject: [PATCH v2 00/14] OpenSBI Kconfig Support
Message-ID: <20220718145611.2211638-1-apatel@ventanamicro.com>

In future, we will see more drivers in OpenSBI for system reset, serial port,
power management, etc platform specific devices. We need platform specific
compile time configuration to allow users control the size of OpenSBI generic
firmware binaries by only enabling desired drivers. In other words, users
should be allowed to restrict the set of RISC-V platforms on which OpenSBI
generic firmwares built by them can run.

To address above, we add Kconfig support for OpenSBI platforms using the
kconfig-frontends project which is a standalone kconfig parser.

The kconfig-frontends is available as a package/receipe on various distros
(such as Debian, UBuntu, OpenSUSE, etc) and Yocto project.

To try OpenSBI Kconfig support with generic platform, here are few
example commands:
make PLATFORM=generic             <-- builds OpenSBI using "defconfig"
make PLATFORM=generic menuconfig  <-- launches graphical interface for changes

These patches can also be found in the kconfig_v2 branch at:
https://github.com/avpatel/opensbi.git

Changes since v1:
 - Fix SPDX license specifier in new Kconfig files

Anup Patel (14):
  Makefile: Add initial kconfig support for each platform
  Makefile: Compile lib/utils sources separately for each platform
  lib: utils/serial: Use kconfig for enabling/disabling drivers
  lib: utils/reset: Use kconfig for enabling/disabling drivers
  lib: utils/sys: Use kconfig for enabling/disabling drivers
  lib: utils/timer: Use kconfig for enabling/disabling drivers
  lib: utils/ipi: Use kconfig for enabling/disabling drivers
  lib: utils/irqchip: Use kconfig for enabling/disabling drivers
  lib: utils/i2c: Use kconfig for enabling/disabling drivers
  lib: utils/gpio: Use kconfig for enabling/disabling drivers
  lib: utils/fdt: Use kconfig for enabling/disabling
  platform: generic: Use kconfig for enabling/disabling overrides
  platform: Remove redundant config.mk from all platforms
  docs: Update documentation for kconfig support

 Kconfig                                   |  23 +++++
 Makefile                                  | 102 ++++++++++++++++------
 README.md                                 |  25 +++++-
 docs/firmware/fw_dynamic.md               |   2 +-
 docs/firmware/fw_jump.md                  |   4 +-
 docs/firmware/fw_payload.md               |   4 +-
 docs/library_usage.md                     |   6 +-
 docs/platform/platform.md                 |   4 +-
 docs/platform_guide.md                    |   9 +-
 firmware/Kconfig                          |   1 +
 include/sbi_utils/fdt/fdt_domain.h        |   9 ++
 include/sbi_utils/fdt/fdt_pmu.h           |  10 +++
 include/sbi_utils/ipi/fdt_ipi.h           |   9 ++
 include/sbi_utils/irqchip/fdt_irqchip.h   |  10 +++
 include/sbi_utils/irqchip/imsic.h         |  10 +++
 include/sbi_utils/reset/fdt_reset.h       |  12 +++
 include/sbi_utils/serial/fdt_serial.h     |   8 ++
 include/sbi_utils/timer/fdt_timer.h       |   9 ++
 lib/utils/Kconfig                         |  25 ++++++
 lib/utils/fdt/Kconfig                     |  18 ++++
 lib/utils/fdt/objects.mk                  |   8 +-
 lib/utils/gpio/Kconfig                    |  23 +++++
 lib/utils/gpio/objects.mk                 |  10 +--
 lib/utils/i2c/Kconfig                     |  23 +++++
 lib/utils/i2c/objects.mk                  |  10 +--
 lib/utils/ipi/Kconfig                     |  23 +++++
 lib/utils/ipi/objects.mk                  |  10 +--
 lib/utils/irqchip/Kconfig                 |  41 +++++++++
 lib/utils/irqchip/objects.mk              |  22 ++---
 lib/utils/libfdt/Kconfig                  |   5 ++
 lib/utils/libfdt/objects.mk               |   4 +-
 lib/utils/reset/Kconfig                   |  37 ++++++++
 lib/utils/reset/objects.mk                |  28 +++---
 lib/utils/serial/Kconfig                  |  73 ++++++++++++++++
 lib/utils/serial/objects.mk               |  44 +++++-----
 lib/utils/sys/Kconfig                     |  13 +++
 lib/utils/sys/objects.mk                  |   4 +-
 lib/utils/timer/Kconfig                   |  23 +++++
 lib/utils/timer/objects.mk                |  10 +--
 platform/andes/ae350/Kconfig              |   8 ++
 platform/andes/ae350/config.mk            |  36 --------
 platform/andes/ae350/configs/defconfig    |   0
 platform/andes/ae350/objects.mk           |  29 ++++++
 platform/fpga/ariane/Kconfig              |  10 +++
 platform/fpga/ariane/config.mk            |  36 --------
 platform/fpga/ariane/configs/defconfig    |   0
 platform/fpga/ariane/objects.mk           |  35 ++++++++
 platform/fpga/openpiton/Kconfig           |  10 +++
 platform/fpga/openpiton/config.mk         |  35 --------
 platform/fpga/openpiton/configs/defconfig |   0
 platform/fpga/openpiton/objects.mk        |  35 ++++++++
 platform/generic/Kconfig                  |  26 ++++++
 platform/generic/allwinner/objects.mk     |   4 +-
 platform/generic/config.mk                |  40 ---------
 platform/generic/configs/defconfig        |  29 ++++++
 platform/generic/objects.mk               |  33 +++++++
 platform/generic/sifive/objects.mk        |   8 +-
 platform/kendryte/k210/Kconfig            |  10 +++
 platform/kendryte/k210/config.mk          |  19 ----
 platform/kendryte/k210/configs/defconfig  |   0
 platform/kendryte/k210/objects.mk         |  12 +++
 platform/nuclei/ux600/Kconfig             |  10 +++
 platform/nuclei/ux600/config.mk           |  30 -------
 platform/nuclei/ux600/configs/defconfig   |   0
 platform/nuclei/ux600/objects.mk          |  22 +++++
 platform/nuclei/ux600/platform.c          |   1 -
 platform/template/Kconfig                 |   9 ++
 platform/template/config.mk               |  77 ----------------
 platform/template/configs/defconfig       |   0
 platform/template/objects.mk              |  72 +++++++++++++++
 platform/template/platform.c              |   2 +-
 scripts/create-binary-archive.sh          |   2 +-
 72 files changed, 948 insertions(+), 403 deletions(-)
 create mode 100644 Kconfig
 create mode 100644 firmware/Kconfig
 create mode 100644 lib/utils/Kconfig
 create mode 100644 lib/utils/fdt/Kconfig
 create mode 100644 lib/utils/gpio/Kconfig
 create mode 100644 lib/utils/i2c/Kconfig
 create mode 100644 lib/utils/ipi/Kconfig
 create mode 100644 lib/utils/irqchip/Kconfig
 create mode 100644 lib/utils/libfdt/Kconfig
 create mode 100644 lib/utils/reset/Kconfig
 create mode 100644 lib/utils/serial/Kconfig
 create mode 100644 lib/utils/sys/Kconfig
 create mode 100644 lib/utils/timer/Kconfig
 create mode 100644 platform/andes/ae350/Kconfig
 delete mode 100644 platform/andes/ae350/config.mk
 create mode 100644 platform/andes/ae350/configs/defconfig
 create mode 100644 platform/fpga/ariane/Kconfig
 delete mode 100644 platform/fpga/ariane/config.mk
 create mode 100644 platform/fpga/ariane/configs/defconfig
 create mode 100644 platform/fpga/openpiton/Kconfig
 delete mode 100644 platform/fpga/openpiton/config.mk
 create mode 100644 platform/fpga/openpiton/configs/defconfig
 create mode 100644 platform/generic/Kconfig
 delete mode 100644 platform/generic/config.mk
 create mode 100644 platform/generic/configs/defconfig
 create mode 100644 platform/kendryte/k210/Kconfig
 delete mode 100644 platform/kendryte/k210/config.mk
 create mode 100644 platform/kendryte/k210/configs/defconfig
 create mode 100644 platform/nuclei/ux600/Kconfig
 delete mode 100644 platform/nuclei/ux600/config.mk
 create mode 100644 platform/nuclei/ux600/configs/defconfig
 create mode 100644 platform/template/Kconfig
 delete mode 100644 platform/template/config.mk
 create mode 100644 platform/template/configs/defconfig

-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 07:55:58 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 20:25:58 +0530
Subject: [PATCH v2 01/14] Makefile: Add initial kconfig support for each platform
In-Reply-To: <20220718145611.2211638-1-apatel@ventanamicro.com>
References: <20220718145611.2211638-1-apatel@ventanamicro.com>
Message-ID: <20220718145611.2211638-2-apatel@ventanamicro.com>

We extend the top-level makefile to allow kconfig based configuration
for each platform where each platform has it's own set of configs with
"defconfig" being the default config.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 Kconfig                                   | 23 ++++++++++
 Makefile                                  | 54 +++++++++++++++++++++--
 firmware/Kconfig                          |  1 +
 lib/utils/Kconfig                         |  1 +
 platform/andes/ae350/Kconfig              |  5 +++
 platform/andes/ae350/configs/defconfig    |  0
 platform/fpga/ariane/Kconfig              |  5 +++
 platform/fpga/ariane/configs/defconfig    |  0
 platform/fpga/openpiton/Kconfig           |  5 +++
 platform/fpga/openpiton/configs/defconfig |  0
 platform/generic/Kconfig                  |  5 +++
 platform/generic/configs/defconfig        |  0
 platform/kendryte/k210/Kconfig            |  5 +++
 platform/kendryte/k210/configs/defconfig  |  0
 platform/nuclei/ux600/Kconfig             |  5 +++
 platform/nuclei/ux600/configs/defconfig   |  0
 platform/template/Kconfig                 |  5 +++
 platform/template/configs/defconfig       |  0
 18 files changed, 111 insertions(+), 3 deletions(-)
 create mode 100644 Kconfig
 create mode 100644 firmware/Kconfig
 create mode 100644 lib/utils/Kconfig
 create mode 100644 platform/andes/ae350/Kconfig
 create mode 100644 platform/andes/ae350/configs/defconfig
 create mode 100644 platform/fpga/ariane/Kconfig
 create mode 100644 platform/fpga/ariane/configs/defconfig
 create mode 100644 platform/fpga/openpiton/Kconfig
 create mode 100644 platform/fpga/openpiton/configs/defconfig
 create mode 100644 platform/generic/Kconfig
 create mode 100644 platform/generic/configs/defconfig
 create mode 100644 platform/kendryte/k210/Kconfig
 create mode 100644 platform/kendryte/k210/configs/defconfig
 create mode 100644 platform/nuclei/ux600/Kconfig
 create mode 100644 platform/nuclei/ux600/configs/defconfig
 create mode 100644 platform/template/Kconfig
 create mode 100644 platform/template/configs/defconfig

diff --git a/Kconfig b/Kconfig
new file mode 100644
index 0000000..b1e253c
--- /dev/null
+++ b/Kconfig
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+mainmenu "OpenSBI $OPENSBI_PLATFORM Configuration"
+
+config OPENSBI_SRC_DIR
+	string
+	option env="OPENSBI_SRC_DIR"
+
+config OPENSBI_PLATFORM
+	string
+	option env="OPENSBI_PLATFORM"
+
+config OPENSBI_PLATFORM_SRC_DIR
+	string
+	option env="OPENSBI_PLATFORM_SRC_DIR"
+
+menu "Platform Options"
+source "$OPENSBI_PLATFORM_SRC_DIR/Kconfig"
+endmenu
+
+source "$OPENSBI_SRC_DIR/lib/utils/Kconfig"
+
+source "$OPENSBI_SRC_DIR/firmware/Kconfig"
diff --git a/Makefile b/Makefile
index f619ef7..46c7c1c 100644
--- a/Makefile
+++ b/Makefile
@@ -52,6 +52,9 @@ ifdef PLATFORM_DIR
 else
  platform_parent_dir=$(src_dir)/platform
 endif
+ifndef PLATFORM_DEFCONFIG
+PLATFORM_DEFCONFIG=defconfig
+endif
 
 # Check if verbosity is ON for build process
 CMD_PREFIX_DEFAULT := @
@@ -70,6 +73,19 @@ export libsbi_dir=$(CURDIR)/lib/sbi
 export libsbiutils_dir=$(CURDIR)/lib/utils
 export firmware_dir=$(CURDIR)/firmware
 
+# Setup variables for kconfig-frontends
+ifdef PLATFORM
+export KCONFIG_CONFIG=$(platform_build_dir)/.config
+export KCONFIG_AUTOCONFIG=$(platform_build_dir)/auto.conf
+export KCONFIG_TRISTATE=$(platform_build_dir)/tristate.conf
+export KCONFIG_AUTOHEADER=$(platform_build_dir)/autoconf.h
+export KCONFIG_AUTOCONF_CMD=$(platform_build_dir)/include/config/auto.conf.cmd
+# Additional exports for include paths in Kconfig files
+export OPENSBI_SRC_DIR=$(src_dir)
+export OPENSBI_PLATFORM=$(PLATFORM)
+export OPENSBI_PLATFORM_SRC_DIR=$(platform_src_dir)
+endif
+
 # Find library version
 OPENSBI_VERSION_MAJOR=`grep "define OPENSBI_VERSION_MAJOR" $(include_dir)/sbi/sbi_version.h | sed 's/.*MAJOR.*\([0-9][0-9]*\)/\1/'`
 OPENSBI_VERSION_MINOR=`grep "define OPENSBI_VERSION_MINOR" $(include_dir)/sbi/sbi_version.h | sed 's/.*MINOR.*\([0-9][0-9]*\)/\1/'`
@@ -183,8 +199,34 @@ libsbi-object-mks=$(shell if [ -d $(libsbi_dir) ]; then find $(libsbi_dir) -inam
 libsbiutils-object-mks=$(shell if [ -d $(libsbiutils_dir) ]; then find $(libsbiutils_dir) -iname "objects.mk" | sort -r; fi)
 firmware-object-mks=$(shell if [ -d $(firmware_dir) ]; then find $(firmware_dir) -iname "objects.mk" | sort -r; fi)
 
-# Include platform specifig config.mk
+# The "make all" rule should always be first rule
+.PHONY: all
+all:
+
+# Include platform specific .config and config.mk
 ifdef PLATFORM
+.PHONY: menuconfig
+menuconfig: $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
+	$(CMD_PREFIX)mkdir -p `dirname $(KCONFIG_CONFIG)`
+	$(CMD_PREFIX)mkdir -p `dirname $(KCONFIG_AUTOCONF_CMD)`
+	$(CMD_PREFIX)kconfig-mconf $(src_dir)/Kconfig
+	$(CMD_PREFIX)cd $(platform_build_dir) && kconfig-conf --silentoldconfig $(src_dir)/Kconfig
+
+.PHONY: savedefconfig
+savedefconfig: $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
+	$(CMD_PREFIX)mkdir -p `dirname $(KCONFIG_CONFIG)`
+	$(CMD_PREFIX)mkdir -p `dirname $(KCONFIG_AUTOCONF_CMD)`
+	$(CMD_PREFIX)kconfig-conf --savedefconfig `dirname $(KCONFIG_CONFIG)`/defconfig $(src_dir)/Kconfig
+
+$(KCONFIG_CONFIG): $(platform_src_dir)/configs/$(PLATFORM_DEFCONFIG) $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
+	$(CMD_PREFIX)mkdir -p `dirname $(KCONFIG_CONFIG)`
+	$(CMD_PREFIX)mkdir -p `dirname $(KCONFIG_AUTOCONF_CMD)`
+	$(CMD_PREFIX)cp -f $(platform_src_dir)/configs/$(PLATFORM_DEFCONFIG) $(KCONFIG_CONFIG)
+	$(CMD_PREFIX)cd $(platform_build_dir) && kconfig-conf --silentoldconfig --olddefconfig $(src_dir)/Kconfig
+$(KCONFIG_AUTOCONF_CMD): $(KCONFIG_CONFIG)
+include $(KCONFIG_CONFIG)
+include $(KCONFIG_AUTOCONF_CMD)
+
 include $(platform_src_dir)/config.mk
 endif
 
@@ -280,6 +322,9 @@ ifeq ($(BUILD_INFO),y)
 GENFLAGS	+=	-DOPENSBI_BUILD_TIME_STAMP="\"$(OPENSBI_BUILD_TIME_STAMP)\""
 GENFLAGS	+=	-DOPENSBI_BUILD_COMPILER_VERSION="\"$(OPENSBI_BUILD_COMPILER_VERSION)\""
 endif
+ifdef PLATFORM
+GENFLAGS	+=	-include $(KCONFIG_AUTOHEADER)
+endif
 GENFLAGS	+=	$(libsbiutils-genflags-y)
 GENFLAGS	+=	$(platform-genflags-y)
 GENFLAGS	+=	$(firmware-genflags-y)
@@ -419,7 +464,7 @@ targets-y += $(platform_build_dir)/lib/libplatsbi.a
 endif
 targets-y += $(firmware-bins-path-y)
 
-# Default rule "make" should always be first rule
+# The default "make all" rule
 .PHONY: all
 all: $(targets-y)
 
@@ -456,7 +501,7 @@ $(build_dir)/%.o: $(src_dir)/%.S
 	$(call compile_as,$@,$<)
 
 $(build_dir)/%.dep: $(src_dir)/%.carray
-	$(call compile_gen_dep,$@,.c,$<)
+	$(call compile_gen_dep,$@,.c,$< $(KCONFIG_CONFIG))
 	$(call compile_gen_dep,$@,.o,$(@:.dep=.c))
 
 $(build_dir)/%.c: $(src_dir)/%.carray
@@ -523,6 +568,9 @@ docs: $(build_dir)/docs/latex/refman.pdf
 # They should not be included for any "xxxconfig" or "xxxclean" rule
 all-deps-1 = $(if $(findstring config,$(MAKECMDGOALS)),,$(deps-y))
 all-deps-2 = $(if $(findstring clean,$(MAKECMDGOALS)),,$(all-deps-1))
+ifdef PLATFORM
+$(all-deps-2): | $(KCONFIG_CONFIG)
+endif
 -include $(all-deps-2)
 
 # Include external dependency of firmwares after default Makefile rules
diff --git a/firmware/Kconfig b/firmware/Kconfig
new file mode 100644
index 0000000..d6e0506
--- /dev/null
+++ b/firmware/Kconfig
@@ -0,0 +1 @@
+# SPDX-License-Identifier: BSD-2-Clause
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
new file mode 100644
index 0000000..d6e0506
--- /dev/null
+++ b/lib/utils/Kconfig
@@ -0,0 +1 @@
+# SPDX-License-Identifier: BSD-2-Clause
diff --git a/platform/andes/ae350/Kconfig b/platform/andes/ae350/Kconfig
new file mode 100644
index 0000000..4b73c76
--- /dev/null
+++ b/platform/andes/ae350/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+config PLATFORM_ANDES_AE350
+	bool
+	default y
diff --git a/platform/andes/ae350/configs/defconfig b/platform/andes/ae350/configs/defconfig
new file mode 100644
index 0000000..e69de29
diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
new file mode 100644
index 0000000..70d2004
--- /dev/null
+++ b/platform/fpga/ariane/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+config PLATFORM_ARIANE_FPGA
+	bool
+	default y
diff --git a/platform/fpga/ariane/configs/defconfig b/platform/fpga/ariane/configs/defconfig
new file mode 100644
index 0000000..e69de29
diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
new file mode 100644
index 0000000..81e4b76
--- /dev/null
+++ b/platform/fpga/openpiton/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+config PLATFORM_OPENPITON_FPGA
+	bool
+	default y
diff --git a/platform/fpga/openpiton/configs/defconfig b/platform/fpga/openpiton/configs/defconfig
new file mode 100644
index 0000000..e69de29
diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
new file mode 100644
index 0000000..3eab282
--- /dev/null
+++ b/platform/generic/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+config PLATFORM_GENERIC
+	bool
+	default y
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
new file mode 100644
index 0000000..e69de29
diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
new file mode 100644
index 0000000..889f16b
--- /dev/null
+++ b/platform/kendryte/k210/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+config PLATFORM_KENDRYTE_K210
+	bool
+	default y
diff --git a/platform/kendryte/k210/configs/defconfig b/platform/kendryte/k210/configs/defconfig
new file mode 100644
index 0000000..e69de29
diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
new file mode 100644
index 0000000..c368ef4
--- /dev/null
+++ b/platform/nuclei/ux600/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+config PLATFORM_NUCLEI_UX600
+	bool
+	default y
diff --git a/platform/nuclei/ux600/configs/defconfig b/platform/nuclei/ux600/configs/defconfig
new file mode 100644
index 0000000..e69de29
diff --git a/platform/template/Kconfig b/platform/template/Kconfig
new file mode 100644
index 0000000..b9c4350
--- /dev/null
+++ b/platform/template/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+config PLATFORM_TEMPLATE
+	bool
+	default y
diff --git a/platform/template/configs/defconfig b/platform/template/configs/defconfig
new file mode 100644
index 0000000..e69de29
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 07:55:59 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 20:25:59 +0530
Subject: [PATCH v2 02/14] Makefile: Compile lib/utils sources separately for each platform
In-Reply-To: <20220718145611.2211638-1-apatel@ventanamicro.com>
References: <20220718145611.2211638-1-apatel@ventanamicro.com>
Message-ID: <20220718145611.2211638-3-apatel@ventanamicro.com>

Currently, if same build directory is used to compile two different
platforms then lib/utils objects are shared for these platforms.

We will be having platform specific configs to enable/disable drivers
in lib/utils and select compile time options for lib/utils sources.
This means lib/utils sources will now be compiled in a platform
specific way.

To tackle above, we update top-level Makefile as follows:
1) Don't create libsbiutils.a anymore because this can't be shared
   between platforms.
2) Compile lib/utils sources separately for each platform.
3) Add comments showing which make rules are for lib/sbi, lib/utils,
   firmware, and platform sources.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 Makefile                         | 48 ++++++++++++++++----------------
 docs/library_usage.md            |  6 +---
 scripts/create-binary-archive.sh |  2 +-
 3 files changed, 26 insertions(+), 30 deletions(-)

diff --git a/Makefile b/Makefile
index 46c7c1c..5fc67db 100644
--- a/Makefile
+++ b/Makefile
@@ -240,8 +240,8 @@ include $(firmware-object-mks)
 
 # Setup list of objects
 libsbi-objs-path-y=$(foreach obj,$(libsbi-objs-y),$(build_dir)/lib/sbi/$(obj))
-libsbiutils-objs-path-y=$(foreach obj,$(libsbiutils-objs-y),$(build_dir)/lib/utils/$(obj))
 ifdef PLATFORM
+libsbiutils-objs-path-y=$(foreach obj,$(libsbiutils-objs-y),$(platform_build_dir)/lib/utils/$(obj))
 platform-objs-path-y=$(foreach obj,$(platform-objs-y),$(platform_build_dir)/$(obj))
 firmware-bins-path-y=$(foreach bin,$(firmware-bins-y),$(platform_build_dir)/firmware/$(bin))
 endif
@@ -458,7 +458,6 @@ compile_gen_dep = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
 	     echo "$(1:.dep=$(2)): $(3)" >> $(1)
 
 targets-y  = $(build_dir)/lib/libsbi.a
-targets-y  += $(build_dir)/lib/libsbiutils.a
 ifdef PLATFORM
 targets-y += $(platform_build_dir)/lib/libplatsbi.a
 endif
@@ -471,12 +470,10 @@ all: $(targets-y)
 # Preserve all intermediate files
 .SECONDARY:
 
+# Rules for lib/sbi sources
 $(build_dir)/lib/libsbi.a: $(libsbi-objs-path-y)
 	$(call compile_ar,$@,$^)
 
-$(build_dir)/lib/libsbiutils.a: $(libsbi-objs-path-y) $(libsbiutils-objs-path-y)
-	$(call compile_ar,$@,$^)
-
 $(platform_build_dir)/lib/libplatsbi.a: $(libsbi-objs-path-y) $(libsbiutils-objs-path-y) $(platform-objs-path-y)
 	$(call compile_ar,$@,$^)
 
@@ -500,22 +497,14 @@ $(build_dir)/%.dep: $(src_dir)/%.S
 $(build_dir)/%.o: $(src_dir)/%.S
 	$(call compile_as,$@,$<)
 
-$(build_dir)/%.dep: $(src_dir)/%.carray
+# Rules for platform sources
+$(platform_build_dir)/%.dep: $(platform_src_dir)/%.carray
 	$(call compile_gen_dep,$@,.c,$< $(KCONFIG_CONFIG))
 	$(call compile_gen_dep,$@,.o,$(@:.dep=.c))
 
-$(build_dir)/%.c: $(src_dir)/%.carray
+$(platform_build_dir)/%.c: $(platform_src_dir)/%.carray
 	$(call compile_carray,$@,$<)
 
-$(platform_build_dir)/%.bin: $(platform_build_dir)/%.elf
-	$(call compile_objcopy,$@,$<)
-
-$(platform_build_dir)/%.elf: $(platform_build_dir)/%.o $(platform_build_dir)/%.elf.ld $(platform_build_dir)/lib/libplatsbi.a
-	$(call compile_elf,$@,$@.ld,$< $(platform_build_dir)/lib/libplatsbi.a)
-
-$(platform_build_dir)/%.ld: $(src_dir)/%.ldS
-	$(call compile_cpp,$@,$<)
-
 $(platform_build_dir)/%.dep: $(platform_src_dir)/%.c
 	$(call compile_cc_dep,$@,$<)
 
@@ -539,6 +528,23 @@ $(platform_build_dir)/%.c: $(platform_build_dir)/%.dtb
 $(platform_build_dir)/%.dtb: $(platform_src_dir)/%.dts
 	$(call compile_dts,$@,$<)
 
+# Rules for lib/utils and firmware sources
+$(platform_build_dir)/%.bin: $(platform_build_dir)/%.elf
+	$(call compile_objcopy,$@,$<)
+
+$(platform_build_dir)/%.elf: $(platform_build_dir)/%.o $(platform_build_dir)/%.elf.ld $(platform_build_dir)/lib/libplatsbi.a
+	$(call compile_elf,$@,$@.ld,$< $(platform_build_dir)/lib/libplatsbi.a)
+
+$(platform_build_dir)/%.ld: $(src_dir)/%.ldS
+	$(call compile_cpp,$@,$<)
+
+$(platform_build_dir)/%.dep: $(src_dir)/%.carray
+	$(call compile_gen_dep,$@,.c,$< $(KCONFIG_CONFIG))
+	$(call compile_gen_dep,$@,.o,$(@:.dep=.c))
+
+$(platform_build_dir)/%.c: $(src_dir)/%.carray
+	$(call compile_carray,$@,$<)
+
 $(platform_build_dir)/%.dep: $(src_dir)/%.c
 	$(call compile_cc_dep,$@,$<)
 
@@ -592,7 +598,6 @@ endif
 endif
 
 install_targets-y  = install_libsbi
-install_targets-y  += install_libsbiutils
 ifdef PLATFORM
 install_targets-y += install_libplatsbi
 install_targets-y += install_firmwares
@@ -607,17 +612,12 @@ install_libsbi: $(build_dir)/lib/libsbi.a
 	$(call inst_header_dir,$(install_root_dir)/$(install_include_path),$(include_dir)/sbi)
 	$(call inst_file,$(install_root_dir)/$(install_lib_path)/libsbi.a,$(build_dir)/lib/libsbi.a)
 
-.PHONY: install_libsbiutils
-install_libsbiutils: $(build_dir)/lib/libsbiutils.a
-	$(call inst_header_dir,$(install_root_dir)/$(install_include_path),$(include_dir)/sbi_utils)
-	$(call inst_file,$(install_root_dir)/$(install_lib_path)/libsbiutils.a,$(build_dir)/lib/libsbiutils.a)
-
 .PHONY: install_libplatsbi
-install_libplatsbi: $(platform_build_dir)/lib/libplatsbi.a $(build_dir)/lib/libsbi.a $(build_dir)/lib/libsbiutils.a
+install_libplatsbi: $(platform_build_dir)/lib/libplatsbi.a $(build_dir)/lib/libsbi.a
 	$(call inst_file,$(install_root_dir)/$(install_lib_path)/opensbi/$(platform_subdir)/lib/libplatsbi.a,$(platform_build_dir)/lib/libplatsbi.a)
 
 .PHONY: install_firmwares
-install_firmwares: $(platform_build_dir)/lib/libplatsbi.a $(build_dir)/lib/libsbi.a $(build_dir)/lib/libsbiutils.a $(firmware-bins-path-y)
+install_firmwares: $(platform_build_dir)/lib/libplatsbi.a $(build_dir)/lib/libsbi.a $(firmware-bins-path-y)
 	$(call inst_file_list,$(install_root_dir),$(build_dir),$(install_firmware_path)/$(platform_subdir)/firmware,$(firmware-elfs-path-y))
 	$(call inst_file_list,$(install_root_dir),$(build_dir),$(install_firmware_path)/$(platform_subdir)/firmware,$(firmware-bins-path-y))
 
diff --git a/docs/library_usage.md b/docs/library_usage.md
index ff99801..d5d2ba9 100644
--- a/docs/library_usage.md
+++ b/docs/library_usage.md
@@ -8,11 +8,7 @@ OpenSBI provides two types of static libraries:
    hooks for the execution of this interface must be provided by the firmware or
    bootloader linking with this library. This library is installed as
    *<install_directory>/lib/libsbi.a*
-2. *libsbiutils.a* - A static library that will contain all common code required
-   by any platform supported in OpenSBI. It will be built by default and included
-   in libplatsbi.a. This library is installed as
-   *<install_directory>/lib/libsbiutils.a*.
-3. *libplatsbi.a* - An example platform-specific static library integrating
+2. *libplatsbi.a* - An example platform-specific static library integrating
    *libsbi.a* with platform-specific hooks. This library is available only for
    the platforms supported by OpenSBI. This library is installed as
    *<install_directory>/platform/<platform_subdir>/lib/libplatsbi.a*
diff --git a/scripts/create-binary-archive.sh b/scripts/create-binary-archive.sh
index 43c5452..261a45a 100755
--- a/scripts/create-binary-archive.sh
+++ b/scripts/create-binary-archive.sh
@@ -118,7 +118,7 @@ build_opensbi() {
 	# Build and install generic library
 	echo "Build and install generic library XLEN=${BUILD_RISCV_XLEN}"
 	echo ""
-	make -C "${BUILD_OPENSBI_SOURCE_PATH}" O="${BUILD_OUTPUT_PATH}/${BUILD_NAME}" I="${BUILD_OUTPUT_PATH}/${BUILD_ARCHIVE_NAME}" PLATFORM_RISCV_XLEN="${BUILD_RISCV_XLEN}" install_libsbi install_libsbiutils -j "${BUILD_NUM_THREADS}"
+	make -C "${BUILD_OPENSBI_SOURCE_PATH}" O="${BUILD_OUTPUT_PATH}/${BUILD_NAME}" I="${BUILD_OUTPUT_PATH}/${BUILD_ARCHIVE_NAME}" PLATFORM_RISCV_XLEN="${BUILD_RISCV_XLEN}" install_libsbi -j "${BUILD_NUM_THREADS}"
 	echo ""
 
 	# Build and install relevant platforms
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 07:56:00 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 20:26:00 +0530
Subject: [PATCH v2 03/14] lib: utils/serial: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220718145611.2211638-1-apatel@ventanamicro.com>
References: <20220718145611.2211638-1-apatel@ventanamicro.com>
Message-ID: <20220718145611.2211638-4-apatel@ventanamicro.com>

We update serial drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate serial
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 include/sbi_utils/serial/fdt_serial.h |  8 +++
 lib/utils/Kconfig                     |  6 +++
 lib/utils/serial/Kconfig              | 71 +++++++++++++++++++++++++++
 lib/utils/serial/objects.mk           | 44 ++++++++---------
 platform/andes/ae350/Kconfig          |  1 +
 platform/fpga/ariane/Kconfig          |  1 +
 platform/fpga/openpiton/Kconfig       |  1 +
 platform/generic/configs/defconfig    |  8 +++
 platform/kendryte/k210/Kconfig        |  1 +
 platform/nuclei/ux600/Kconfig         |  1 +
 platform/template/Kconfig             |  1 +
 11 files changed, 121 insertions(+), 22 deletions(-)
 create mode 100644 lib/utils/serial/Kconfig

diff --git a/include/sbi_utils/serial/fdt_serial.h b/include/sbi_utils/serial/fdt_serial.h
index 6451c23..daa2e4f 100644
--- a/include/sbi_utils/serial/fdt_serial.h
+++ b/include/sbi_utils/serial/fdt_serial.h
@@ -12,6 +12,8 @@
 
 #include <sbi/sbi_types.h>
 
+#ifdef CONFIG_FDT_SERIAL
+
 struct fdt_serial {
 	const struct fdt_match *match_table;
 	int (*init)(void *fdt, int nodeoff, const struct fdt_match *match);
@@ -19,4 +21,10 @@ struct fdt_serial {
 
 int fdt_serial_init(void);
 
+#else
+
+static inline int fdt_serial_init(void) { return 0; }
+
+#endif
+
 #endif
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index d6e0506..0341fa7 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -1 +1,7 @@
 # SPDX-License-Identifier: BSD-2-Clause
+
+menu "Utils and Drivers Support"
+
+source "$OPENSBI_SRC_DIR/lib/utils/serial/Kconfig"
+
+endmenu
diff --git a/lib/utils/serial/Kconfig b/lib/utils/serial/Kconfig
new file mode 100644
index 0000000..e114f26
--- /dev/null
+++ b/lib/utils/serial/Kconfig
@@ -0,0 +1,71 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "Serial Device Support"
+
+config FDT_SERIAL
+	bool "FDT based serial drivers"
+	default n
+
+if FDT_SERIAL
+
+config FDT_SERIAL_GAISLER
+	bool "Gaisler UART FDT driver"
+	select SERIAL_GAISLER
+	default n
+
+config FDT_SERIAL_HTIF
+	bool "Host transfer interface (HTIF) UART FDT driver"
+	default n
+
+config FDT_SERIAL_SHAKTI
+	bool "Shakti UART FDT driver"
+	select SERIAL_SHAKTI
+	default n
+
+config FDT_SERIAL_SIFIVE
+	bool "SiFive UART FDT driver"
+	select SERIAL_SIFIVE
+	default n
+
+config FDT_SERIAL_LITEX
+	bool "LiteX UART FDT driver"
+	select SERIAL_LITEX
+	default n
+
+config FDT_SERIAL_UART8250
+	bool "8250 UART FDT driver"
+	select SERIAL_UART8250
+	default n
+
+config FDT_SERIAL_XILINX_UARTLITE
+	bool "Xilinx UART Lite FDT driver"
+	select SERIAL_XILINX_UARTLITE
+	default n
+
+endif
+
+config SERIAL_GAISLER
+	bool "Gaisler UART support"
+	default n
+
+config SERIAL_SHAKTI
+	bool "Shakti UART support"
+	default n
+
+config SERIAL_SIFIVE
+	bool "SiFive UART support"
+	default n
+
+config SERIAL_LITEX
+	bool "LiteX UART support"
+	default n
+
+config SERIAL_UART8250
+	bool "8250 UART support"
+	default n
+
+config SERIAL_XILINX_UARTLITE
+	bool "Xilinx UART Lite support"
+	default n
+
+endmenu
diff --git a/lib/utils/serial/objects.mk b/lib/utils/serial/objects.mk
index d26a74e..fa9f5a3 100644
--- a/lib/utils/serial/objects.mk
+++ b/lib/utils/serial/objects.mk
@@ -7,33 +7,33 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += serial/fdt_serial.o
-libsbiutils-objs-y += serial/fdt_serial_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_SERIAL) += serial/fdt_serial.o
+libsbiutils-objs-$(CONFIG_FDT_SERIAL) += serial/fdt_serial_drivers.o
 
-carray-fdt_serial_drivers-y += fdt_serial_gaisler
-libsbiutils-objs-y += serial/fdt_serial_gaisler.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_GAISLER) += fdt_serial_gaisler
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_GAISLER) += serial/fdt_serial_gaisler.o
 
-carray-fdt_serial_drivers-y += fdt_serial_htif
-libsbiutils-objs-y += serial/fdt_serial_htif.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_HTIF) += fdt_serial_htif
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_HTIF) += serial/fdt_serial_htif.o
 
-carray-fdt_serial_drivers-y += fdt_serial_shakti
-libsbiutils-objs-y += serial/fdt_serial_shakti.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_SHAKTI) += fdt_serial_shakti
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_SHAKTI) += serial/fdt_serial_shakti.o
 
-carray-fdt_serial_drivers-y += fdt_serial_sifive
-libsbiutils-objs-y += serial/fdt_serial_sifive.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_SIFIVE) += fdt_serial_sifive
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_SIFIVE) += serial/fdt_serial_sifive.o
 
-carray-fdt_serial_drivers-y += fdt_serial_litex
-libsbiutils-objs-y += serial/fdt_serial_litex.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_LITEX) += fdt_serial_litex
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_LITEX) += serial/fdt_serial_litex.o
 
-carray-fdt_serial_drivers-y += fdt_serial_uart8250
-libsbiutils-objs-y += serial/fdt_serial_uart8250.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_UART8250) += fdt_serial_uart8250
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_UART8250) += serial/fdt_serial_uart8250.o
 
-carray-fdt_serial_drivers-y += fdt_serial_xlnx_uartlite
-libsbiutils-objs-y += serial/fdt_serial_xlnx_uartlite.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_XILINX_UARTLITE) += fdt_serial_xlnx_uartlite
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_XILINX_UARTLITE) += serial/fdt_serial_xlnx_uartlite.o
 
-libsbiutils-objs-y += serial/gaisler-uart.o
-libsbiutils-objs-y += serial/shakti-uart.o
-libsbiutils-objs-y += serial/sifive-uart.o
-libsbiutils-objs-y += serial/litex-uart.o
-libsbiutils-objs-y += serial/uart8250.o
-libsbiutils-objs-y += serial/xlnx-uartlite.o
+libsbiutils-objs-$(CONFIG_SERIAL_GAISLER) += serial/gaisler-uart.o
+libsbiutils-objs-$(CONFIG_SERIAL_SHAKTI) += serial/shakti-uart.o
+libsbiutils-objs-$(CONFIG_SERIAL_SIFIVE) += serial/sifive-uart.o
+libsbiutils-objs-$(CONFIG_SERIAL_LITEX) += serial/litex-uart.o
+libsbiutils-objs-$(CONFIG_SERIAL_UART8250) += serial/uart8250.o
+libsbiutils-objs-$(CONFIG_SERIAL_XILINX_UARTLITE) += serial/xlnx-uartlite.o
diff --git a/platform/andes/ae350/Kconfig b/platform/andes/ae350/Kconfig
index 4b73c76..162d19c 100644
--- a/platform/andes/ae350/Kconfig
+++ b/platform/andes/ae350/Kconfig
@@ -2,4 +2,5 @@
 
 config PLATFORM_ANDES_AE350
 	bool
+	select SERIAL_UART8250
 	default y
diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
index 70d2004..3ce0ec1 100644
--- a/platform/fpga/ariane/Kconfig
+++ b/platform/fpga/ariane/Kconfig
@@ -2,4 +2,5 @@
 
 config PLATFORM_ARIANE_FPGA
 	bool
+	select SERIAL_UART8250
 	default y
diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
index 81e4b76..4dd752d 100644
--- a/platform/fpga/openpiton/Kconfig
+++ b/platform/fpga/openpiton/Kconfig
@@ -2,4 +2,5 @@
 
 config PLATFORM_OPENPITON_FPGA
 	bool
+	select SERIAL_UART8250
 	default y
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index e69de29..ce70c59 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -0,0 +1,8 @@
+CONFIG_FDT_SERIAL=y
+CONFIG_FDT_SERIAL_GAISLER=y
+CONFIG_FDT_SERIAL_HTIF=y
+CONFIG_FDT_SERIAL_SHAKTI=y
+CONFIG_FDT_SERIAL_SIFIVE=y
+CONFIG_FDT_SERIAL_LITEX=y
+CONFIG_FDT_SERIAL_UART8250=y
+CONFIG_FDT_SERIAL_XILINX_UARTLITE=y
diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
index 889f16b..adc7613 100644
--- a/platform/kendryte/k210/Kconfig
+++ b/platform/kendryte/k210/Kconfig
@@ -2,4 +2,5 @@
 
 config PLATFORM_KENDRYTE_K210
 	bool
+	select SERIAL_SIFIVE
 	default y
diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
index c368ef4..69fe8f8 100644
--- a/platform/nuclei/ux600/Kconfig
+++ b/platform/nuclei/ux600/Kconfig
@@ -2,4 +2,5 @@
 
 config PLATFORM_NUCLEI_UX600
 	bool
+	select SERIAL_SIFIVE
 	default y
diff --git a/platform/template/Kconfig b/platform/template/Kconfig
index b9c4350..5a5472b 100644
--- a/platform/template/Kconfig
+++ b/platform/template/Kconfig
@@ -2,4 +2,5 @@
 
 config PLATFORM_TEMPLATE
 	bool
+	select SERIAL_UART8250
 	default y
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 07:56:01 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 20:26:01 +0530
Subject: [PATCH v2 04/14] lib: utils/reset: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220718145611.2211638-1-apatel@ventanamicro.com>
References: <20220718145611.2211638-1-apatel@ventanamicro.com>
Message-ID: <20220718145611.2211638-5-apatel@ventanamicro.com>

We update reset drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate reset
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 include/sbi_utils/reset/fdt_reset.h | 12 +++++++++++
 lib/utils/Kconfig                   |  2 ++
 lib/utils/reset/Kconfig             | 33 +++++++++++++++++++++++++++++
 lib/utils/reset/objects.mk          | 28 ++++++++++++------------
 platform/generic/configs/defconfig  |  6 ++++++
 5 files changed, 67 insertions(+), 14 deletions(-)
 create mode 100644 lib/utils/reset/Kconfig

diff --git a/include/sbi_utils/reset/fdt_reset.h b/include/sbi_utils/reset/fdt_reset.h
index e7f7350..ea8063b 100644
--- a/include/sbi_utils/reset/fdt_reset.h
+++ b/include/sbi_utils/reset/fdt_reset.h
@@ -17,6 +17,8 @@ struct fdt_reset {
 	int (*init)(void *fdt, int nodeoff, const struct fdt_match *match);
 };
 
+#ifdef CONFIG_FDT_RESET
+
 /**
  * fdt_reset_driver_init() - initialize reset driver based on the device-tree
  */
@@ -29,4 +31,14 @@ int fdt_reset_driver_init(void *fdt, struct fdt_reset *drv);
  */
 void fdt_reset_init(void);
 
+#else
+
+static inline int fdt_reset_driver_init(void *fdt, struct fdt_reset *drv)
+{
+	return 0;
+}
+static inline void fdt_reset_init(void) { }
+
+#endif
+
 #endif
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 0341fa7..3a6389b 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -2,6 +2,8 @@
 
 menu "Utils and Drivers Support"
 
+source "$OPENSBI_SRC_DIR/lib/utils/reset/Kconfig"
+
 source "$OPENSBI_SRC_DIR/lib/utils/serial/Kconfig"
 
 endmenu
diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig
new file mode 100644
index 0000000..845a1ae
--- /dev/null
+++ b/lib/utils/reset/Kconfig
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "System Reset Support"
+
+config FDT_RESET
+	bool "FDT based reset drivers"
+	default n
+
+if FDT_RESET
+
+config FDT_RESET_GPIO
+	bool "GPIO FDT reset driver"
+	default n
+
+config FDT_RESET_HTIF
+	bool "Host transfer interface (HTIF) FDT reset driver"
+	default n
+
+config FDT_RESET_SIFIVE_TEST
+	bool "SiFive Test FDT reset driver"
+	default n
+
+config FDT_RESET_SUNXI_WDT
+	bool "Sunxi WDT FDT reset driver"
+	default n
+
+config FDT_RESET_THEAD
+	bool "T-HEAD FDT reset driver"
+	default n
+
+endif
+
+endmenu
diff --git a/lib/utils/reset/objects.mk b/lib/utils/reset/objects.mk
index 8cddcdf..8a50dd0 100644
--- a/lib/utils/reset/objects.mk
+++ b/lib/utils/reset/objects.mk
@@ -7,22 +7,22 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += reset/fdt_reset.o
-libsbiutils-objs-y += reset/fdt_reset_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_RESET) += reset/fdt_reset.o
+libsbiutils-objs-$(CONFIG_FDT_RESET) += reset/fdt_reset_drivers.o
 
-carray-fdt_reset_drivers-y += fdt_poweroff_gpio
-carray-fdt_reset_drivers-y += fdt_reset_gpio
-libsbiutils-objs-y += reset/fdt_reset_gpio.o
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_GPIO) += fdt_poweroff_gpio
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_GPIO) += fdt_reset_gpio
+libsbiutils-objs-$(CONFIG_FDT_RESET_GPIO) += reset/fdt_reset_gpio.o
 
-carray-fdt_reset_drivers-y += fdt_reset_htif
-libsbiutils-objs-y += reset/fdt_reset_htif.o
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_HTIF) += fdt_reset_htif
+libsbiutils-objs-$(CONFIG_FDT_RESET_HTIF) += reset/fdt_reset_htif.o
 
-carray-fdt_reset_drivers-y += fdt_reset_sifive_test
-libsbiutils-objs-y += reset/fdt_reset_sifive_test.o
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_SIFIVE_TEST) += fdt_reset_sifive_test
+libsbiutils-objs-$(CONFIG_FDT_RESET_SIFIVE_TEST) += reset/fdt_reset_sifive_test.o
 
-carray-fdt_reset_drivers-y += fdt_reset_sunxi_wdt
-libsbiutils-objs-y += reset/fdt_reset_sunxi_wdt.o
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_SUNXI_WDT) += fdt_reset_sunxi_wdt
+libsbiutils-objs-$(CONFIG_FDT_RESET_SUNXI_WDT) += reset/fdt_reset_sunxi_wdt.o
 
-carray-fdt_reset_drivers-y += fdt_reset_thead
-libsbiutils-objs-y += reset/fdt_reset_thead.o
-libsbiutils-objs-y += reset/fdt_reset_thead_asm.o
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_THEAD) += fdt_reset_thead
+libsbiutils-objs-$(CONFIG_FDT_RESET_THEAD) += reset/fdt_reset_thead.o
+libsbiutils-objs-$(CONFIG_FDT_RESET_THEAD) += reset/fdt_reset_thead_asm.o
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index ce70c59..9778665 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -1,3 +1,9 @@
+CONFIG_FDT_RESET=y
+CONFIG_FDT_RESET_GPIO=y
+CONFIG_FDT_RESET_HTIF=y
+CONFIG_FDT_RESET_SIFIVE_TEST=y
+CONFIG_FDT_RESET_SUNXI_WDT=y
+CONFIG_FDT_RESET_THEAD=y
 CONFIG_FDT_SERIAL=y
 CONFIG_FDT_SERIAL_GAISLER=y
 CONFIG_FDT_SERIAL_HTIF=y
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 07:56:02 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 20:26:02 +0530
Subject: [PATCH v2 05/14] lib: utils/sys: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220718145611.2211638-1-apatel@ventanamicro.com>
References: <20220718145611.2211638-1-apatel@ventanamicro.com>
Message-ID: <20220718145611.2211638-6-apatel@ventanamicro.com>

We update system drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate system
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 lib/utils/Kconfig        |  2 ++
 lib/utils/reset/Kconfig  |  2 ++
 lib/utils/serial/Kconfig |  1 +
 lib/utils/sys/Kconfig    | 13 +++++++++++++
 lib/utils/sys/objects.mk |  4 ++--
 5 files changed, 20 insertions(+), 2 deletions(-)
 create mode 100644 lib/utils/sys/Kconfig

diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 3a6389b..97e73bb 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -6,4 +6,6 @@ source "$OPENSBI_SRC_DIR/lib/utils/reset/Kconfig"
 
 source "$OPENSBI_SRC_DIR/lib/utils/serial/Kconfig"
 
+source "$OPENSBI_SRC_DIR/lib/utils/sys/Kconfig"
+
 endmenu
diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig
index 845a1ae..e7a71e3 100644
--- a/lib/utils/reset/Kconfig
+++ b/lib/utils/reset/Kconfig
@@ -14,10 +14,12 @@ config FDT_RESET_GPIO
 
 config FDT_RESET_HTIF
 	bool "Host transfer interface (HTIF) FDT reset driver"
+	select SYS_HTIF
 	default n
 
 config FDT_RESET_SIFIVE_TEST
 	bool "SiFive Test FDT reset driver"
+	select SYS_SIFIVE_TEST
 	default n
 
 config FDT_RESET_SUNXI_WDT
diff --git a/lib/utils/serial/Kconfig b/lib/utils/serial/Kconfig
index e114f26..bf37e34 100644
--- a/lib/utils/serial/Kconfig
+++ b/lib/utils/serial/Kconfig
@@ -15,6 +15,7 @@ config FDT_SERIAL_GAISLER
 
 config FDT_SERIAL_HTIF
 	bool "Host transfer interface (HTIF) UART FDT driver"
+	select SYS_HTIF
 	default n
 
 config FDT_SERIAL_SHAKTI
diff --git a/lib/utils/sys/Kconfig b/lib/utils/sys/Kconfig
new file mode 100644
index 0000000..ee85b1a
--- /dev/null
+++ b/lib/utils/sys/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "System Device Support"
+
+config SYS_HTIF
+	bool "Host transfere interface (HTIF) support"
+	default n
+
+config SYS_SIFIVE_TEST
+	bool "SiFive test support"
+	default n
+
+endmenu
diff --git a/lib/utils/sys/objects.mk b/lib/utils/sys/objects.mk
index 06be322..9f67aee 100644
--- a/lib/utils/sys/objects.mk
+++ b/lib/utils/sys/objects.mk
@@ -7,5 +7,5 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += sys/htif.o
-libsbiutils-objs-y += sys/sifive_test.o
+libsbiutils-objs-$(CONFIG_SYS_HTIF) += sys/htif.o
+libsbiutils-objs-$(CONFIG_SYS_SIFIVE_TEST) += sys/sifive_test.o
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 07:56:03 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 20:26:03 +0530
Subject: [PATCH v2 06/14] lib: utils/timer: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220718145611.2211638-1-apatel@ventanamicro.com>
References: <20220718145611.2211638-1-apatel@ventanamicro.com>
Message-ID: <20220718145611.2211638-7-apatel@ventanamicro.com>

We update timer drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate timer
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 include/sbi_utils/timer/fdt_timer.h |  9 +++++++++
 lib/utils/Kconfig                   |  2 ++
 lib/utils/timer/Kconfig             | 22 ++++++++++++++++++++++
 lib/utils/timer/objects.mk          | 10 +++++-----
 platform/fpga/ariane/Kconfig        |  1 +
 platform/fpga/openpiton/Kconfig     |  1 +
 platform/generic/configs/defconfig  |  2 ++
 platform/kendryte/k210/Kconfig      |  1 +
 platform/nuclei/ux600/Kconfig       |  1 +
 platform/template/Kconfig           |  1 +
 10 files changed, 45 insertions(+), 5 deletions(-)
 create mode 100644 lib/utils/timer/Kconfig

diff --git a/include/sbi_utils/timer/fdt_timer.h b/include/sbi_utils/timer/fdt_timer.h
index 36202a4..cf105fe 100644
--- a/include/sbi_utils/timer/fdt_timer.h
+++ b/include/sbi_utils/timer/fdt_timer.h
@@ -12,6 +12,8 @@
 
 #include <sbi/sbi_types.h>
 
+#ifdef CONFIG_FDT_TIMER
+
 struct fdt_timer {
 	const struct fdt_match *match_table;
 	int (*cold_init)(void *fdt, int nodeoff, const struct fdt_match *match);
@@ -23,4 +25,11 @@ void fdt_timer_exit(void);
 
 int fdt_timer_init(bool cold_boot);
 
+#else
+
+static inline void fdt_timer_exit(void) { }
+static inline int fdt_timer_init(bool cold_boot) { return 0; }
+
+#endif
+
 #endif
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 97e73bb..5798a5c 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -8,4 +8,6 @@ source "$OPENSBI_SRC_DIR/lib/utils/serial/Kconfig"
 
 source "$OPENSBI_SRC_DIR/lib/utils/sys/Kconfig"
 
+source "$OPENSBI_SRC_DIR/lib/utils/timer/Kconfig"
+
 endmenu
diff --git a/lib/utils/timer/Kconfig b/lib/utils/timer/Kconfig
new file mode 100644
index 0000000..0a1c727
--- /dev/null
+++ b/lib/utils/timer/Kconfig
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "Timer Device Support"
+
+config FDT_TIMER
+	bool "FDT based timer drivers"
+	default n
+
+if FDT_TIMER
+
+config FDT_TIMER_MTIMER
+	bool "ACLINT MTIMER FDT driver"
+	select TIMER_MTIMER
+	default n
+
+endif
+
+config TIMER_MTIMER
+	bool "ACLINT MTIMER support"
+	default n
+
+endmenu
diff --git a/lib/utils/timer/objects.mk b/lib/utils/timer/objects.mk
index bc4073d..7f5f3ce 100644
--- a/lib/utils/timer/objects.mk
+++ b/lib/utils/timer/objects.mk
@@ -7,10 +7,10 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += timer/aclint_mtimer.o
+libsbiutils-objs-$(CONFIG_TIMER_MTIMER) += timer/aclint_mtimer.o
 
-libsbiutils-objs-y += timer/fdt_timer.o
-libsbiutils-objs-y += timer/fdt_timer_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_TIMER) += timer/fdt_timer.o
+libsbiutils-objs-$(CONFIG_FDT_TIMER) += timer/fdt_timer_drivers.o
 
-carray-fdt_timer_drivers-y += fdt_timer_mtimer
-libsbiutils-objs-y += timer/fdt_timer_mtimer.o
+carray-fdt_timer_drivers-$(CONFIG_FDT_TIMER_MTIMER) += fdt_timer_mtimer
+libsbiutils-objs-$(CONFIG_FDT_TIMER_MTIMER) += timer/fdt_timer_mtimer.o
diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
index 3ce0ec1..a7fd758 100644
--- a/platform/fpga/ariane/Kconfig
+++ b/platform/fpga/ariane/Kconfig
@@ -3,4 +3,5 @@
 config PLATFORM_ARIANE_FPGA
 	bool
 	select SERIAL_UART8250
+	select TIMER_MTIMER
 	default y
diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
index 4dd752d..a7f09e4 100644
--- a/platform/fpga/openpiton/Kconfig
+++ b/platform/fpga/openpiton/Kconfig
@@ -3,4 +3,5 @@
 config PLATFORM_OPENPITON_FPGA
 	bool
 	select SERIAL_UART8250
+	select TIMER_MTIMER
 	default y
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index 9778665..9b96a4f 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -12,3 +12,5 @@ CONFIG_FDT_SERIAL_SIFIVE=y
 CONFIG_FDT_SERIAL_LITEX=y
 CONFIG_FDT_SERIAL_UART8250=y
 CONFIG_FDT_SERIAL_XILINX_UARTLITE=y
+CONFIG_FDT_TIMER=y
+CONFIG_FDT_TIMER_MTIMER=y
diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
index adc7613..90db649 100644
--- a/platform/kendryte/k210/Kconfig
+++ b/platform/kendryte/k210/Kconfig
@@ -3,4 +3,5 @@
 config PLATFORM_KENDRYTE_K210
 	bool
 	select SERIAL_SIFIVE
+	select TIMER_MTIMER
 	default y
diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
index 69fe8f8..dec2654 100644
--- a/platform/nuclei/ux600/Kconfig
+++ b/platform/nuclei/ux600/Kconfig
@@ -3,4 +3,5 @@
 config PLATFORM_NUCLEI_UX600
 	bool
 	select SERIAL_SIFIVE
+	select TIMER_MTIMER
 	default y
diff --git a/platform/template/Kconfig b/platform/template/Kconfig
index 5a5472b..ce463ea 100644
--- a/platform/template/Kconfig
+++ b/platform/template/Kconfig
@@ -3,4 +3,5 @@
 config PLATFORM_TEMPLATE
 	bool
 	select SERIAL_UART8250
+	select TIMER_MTIMER
 	default y
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 07:56:04 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 20:26:04 +0530
Subject: [PATCH v2 07/14] lib: utils/ipi: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220718145611.2211638-1-apatel@ventanamicro.com>
References: <20220718145611.2211638-1-apatel@ventanamicro.com>
Message-ID: <20220718145611.2211638-8-apatel@ventanamicro.com>

We update ipi drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate ipi
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 include/sbi_utils/ipi/fdt_ipi.h    |  9 +++++++++
 lib/utils/Kconfig                  |  2 ++
 lib/utils/ipi/Kconfig              | 22 ++++++++++++++++++++++
 lib/utils/ipi/objects.mk           | 10 +++++-----
 platform/fpga/ariane/Kconfig       |  1 +
 platform/fpga/openpiton/Kconfig    |  1 +
 platform/generic/configs/defconfig |  2 ++
 platform/kendryte/k210/Kconfig     |  1 +
 platform/nuclei/ux600/Kconfig      |  1 +
 platform/template/Kconfig          |  1 +
 10 files changed, 45 insertions(+), 5 deletions(-)
 create mode 100644 lib/utils/ipi/Kconfig

diff --git a/include/sbi_utils/ipi/fdt_ipi.h b/include/sbi_utils/ipi/fdt_ipi.h
index 9337353..1dd9062 100644
--- a/include/sbi_utils/ipi/fdt_ipi.h
+++ b/include/sbi_utils/ipi/fdt_ipi.h
@@ -12,6 +12,8 @@
 
 #include <sbi/sbi_types.h>
 
+#ifdef CONFIG_FDT_IPI
+
 struct fdt_ipi {
 	const struct fdt_match *match_table;
 	int (*cold_init)(void *fdt, int nodeoff, const struct fdt_match *match);
@@ -23,4 +25,11 @@ void fdt_ipi_exit(void);
 
 int fdt_ipi_init(bool cold_boot);
 
+#else
+
+static inline void fdt_ipi_exit(void) { }
+static inline int fdt_ipi_init(bool cold_boot) { return 0; }
+
+#endif
+
 #endif
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 5798a5c..43e9547 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -2,6 +2,8 @@
 
 menu "Utils and Drivers Support"
 
+source "$OPENSBI_SRC_DIR/lib/utils/ipi/Kconfig"
+
 source "$OPENSBI_SRC_DIR/lib/utils/reset/Kconfig"
 
 source "$OPENSBI_SRC_DIR/lib/utils/serial/Kconfig"
diff --git a/lib/utils/ipi/Kconfig b/lib/utils/ipi/Kconfig
new file mode 100644
index 0000000..c0634d3
--- /dev/null
+++ b/lib/utils/ipi/Kconfig
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "IPI Device Support"
+
+config FDT_IPI
+	bool "FDT based ipi drivers"
+	default n
+
+if FDT_IPI
+
+config FDT_IPI_MSWI
+	bool "ACLINT MSWI FDT driver"
+	select IPI_MSWI
+	default n
+
+endif
+
+config IPI_MSWI
+	bool "ACLINT MSWI support"
+	default n
+
+endmenu
diff --git a/lib/utils/ipi/objects.mk b/lib/utils/ipi/objects.mk
index 0b0bc2d..0600cac 100644
--- a/lib/utils/ipi/objects.mk
+++ b/lib/utils/ipi/objects.mk
@@ -7,10 +7,10 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += ipi/aclint_mswi.o
+libsbiutils-objs-$(CONFIG_IPI_MSWI) += ipi/aclint_mswi.o
 
-libsbiutils-objs-y += ipi/fdt_ipi.o
-libsbiutils-objs-y += ipi/fdt_ipi_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_IPI) += ipi/fdt_ipi.o
+libsbiutils-objs-$(CONFIG_FDT_IPI) += ipi/fdt_ipi_drivers.o
 
-carray-fdt_ipi_drivers-y += fdt_ipi_mswi
-libsbiutils-objs-y += ipi/fdt_ipi_mswi.o
+carray-fdt_ipi_drivers-$(CONFIG_FDT_IPI_MSWI) += fdt_ipi_mswi
+libsbiutils-objs-$(CONFIG_FDT_IPI_MSWI) += ipi/fdt_ipi_mswi.o
diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
index a7fd758..0bc1597 100644
--- a/platform/fpga/ariane/Kconfig
+++ b/platform/fpga/ariane/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_ARIANE_FPGA
 	bool
+	select IPI_MSWI
 	select SERIAL_UART8250
 	select TIMER_MTIMER
 	default y
diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
index a7f09e4..0e4e3ba 100644
--- a/platform/fpga/openpiton/Kconfig
+++ b/platform/fpga/openpiton/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_OPENPITON_FPGA
 	bool
+	select IPI_MSWI
 	select SERIAL_UART8250
 	select TIMER_MTIMER
 	default y
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index 9b96a4f..e218c32 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -1,3 +1,5 @@
+CONFIG_FDT_IPI=y
+CONFIG_FDT_IPI_MSWI=y
 CONFIG_FDT_RESET=y
 CONFIG_FDT_RESET_GPIO=y
 CONFIG_FDT_RESET_HTIF=y
diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
index 90db649..685a5cd 100644
--- a/platform/kendryte/k210/Kconfig
+++ b/platform/kendryte/k210/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_KENDRYTE_K210
 	bool
+	select IPI_MSWI
 	select SERIAL_SIFIVE
 	select TIMER_MTIMER
 	default y
diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
index dec2654..cd995a3 100644
--- a/platform/nuclei/ux600/Kconfig
+++ b/platform/nuclei/ux600/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_NUCLEI_UX600
 	bool
+	select IPI_MSWI
 	select SERIAL_SIFIVE
 	select TIMER_MTIMER
 	default y
diff --git a/platform/template/Kconfig b/platform/template/Kconfig
index ce463ea..672b6e7 100644
--- a/platform/template/Kconfig
+++ b/platform/template/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_TEMPLATE
 	bool
+	select IPI_MSWI
 	select SERIAL_UART8250
 	select TIMER_MTIMER
 	default y
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 07:56:05 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 20:26:05 +0530
Subject: [PATCH v2 08/14] lib: utils/irqchip: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220718145611.2211638-1-apatel@ventanamicro.com>
References: <20220718145611.2211638-1-apatel@ventanamicro.com>
Message-ID: <20220718145611.2211638-9-apatel@ventanamicro.com>

We update irqchip drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate irqchip
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 include/sbi_utils/irqchip/fdt_irqchip.h | 10 +++++++
 include/sbi_utils/irqchip/imsic.h       | 10 +++++++
 lib/utils/Kconfig                       |  2 ++
 lib/utils/irqchip/Kconfig               | 40 +++++++++++++++++++++++++
 lib/utils/irqchip/objects.mk            | 22 +++++++-------
 platform/andes/ae350/Kconfig            |  1 +
 platform/fpga/ariane/Kconfig            |  1 +
 platform/fpga/openpiton/Kconfig         |  1 +
 platform/generic/configs/defconfig      |  4 +++
 platform/kendryte/k210/Kconfig          |  1 +
 platform/nuclei/ux600/Kconfig           |  1 +
 platform/template/Kconfig               |  1 +
 12 files changed, 83 insertions(+), 11 deletions(-)
 create mode 100644 lib/utils/irqchip/Kconfig

diff --git a/include/sbi_utils/irqchip/fdt_irqchip.h b/include/sbi_utils/irqchip/fdt_irqchip.h
index 13ef6f7..63e9fd9 100644
--- a/include/sbi_utils/irqchip/fdt_irqchip.h
+++ b/include/sbi_utils/irqchip/fdt_irqchip.h
@@ -12,6 +12,8 @@
 
 #include <sbi/sbi_types.h>
 
+#ifdef CONFIG_FDT_IRQCHIP
+
 struct fdt_irqchip {
 	const struct fdt_match *match_table;
 	int (*cold_init)(void *fdt, int nodeoff, const struct fdt_match *match);
@@ -23,4 +25,12 @@ void fdt_irqchip_exit(void);
 
 int fdt_irqchip_init(bool cold_boot);
 
+#else
+
+static inline void fdt_irqchip_exit(void) { }
+
+static inline int fdt_irqchip_init(bool cold_boot) { return 0; }
+
+#endif
+
 #endif
diff --git a/include/sbi_utils/irqchip/imsic.h b/include/sbi_utils/irqchip/imsic.h
index cffcb5a..e295771 100644
--- a/include/sbi_utils/irqchip/imsic.h
+++ b/include/sbi_utils/irqchip/imsic.h
@@ -33,6 +33,8 @@ struct imsic_data {
 	struct imsic_regs regs[IMSIC_MAX_REGS];
 };
 
+#ifdef CONFIG_IRQCHIP_IMSIC
+
 int imsic_map_hartid_to_data(u32 hartid, struct imsic_data *imsic, int file);
 
 struct imsic_data *imsic_get_data(u32 hartid);
@@ -47,4 +49,12 @@ int imsic_data_check(struct imsic_data *imsic);
 
 int imsic_cold_irqchip_init(struct imsic_data *imsic);
 
+#else
+
+static inline void imsic_local_irqchip_init(void) { }
+
+static inline int imsic_data_check(struct imsic_data *imsic) { return 0; }
+
+#endif
+
 #endif
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 43e9547..18e06d5 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -4,6 +4,8 @@ menu "Utils and Drivers Support"
 
 source "$OPENSBI_SRC_DIR/lib/utils/ipi/Kconfig"
 
+source "$OPENSBI_SRC_DIR/lib/utils/irqchip/Kconfig"
+
 source "$OPENSBI_SRC_DIR/lib/utils/reset/Kconfig"
 
 source "$OPENSBI_SRC_DIR/lib/utils/serial/Kconfig"
diff --git a/lib/utils/irqchip/Kconfig b/lib/utils/irqchip/Kconfig
new file mode 100644
index 0000000..6a7e297
--- /dev/null
+++ b/lib/utils/irqchip/Kconfig
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "Interrupt Controller Support"
+
+config FDT_IRQCHIP
+	bool "FDT based interrupt controller drivers"
+	default n
+
+if FDT_IRQCHIP
+
+config FDT_IRQCHIP_APLIC
+	bool "Advanced Platform Level Interrupt Controller (APLIC) FDT driver"
+	select IRQCHIP_APLIC
+	default n
+
+config FDT_IRQCHIP_IMSIC
+	bool "Incoming Message Signalled Interrupt Controller (IMSIC) FDT driver"
+	select IRQCHIP_IMSIC
+	default n
+
+config FDT_IRQCHIP_PLIC
+	bool "Platform Level Interrupt Controller (PLIC) FDT driver"
+	select IRQCHIP_PLIC
+	default n
+
+endif
+
+config IRQCHIP_APLIC
+	bool "Advanced Platform Level Interrupt Controller (APLIC) support"
+	default n
+
+config IRQCHIP_IMSIC
+	bool "Incoming Message Signalled Interrupt Controller (IMSIC) support"
+	default n
+
+config IRQCHIP_PLIC
+	bool "Platform Level Interrupt Controller (PLIC) support"
+	default n
+
+endmenu
diff --git a/lib/utils/irqchip/objects.mk b/lib/utils/irqchip/objects.mk
index 7775bc4..e458891 100644
--- a/lib/utils/irqchip/objects.mk
+++ b/lib/utils/irqchip/objects.mk
@@ -7,18 +7,18 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += irqchip/fdt_irqchip.o
-libsbiutils-objs-y += irqchip/fdt_irqchip_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_IRQCHIP) += irqchip/fdt_irqchip.o
+libsbiutils-objs-$(CONFIG_FDT_IRQCHIP) += irqchip/fdt_irqchip_drivers.o
 
-carray-fdt_irqchip_drivers-y += fdt_irqchip_aplic
-libsbiutils-objs-y += irqchip/fdt_irqchip_aplic.o
+carray-fdt_irqchip_drivers-$(CONFIG_FDT_IRQCHIP_APLIC) += fdt_irqchip_aplic
+libsbiutils-objs-$(CONFIG_FDT_IRQCHIP_APLIC) += irqchip/fdt_irqchip_aplic.o
 
-carray-fdt_irqchip_drivers-y += fdt_irqchip_imsic
-libsbiutils-objs-y += irqchip/fdt_irqchip_imsic.o
+carray-fdt_irqchip_drivers-$(CONFIG_FDT_IRQCHIP_IMSIC) += fdt_irqchip_imsic
+libsbiutils-objs-$(CONFIG_FDT_IRQCHIP_IMSIC) += irqchip/fdt_irqchip_imsic.o
 
-carray-fdt_irqchip_drivers-y += fdt_irqchip_plic
-libsbiutils-objs-y += irqchip/fdt_irqchip_plic.o
+carray-fdt_irqchip_drivers-$(CONFIG_FDT_IRQCHIP_PLIC) += fdt_irqchip_plic
+libsbiutils-objs-$(CONFIG_FDT_IRQCHIP_PLIC) += irqchip/fdt_irqchip_plic.o
 
-libsbiutils-objs-y += irqchip/aplic.o
-libsbiutils-objs-y += irqchip/imsic.o
-libsbiutils-objs-y += irqchip/plic.o
+libsbiutils-objs-$(CONFIG_IRQCHIP_APLIC) += irqchip/aplic.o
+libsbiutils-objs-$(CONFIG_IRQCHIP_IMSIC) += irqchip/imsic.o
+libsbiutils-objs-$(CONFIG_IRQCHIP_PLIC) += irqchip/plic.o
diff --git a/platform/andes/ae350/Kconfig b/platform/andes/ae350/Kconfig
index 162d19c..87deab0 100644
--- a/platform/andes/ae350/Kconfig
+++ b/platform/andes/ae350/Kconfig
@@ -2,5 +2,6 @@
 
 config PLATFORM_ANDES_AE350
 	bool
+	select IRQCHIP_PLIC
 	select SERIAL_UART8250
 	default y
diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
index 0bc1597..5a720d9 100644
--- a/platform/fpga/ariane/Kconfig
+++ b/platform/fpga/ariane/Kconfig
@@ -3,6 +3,7 @@
 config PLATFORM_ARIANE_FPGA
 	bool
 	select IPI_MSWI
+	select IRQCHIP_PLIC
 	select SERIAL_UART8250
 	select TIMER_MTIMER
 	default y
diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
index 0e4e3ba..973906e 100644
--- a/platform/fpga/openpiton/Kconfig
+++ b/platform/fpga/openpiton/Kconfig
@@ -3,6 +3,7 @@
 config PLATFORM_OPENPITON_FPGA
 	bool
 	select IPI_MSWI
+	select IRQCHIP_PLIC
 	select SERIAL_UART8250
 	select TIMER_MTIMER
 	default y
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index e218c32..ad66a63 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -1,5 +1,9 @@
 CONFIG_FDT_IPI=y
 CONFIG_FDT_IPI_MSWI=y
+CONFIG_FDT_IRQCHIP=y
+CONFIG_FDT_IRQCHIP_APLIC=y
+CONFIG_FDT_IRQCHIP_IMSIC=y
+CONFIG_FDT_IRQCHIP_PLIC=y
 CONFIG_FDT_RESET=y
 CONFIG_FDT_RESET_GPIO=y
 CONFIG_FDT_RESET_HTIF=y
diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
index 685a5cd..2a1daaa 100644
--- a/platform/kendryte/k210/Kconfig
+++ b/platform/kendryte/k210/Kconfig
@@ -3,6 +3,7 @@
 config PLATFORM_KENDRYTE_K210
 	bool
 	select IPI_MSWI
+	select IRQCHIP_PLIC
 	select SERIAL_SIFIVE
 	select TIMER_MTIMER
 	default y
diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
index cd995a3..464802e 100644
--- a/platform/nuclei/ux600/Kconfig
+++ b/platform/nuclei/ux600/Kconfig
@@ -3,6 +3,7 @@
 config PLATFORM_NUCLEI_UX600
 	bool
 	select IPI_MSWI
+	select IRQCHIP_PLIC
 	select SERIAL_SIFIVE
 	select TIMER_MTIMER
 	default y
diff --git a/platform/template/Kconfig b/platform/template/Kconfig
index 672b6e7..8338425 100644
--- a/platform/template/Kconfig
+++ b/platform/template/Kconfig
@@ -3,6 +3,7 @@
 config PLATFORM_TEMPLATE
 	bool
 	select IPI_MSWI
+	select IRQCHIP_PLIC
 	select SERIAL_UART8250
 	select TIMER_MTIMER
 	default y
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 07:56:06 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 20:26:06 +0530
Subject: [PATCH v2 09/14] lib: utils/i2c: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220718145611.2211638-1-apatel@ventanamicro.com>
References: <20220718145611.2211638-1-apatel@ventanamicro.com>
Message-ID: <20220718145611.2211638-10-apatel@ventanamicro.com>

We update i2c drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate i2c
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 lib/utils/Kconfig                  |  2 ++
 lib/utils/i2c/Kconfig              | 22 ++++++++++++++++++++++
 lib/utils/i2c/objects.mk           | 10 +++++-----
 platform/generic/configs/defconfig |  2 ++
 4 files changed, 31 insertions(+), 5 deletions(-)
 create mode 100644 lib/utils/i2c/Kconfig

diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 18e06d5..52f28da 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -2,6 +2,8 @@
 
 menu "Utils and Drivers Support"
 
+source "$OPENSBI_SRC_DIR/lib/utils/i2c/Kconfig"
+
 source "$OPENSBI_SRC_DIR/lib/utils/ipi/Kconfig"
 
 source "$OPENSBI_SRC_DIR/lib/utils/irqchip/Kconfig"
diff --git a/lib/utils/i2c/Kconfig b/lib/utils/i2c/Kconfig
new file mode 100644
index 0000000..16c4453
--- /dev/null
+++ b/lib/utils/i2c/Kconfig
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "I2C Support"
+
+config FDT_I2C
+	bool "FDT based I2C drivers"
+	select I2C
+	default n
+
+if FDT_I2C
+
+config FDT_I2C_SIFIVE
+	bool "SiFive I2C FDT driver"
+	default n
+
+endif
+
+config I2C
+	bool "I2C support"
+	default n
+
+endmenu
diff --git a/lib/utils/i2c/objects.mk b/lib/utils/i2c/objects.mk
index bb0d6d5..a0fbbb5 100644
--- a/lib/utils/i2c/objects.mk
+++ b/lib/utils/i2c/objects.mk
@@ -7,10 +7,10 @@
 #   Nikita Shubin <n.shubin at yadro.com>
 #
 
-libsbiutils-objs-y += i2c/i2c.o
+libsbiutils-objs-$(CONFIG_I2C) += i2c/i2c.o
 
-libsbiutils-objs-y += i2c/fdt_i2c.o
-libsbiutils-objs-y += i2c/fdt_i2c_adapter_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_I2C) += i2c/fdt_i2c.o
+libsbiutils-objs-$(CONFIG_FDT_I2C) += i2c/fdt_i2c_adapter_drivers.o
 
-carray-fdt_i2c_adapter_drivers-y += fdt_i2c_adapter_sifive
-libsbiutils-objs-y += i2c/fdt_i2c_sifive.o
+carray-fdt_i2c_adapter_drivers-$(CONFIG_FDT_I2C_SIFIVE) += fdt_i2c_adapter_sifive
+libsbiutils-objs-$(CONFIG_FDT_I2C_SIFIVE) += i2c/fdt_i2c_sifive.o
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index ad66a63..c9c221e 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -1,3 +1,5 @@
+CONFIG_FDT_I2C=y
+CONFIG_FDT_I2C_SIFIVE=y
 CONFIG_FDT_IPI=y
 CONFIG_FDT_IPI_MSWI=y
 CONFIG_FDT_IRQCHIP=y
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 07:56:07 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 20:26:07 +0530
Subject: [PATCH v2 10/14] lib: utils/gpio: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220718145611.2211638-1-apatel@ventanamicro.com>
References: <20220718145611.2211638-1-apatel@ventanamicro.com>
Message-ID: <20220718145611.2211638-11-apatel@ventanamicro.com>

We update gpio drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate gpio
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 lib/utils/Kconfig                  |  2 ++
 lib/utils/gpio/Kconfig             | 22 ++++++++++++++++++++++
 lib/utils/gpio/objects.mk          | 10 +++++-----
 lib/utils/reset/Kconfig            |  1 +
 platform/generic/configs/defconfig |  2 ++
 5 files changed, 32 insertions(+), 5 deletions(-)
 create mode 100644 lib/utils/gpio/Kconfig

diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 52f28da..79b9c2c 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -2,6 +2,8 @@
 
 menu "Utils and Drivers Support"
 
+source "$OPENSBI_SRC_DIR/lib/utils/gpio/Kconfig"
+
 source "$OPENSBI_SRC_DIR/lib/utils/i2c/Kconfig"
 
 source "$OPENSBI_SRC_DIR/lib/utils/ipi/Kconfig"
diff --git a/lib/utils/gpio/Kconfig b/lib/utils/gpio/Kconfig
new file mode 100644
index 0000000..daf7f91
--- /dev/null
+++ b/lib/utils/gpio/Kconfig
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "GPIO Support"
+
+config FDT_GPIO
+	bool "FDT based GPIO drivers"
+	select GPIO
+	default n
+
+if FDT_GPIO
+
+config FDT_GPIO_SIFIVE
+	bool "SiFive GPIO FDT driver"
+	default n
+
+endif
+
+config GPIO
+	bool "GPIO support"
+	default n
+
+endmenu
diff --git a/lib/utils/gpio/objects.mk b/lib/utils/gpio/objects.mk
index a5e131b..eedd699 100644
--- a/lib/utils/gpio/objects.mk
+++ b/lib/utils/gpio/objects.mk
@@ -7,10 +7,10 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += gpio/fdt_gpio.o
-libsbiutils-objs-y += gpio/fdt_gpio_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_GPIO) += gpio/fdt_gpio.o
+libsbiutils-objs-$(CONFIG_FDT_GPIO) += gpio/fdt_gpio_drivers.o
 
-carray-fdt_gpio_drivers-y += fdt_gpio_sifive
-libsbiutils-objs-y += gpio/fdt_gpio_sifive.o
+carray-fdt_gpio_drivers-$(CONFIG_FDT_GPIO_SIFIVE) += fdt_gpio_sifive
+libsbiutils-objs-$(CONFIG_FDT_GPIO_SIFIVE) += gpio/fdt_gpio_sifive.o
 
-libsbiutils-objs-y += gpio/gpio.o
+libsbiutils-objs-$(CONFIG_GPIO) += gpio/gpio.o
diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig
index e7a71e3..71996cb 100644
--- a/lib/utils/reset/Kconfig
+++ b/lib/utils/reset/Kconfig
@@ -10,6 +10,7 @@ if FDT_RESET
 
 config FDT_RESET_GPIO
 	bool "GPIO FDT reset driver"
+	depends on FDT_GPIO
 	default n
 
 config FDT_RESET_HTIF
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index c9c221e..d56cef2 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -1,3 +1,5 @@
+CONFIG_FDT_GPIO=y
+CONFIG_FDT_GPIO_SIFIVE=y
 CONFIG_FDT_I2C=y
 CONFIG_FDT_I2C_SIFIVE=y
 CONFIG_FDT_IPI=y
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 07:56:09 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 20:26:09 +0530
Subject: [PATCH v2 12/14] platform: generic: Use kconfig for enabling/disabling overrides
In-Reply-To: <20220718145611.2211638-1-apatel@ventanamicro.com>
References: <20220718145611.2211638-1-apatel@ventanamicro.com>
Message-ID: <20220718145611.2211638-13-apatel@ventanamicro.com>

We update generic platform to use kconfig for enabling/disabling
platform overrides. We also enable all platform overrides in generic
platform defconfig.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 platform/generic/Kconfig              | 18 ++++++++++++++++++
 platform/generic/allwinner/objects.mk |  4 ++--
 platform/generic/configs/defconfig    |  3 +++
 platform/generic/sifive/objects.mk    |  8 ++++----
 4 files changed, 27 insertions(+), 6 deletions(-)

diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
index c1a77b3..c76bd12 100644
--- a/platform/generic/Kconfig
+++ b/platform/generic/Kconfig
@@ -6,3 +6,21 @@ config PLATFORM_GENERIC
 	select FDT_DOMAIN
 	select FDT_PMU
 	default y
+
+if PLATFORM_GENERIC
+
+config PLATFORM_ALLWINNER_D1
+	bool "Allwinner D1 support"
+	depends on FDT_IRQCHIP_PLIC
+	default n
+
+config PLATFORM_SIFIVE_FU540
+	bool "SiFive FU540 support"
+	default n
+
+config PLATFORM_SIFIVE_FU740
+	bool "SiFive FU740 support"
+	depends on FDT_RESET && FDT_I2C
+	default n
+
+endif
diff --git a/platform/generic/allwinner/objects.mk b/platform/generic/allwinner/objects.mk
index 9e36ab6..447cdf1 100644
--- a/platform/generic/allwinner/objects.mk
+++ b/platform/generic/allwinner/objects.mk
@@ -2,5 +2,5 @@
 # SPDX-License-Identifier: BSD-2-Clause
 #
 
-carray-platform_override_modules-y += sun20i_d1
-platform-objs-y += allwinner/sun20i-d1.o
+carray-platform_override_modules-$(CONFIG_PLATFORM_ALLWINNER_D1) += sun20i_d1
+platform-objs-$(CONFIG_PLATFORM_ALLWINNER_D1) += allwinner/sun20i-d1.o
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index d56cef2..2a75394 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -1,3 +1,6 @@
+CONFIG_PLATFORM_ALLWINNER_D1=y
+CONFIG_PLATFORM_SIFIVE_FU540=y
+CONFIG_PLATFORM_SIFIVE_FU740=y
 CONFIG_FDT_GPIO=y
 CONFIG_FDT_GPIO_SIFIVE=y
 CONFIG_FDT_I2C=y
diff --git a/platform/generic/sifive/objects.mk b/platform/generic/sifive/objects.mk
index c17e2df..d75e444 100644
--- a/platform/generic/sifive/objects.mk
+++ b/platform/generic/sifive/objects.mk
@@ -2,8 +2,8 @@
 # SPDX-License-Identifier: BSD-2-Clause
 #
 
-carray-platform_override_modules-y += sifive_fu540
-platform-objs-y += sifive/fu540.o
+carray-platform_override_modules-$(CONFIG_PLATFORM_SIFIVE_FU540) += sifive_fu540
+platform-objs-$(CONFIG_PLATFORM_SIFIVE_FU540) += sifive/fu540.o
 
-carray-platform_override_modules-y += sifive_fu740
-platform-objs-y += sifive/fu740.o
+carray-platform_override_modules-$(CONFIG_PLATFORM_SIFIVE_FU740) += sifive_fu740
+platform-objs-$(CONFIG_PLATFORM_SIFIVE_FU740) += sifive/fu740.o
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 07:56:08 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 20:26:08 +0530
Subject: [PATCH v2 11/14] lib: utils/fdt: Use kconfig for enabling/disabling
In-Reply-To: <20220718145611.2211638-1-apatel@ventanamicro.com>
References: <20220718145611.2211638-1-apatel@ventanamicro.com>
Message-ID: <20220718145611.2211638-12-apatel@ventanamicro.com>

We update FDT support makefile to use kconfig for enabling/disabling.
To avoid compilation errors, we also enable FDT for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 include/sbi_utils/fdt/fdt_domain.h             |  9 +++++++++
 include/sbi_utils/fdt/fdt_pmu.h                | 10 ++++++++++
 lib/utils/Kconfig                              |  4 ++++
 lib/utils/fdt/Kconfig                          | 18 ++++++++++++++++++
 lib/utils/fdt/objects.mk                       |  8 ++++----
 lib/utils/gpio/Kconfig                         |  1 +
 lib/utils/i2c/Kconfig                          |  1 +
 lib/utils/ipi/Kconfig                          |  1 +
 lib/utils/irqchip/Kconfig                      |  1 +
 {platform/generic => lib/utils/libfdt}/Kconfig |  4 ++--
 lib/utils/libfdt/objects.mk                    |  4 ++--
 lib/utils/reset/Kconfig                        |  1 +
 lib/utils/serial/Kconfig                       |  1 +
 lib/utils/timer/Kconfig                        |  1 +
 platform/andes/ae350/Kconfig                   |  1 +
 platform/fpga/ariane/Kconfig                   |  1 +
 platform/fpga/openpiton/Kconfig                |  1 +
 platform/generic/Kconfig                       |  3 +++
 platform/kendryte/k210/Kconfig                 |  1 +
 platform/nuclei/ux600/Kconfig                  |  1 +
 platform/nuclei/ux600/platform.c               |  1 -
 21 files changed, 64 insertions(+), 9 deletions(-)
 create mode 100644 lib/utils/fdt/Kconfig
 copy {platform/generic => lib/utils/libfdt}/Kconfig (57%)

diff --git a/include/sbi_utils/fdt/fdt_domain.h b/include/sbi_utils/fdt/fdt_domain.h
index 5448eb4..ee09d1b 100644
--- a/include/sbi_utils/fdt/fdt_domain.h
+++ b/include/sbi_utils/fdt/fdt_domain.h
@@ -13,6 +13,8 @@
 
 #include <sbi/sbi_types.h>
 
+#ifdef CONFIG_FDT_DOMAIN
+
 struct sbi_domain;
 
 /**
@@ -70,4 +72,11 @@ void fdt_domain_fixup(void *fdt);
  */
 int fdt_domains_populate(void *fdt);
 
+#else
+
+static inline void fdt_domain_fixup(void *fdt) { }
+static inline int fdt_domains_populate(void *fdt) { return 0; }
+
+#endif
+
 #endif /* __FDT_DOMAIN_H__ */
diff --git a/include/sbi_utils/fdt/fdt_pmu.h b/include/sbi_utils/fdt/fdt_pmu.h
index 2fa01ed..c65cad7 100644
--- a/include/sbi_utils/fdt/fdt_pmu.h
+++ b/include/sbi_utils/fdt/fdt_pmu.h
@@ -13,6 +13,8 @@
 
 #include <sbi/sbi_types.h>
 
+#ifdef CONFIG_FDT_PMU
+
 /**
  * Fix up the PMU node in the device tree
  *
@@ -43,4 +45,12 @@ int fdt_pmu_setup(void *fdt);
  */
 uint64_t fdt_pmu_get_select_value(uint32_t event_idx);
 
+#else
+
+static inline void fdt_pmu_fixup(void *fdt) { }
+static inline int fdt_pmu_setup(void *fdt) { return 0; }
+static inline uint64_t fdt_pmu_get_select_value(uint32_t event_idx) { return 0; }
+
+#endif
+
 #endif
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 79b9c2c..a6e93e4 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -2,6 +2,8 @@
 
 menu "Utils and Drivers Support"
 
+source "$OPENSBI_SRC_DIR/lib/utils/fdt/Kconfig"
+
 source "$OPENSBI_SRC_DIR/lib/utils/gpio/Kconfig"
 
 source "$OPENSBI_SRC_DIR/lib/utils/i2c/Kconfig"
@@ -10,6 +12,8 @@ source "$OPENSBI_SRC_DIR/lib/utils/ipi/Kconfig"
 
 source "$OPENSBI_SRC_DIR/lib/utils/irqchip/Kconfig"
 
+source "$OPENSBI_SRC_DIR/lib/utils/libfdt/Kconfig"
+
 source "$OPENSBI_SRC_DIR/lib/utils/reset/Kconfig"
 
 source "$OPENSBI_SRC_DIR/lib/utils/serial/Kconfig"
diff --git a/lib/utils/fdt/Kconfig b/lib/utils/fdt/Kconfig
new file mode 100644
index 0000000..23b003b
--- /dev/null
+++ b/lib/utils/fdt/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menuconfig FDT
+	bool "Flattened Device Tree (FDT) support"
+	select LIBFDT
+	default n
+
+if FDT
+
+config FDT_DOMAIN
+	bool "FDT domain support"
+	default n
+
+config FDT_PMU
+	bool "FDT performance monitoring unit (PMU) support"
+	default n
+
+endif
diff --git a/lib/utils/fdt/objects.mk b/lib/utils/fdt/objects.mk
index 03800f9..5cede81 100644
--- a/lib/utils/fdt/objects.mk
+++ b/lib/utils/fdt/objects.mk
@@ -4,7 +4,7 @@
 # Copyright (C) 2020 Bin Meng <bmeng.cn at gmail.com>
 #
 
-libsbiutils-objs-y += fdt/fdt_domain.o
-libsbiutils-objs-y += fdt/fdt_pmu.o
-libsbiutils-objs-y += fdt/fdt_helper.o
-libsbiutils-objs-y += fdt/fdt_fixup.o
+libsbiutils-objs-$(CONFIG_FDT_DOMAIN) += fdt/fdt_domain.o
+libsbiutils-objs-$(CONFIG_FDT_PMU) += fdt/fdt_pmu.o
+libsbiutils-objs-$(CONFIG_FDT) += fdt/fdt_helper.o
+libsbiutils-objs-$(CONFIG_FDT) += fdt/fdt_fixup.o
diff --git a/lib/utils/gpio/Kconfig b/lib/utils/gpio/Kconfig
index daf7f91..38a9d75 100644
--- a/lib/utils/gpio/Kconfig
+++ b/lib/utils/gpio/Kconfig
@@ -4,6 +4,7 @@ menu "GPIO Support"
 
 config FDT_GPIO
 	bool "FDT based GPIO drivers"
+	depends on FDT
 	select GPIO
 	default n
 
diff --git a/lib/utils/i2c/Kconfig b/lib/utils/i2c/Kconfig
index 16c4453..46a3454 100644
--- a/lib/utils/i2c/Kconfig
+++ b/lib/utils/i2c/Kconfig
@@ -4,6 +4,7 @@ menu "I2C Support"
 
 config FDT_I2C
 	bool "FDT based I2C drivers"
+	depends on FDT
 	select I2C
 	default n
 
diff --git a/lib/utils/ipi/Kconfig b/lib/utils/ipi/Kconfig
index c0634d3..efc8960 100644
--- a/lib/utils/ipi/Kconfig
+++ b/lib/utils/ipi/Kconfig
@@ -4,6 +4,7 @@ menu "IPI Device Support"
 
 config FDT_IPI
 	bool "FDT based ipi drivers"
+	depends on FDT
 	default n
 
 if FDT_IPI
diff --git a/lib/utils/irqchip/Kconfig b/lib/utils/irqchip/Kconfig
index 6a7e297..7a71388 100644
--- a/lib/utils/irqchip/Kconfig
+++ b/lib/utils/irqchip/Kconfig
@@ -4,6 +4,7 @@ menu "Interrupt Controller Support"
 
 config FDT_IRQCHIP
 	bool "FDT based interrupt controller drivers"
+	depends on FDT
 	default n
 
 if FDT_IRQCHIP
diff --git a/platform/generic/Kconfig b/lib/utils/libfdt/Kconfig
similarity index 57%
copy from platform/generic/Kconfig
copy to lib/utils/libfdt/Kconfig
index 3eab282..d1cecf8 100644
--- a/platform/generic/Kconfig
+++ b/lib/utils/libfdt/Kconfig
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: BSD-2-Clause
 
-config PLATFORM_GENERIC
+config LIBFDT
 	bool
-	default y
+	default n
diff --git a/lib/utils/libfdt/objects.mk b/lib/utils/libfdt/objects.mk
index 8c060df..a5b4001 100644
--- a/lib/utils/libfdt/objects.mk
+++ b/lib/utils/libfdt/objects.mk
@@ -12,5 +12,5 @@ libfdt_files = fdt.o fdt_addresses.o fdt_check.o fdt_empty_tree.o fdt_ro.o fdt_r
 $(foreach file, $(libfdt_files), \
         $(eval CFLAGS_$(file) = -I$(src)/../../utils/libfdt))
 
-libsbiutils-objs-y += $(addprefix libfdt/,$(libfdt_files))
-libsbiutils-genflags-y  += -I$(libsbiutils_dir)/libfdt/
+libsbiutils-objs-$(CONFIG_LIBFDT) += $(addprefix libfdt/,$(libfdt_files))
+libsbiutils-genflags-$(CONFIG_LIBFDT)  += -I$(libsbiutils_dir)/libfdt/
diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig
index 71996cb..2e83ff6 100644
--- a/lib/utils/reset/Kconfig
+++ b/lib/utils/reset/Kconfig
@@ -4,6 +4,7 @@ menu "System Reset Support"
 
 config FDT_RESET
 	bool "FDT based reset drivers"
+	depends on FDT
 	default n
 
 if FDT_RESET
diff --git a/lib/utils/serial/Kconfig b/lib/utils/serial/Kconfig
index bf37e34..152060d 100644
--- a/lib/utils/serial/Kconfig
+++ b/lib/utils/serial/Kconfig
@@ -4,6 +4,7 @@ menu "Serial Device Support"
 
 config FDT_SERIAL
 	bool "FDT based serial drivers"
+	depends on FDT
 	default n
 
 if FDT_SERIAL
diff --git a/lib/utils/timer/Kconfig b/lib/utils/timer/Kconfig
index 0a1c727..23c48c5 100644
--- a/lib/utils/timer/Kconfig
+++ b/lib/utils/timer/Kconfig
@@ -4,6 +4,7 @@ menu "Timer Device Support"
 
 config FDT_TIMER
 	bool "FDT based timer drivers"
+	depends on FDT
 	default n
 
 if FDT_TIMER
diff --git a/platform/andes/ae350/Kconfig b/platform/andes/ae350/Kconfig
index 87deab0..d57f19f 100644
--- a/platform/andes/ae350/Kconfig
+++ b/platform/andes/ae350/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_ANDES_AE350
 	bool
+	select FDT
 	select IRQCHIP_PLIC
 	select SERIAL_UART8250
 	default y
diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
index 5a720d9..e1f118b 100644
--- a/platform/fpga/ariane/Kconfig
+++ b/platform/fpga/ariane/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_ARIANE_FPGA
 	bool
+	select FDT
 	select IPI_MSWI
 	select IRQCHIP_PLIC
 	select SERIAL_UART8250
diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
index 973906e..bc9c86e 100644
--- a/platform/fpga/openpiton/Kconfig
+++ b/platform/fpga/openpiton/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_OPENPITON_FPGA
 	bool
+	select FDT
 	select IPI_MSWI
 	select IRQCHIP_PLIC
 	select SERIAL_UART8250
diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
index 3eab282..c1a77b3 100644
--- a/platform/generic/Kconfig
+++ b/platform/generic/Kconfig
@@ -2,4 +2,7 @@
 
 config PLATFORM_GENERIC
 	bool
+	select FDT
+	select FDT_DOMAIN
+	select FDT_PMU
 	default y
diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
index 2a1daaa..5bf5973 100644
--- a/platform/kendryte/k210/Kconfig
+++ b/platform/kendryte/k210/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_KENDRYTE_K210
 	bool
+	select FDT
 	select IPI_MSWI
 	select IRQCHIP_PLIC
 	select SERIAL_SIFIVE
diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
index 464802e..84d74e8 100644
--- a/platform/nuclei/ux600/Kconfig
+++ b/platform/nuclei/ux600/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_NUCLEI_UX600
 	bool
+	select FDT
 	select IPI_MSWI
 	select IRQCHIP_PLIC
 	select SERIAL_SIFIVE
diff --git a/platform/nuclei/ux600/platform.c b/platform/nuclei/ux600/platform.c
index 5498a1f..2b02734 100644
--- a/platform/nuclei/ux600/platform.c
+++ b/platform/nuclei/ux600/platform.c
@@ -8,7 +8,6 @@
  *   hqfang <578567190 at qq.com>
  */
 
-#include <libfdt.h>
 #include <sbi/riscv_asm.h>
 #include <sbi/riscv_io.h>
 #include <sbi/riscv_encoding.h>
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 07:56:10 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 20:26:10 +0530
Subject: [PATCH v2 13/14] platform: Remove redundant config.mk from all platforms
In-Reply-To: <20220718145611.2211638-1-apatel@ventanamicro.com>
References: <20220718145611.2211638-1-apatel@ventanamicro.com>
Message-ID: <20220718145611.2211638-14-apatel@ventanamicro.com>

The options defined in config.mk can be specified in objects.mk of each
platform so let us remove config.mk from all platforms.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 Makefile                           |  4 +-
 platform/andes/ae350/config.mk     | 36 --------------
 platform/andes/ae350/objects.mk    | 29 +++++++++++
 platform/fpga/ariane/config.mk     | 36 --------------
 platform/fpga/ariane/objects.mk    | 35 ++++++++++++++
 platform/fpga/openpiton/config.mk  | 35 --------------
 platform/fpga/openpiton/objects.mk | 35 ++++++++++++++
 platform/generic/config.mk         | 40 ----------------
 platform/generic/objects.mk        | 33 +++++++++++++
 platform/kendryte/k210/config.mk   | 19 --------
 platform/kendryte/k210/objects.mk  | 12 +++++
 platform/nuclei/ux600/config.mk    | 30 ------------
 platform/nuclei/ux600/objects.mk   | 22 +++++++++
 platform/template/config.mk        | 77 ------------------------------
 platform/template/objects.mk       | 72 ++++++++++++++++++++++++++++
 platform/template/platform.c       |  2 +-
 16 files changed, 240 insertions(+), 277 deletions(-)
 delete mode 100644 platform/andes/ae350/config.mk
 delete mode 100644 platform/fpga/ariane/config.mk
 delete mode 100644 platform/fpga/openpiton/config.mk
 delete mode 100644 platform/generic/config.mk
 delete mode 100644 platform/kendryte/k210/config.mk
 delete mode 100644 platform/nuclei/ux600/config.mk
 delete mode 100644 platform/template/config.mk

diff --git a/Makefile b/Makefile
index 5fc67db..a6475a7 100644
--- a/Makefile
+++ b/Makefile
@@ -203,7 +203,7 @@ firmware-object-mks=$(shell if [ -d $(firmware_dir) ]; then find $(firmware_dir)
 .PHONY: all
 all:
 
-# Include platform specific .config and config.mk
+# Include platform specific .config
 ifdef PLATFORM
 .PHONY: menuconfig
 menuconfig: $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
@@ -226,8 +226,6 @@ $(KCONFIG_CONFIG): $(platform_src_dir)/configs/$(PLATFORM_DEFCONFIG) $(platform_
 $(KCONFIG_AUTOCONF_CMD): $(KCONFIG_CONFIG)
 include $(KCONFIG_CONFIG)
 include $(KCONFIG_AUTOCONF_CMD)
-
-include $(platform_src_dir)/config.mk
 endif
 
 # Include all object.mk files
diff --git a/platform/andes/ae350/config.mk b/platform/andes/ae350/config.mk
deleted file mode 100644
index f555ef5..0000000
--- a/platform/andes/ae350/config.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2019 Andes Technology Corporation
-#
-# Authors:
-#   Zong Li <zong at andestech.com>
-#   Nylon Chen <nylon7 at andestech.com>
-
-# Compiler flags
-platform-cppflags-y =
-platform-cflags-y =
-platform-asflags-y =
-platform-ldflags-y =
-
-# Blobs to build
-FW_TEXT_START=0x00000000
-
-FW_DYNAMIC=y
-
-FW_JUMP=y
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-  FW_JUMP_ADDR=0x400000
-else
-  FW_JUMP_ADDR=0x200000
-endif
-FW_JUMP_FDT_ADDR=0x2000000
-
-FW_PAYLOAD=y
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-  FW_PAYLOAD_OFFSET=0x400000
-else
-  FW_PAYLOAD_OFFSET=0x200000
-endif
-
-FW_PAYLOAD_FDT_ADDR=0x2000000
diff --git a/platform/andes/ae350/objects.mk b/platform/andes/ae350/objects.mk
index 5369677..80f0737 100644
--- a/platform/andes/ae350/objects.mk
+++ b/platform/andes/ae350/objects.mk
@@ -8,4 +8,33 @@
 #   Nylon Chen <nylon7 at andestech.com>
 #
 
+# Compiler flags
+platform-cppflags-y =
+platform-cflags-y =
+platform-asflags-y =
+platform-ldflags-y =
+
+# Objects to build
 platform-objs-y += cache.o platform.o plicsw.o plmt.o
+
+# Blobs to build
+FW_TEXT_START=0x00000000
+
+FW_DYNAMIC=y
+
+FW_JUMP=y
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+  FW_JUMP_ADDR=0x400000
+else
+  FW_JUMP_ADDR=0x200000
+endif
+FW_JUMP_FDT_ADDR=0x2000000
+
+FW_PAYLOAD=y
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+  FW_PAYLOAD_OFFSET=0x400000
+else
+  FW_PAYLOAD_OFFSET=0x200000
+endif
+
+FW_PAYLOAD_FDT_ADDR=0x2000000
diff --git a/platform/fpga/ariane/config.mk b/platform/fpga/ariane/config.mk
deleted file mode 100644
index 3556461..0000000
--- a/platform/fpga/ariane/config.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (C) 2019 FORTH-ICS/CARV
-#		Panagiotis Peristerakis <perister at ics.forth.gr>
-#
-
-#for more infos, check out /platform/template/config.mk
-
-PLATFORM_RISCV_XLEN = 64
-
-# Blobs to build
-FW_TEXT_START=0x80000000
-FW_JUMP=n
-
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
- # This needs to be 4MB aligned for 32-bit support
- FW_JUMP_ADDR=0x80400000
- else
- # This needs to be 2MB aligned for 64-bit support
- FW_JUMP_ADDR=0x80200000
- endif
-FW_JUMP_FDT_ADDR=0x82200000
-
-# Firmware with payload configuration.
-FW_PAYLOAD=y
-
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-# This needs to be 4MB aligned for 32-bit support
-  FW_PAYLOAD_OFFSET=0x400000
-else
-# This needs to be 2MB aligned for 64-bit support
-  FW_PAYLOAD_OFFSET=0x200000
-endif
-FW_PAYLOAD_FDT_ADDR=0x82200000
-FW_PAYLOAD_ALIGN=0x1000
diff --git a/platform/fpga/ariane/objects.mk b/platform/fpga/ariane/objects.mk
index 814e6da..83581ac 100644
--- a/platform/fpga/ariane/objects.mk
+++ b/platform/fpga/ariane/objects.mk
@@ -5,4 +5,39 @@
 #		Panagiotis Peristerakis <perister at ics.forth.gr>
 #
 
+# Compiler flags
+platform-cppflags-y =
+platform-cflags-y =
+platform-asflags-y =
+platform-ldflags-y =
+
+# Object to build
 platform-objs-y += platform.o
+
+PLATFORM_RISCV_XLEN = 64
+
+# Blobs to build
+FW_TEXT_START=0x80000000
+FW_JUMP=n
+
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+ # This needs to be 4MB aligned for 32-bit support
+ FW_JUMP_ADDR=0x80400000
+ else
+ # This needs to be 2MB aligned for 64-bit support
+ FW_JUMP_ADDR=0x80200000
+ endif
+FW_JUMP_FDT_ADDR=0x82200000
+
+# Firmware with payload configuration.
+FW_PAYLOAD=y
+
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+# This needs to be 4MB aligned for 32-bit support
+  FW_PAYLOAD_OFFSET=0x400000
+else
+# This needs to be 2MB aligned for 64-bit support
+  FW_PAYLOAD_OFFSET=0x200000
+endif
+FW_PAYLOAD_FDT_ADDR=0x82200000
+FW_PAYLOAD_ALIGN=0x1000
diff --git a/platform/fpga/openpiton/config.mk b/platform/fpga/openpiton/config.mk
deleted file mode 100644
index a969b25..0000000
--- a/platform/fpga/openpiton/config.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2020 Western Digital Corporation or its affiliates.
-#
-
-#for more infos, check out /platform/template/config.mk
-
-PLATFORM_RISCV_XLEN = 64
-
-# Blobs to build
-FW_TEXT_START=0x80000000
-FW_JUMP=n
-
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
- # This needs to be 4MB aligned for 32-bit support
- FW_JUMP_ADDR=0x80400000
- else
- # This needs to be 2MB aligned for 64-bit support
- FW_JUMP_ADDR=0x80200000
- endif
-FW_JUMP_FDT_ADDR=0x82200000
-
-# Firmware with payload configuration.
-FW_PAYLOAD=y
-
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-# This needs to be 4MB aligned for 32-bit support
-  FW_PAYLOAD_OFFSET=0x400000
-else
-# This needs to be 2MB aligned for 64-bit support
-  FW_PAYLOAD_OFFSET=0x200000
-endif
-FW_PAYLOAD_FDT_ADDR=0x82200000
-FW_PAYLOAD_ALIGN=0x1000
diff --git a/platform/fpga/openpiton/objects.mk b/platform/fpga/openpiton/objects.mk
index 30a3c4f..c8c345a 100644
--- a/platform/fpga/openpiton/objects.mk
+++ b/platform/fpga/openpiton/objects.mk
@@ -4,4 +4,39 @@
 # Copyright (c) 2020 Western Digital Corporation or its affiliates.
 #
 
+# Compiler flags
+platform-cppflags-y =
+platform-cflags-y =
+platform-asflags-y =
+platform-ldflags-y =
+
+# Objects to build
 platform-objs-y += platform.o
+
+PLATFORM_RISCV_XLEN = 64
+
+# Blobs to build
+FW_TEXT_START=0x80000000
+FW_JUMP=n
+
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+ # This needs to be 4MB aligned for 32-bit support
+ FW_JUMP_ADDR=0x80400000
+ else
+ # This needs to be 2MB aligned for 64-bit support
+ FW_JUMP_ADDR=0x80200000
+ endif
+FW_JUMP_FDT_ADDR=0x82200000
+
+# Firmware with payload configuration.
+FW_PAYLOAD=y
+
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+# This needs to be 4MB aligned for 32-bit support
+  FW_PAYLOAD_OFFSET=0x400000
+else
+# This needs to be 2MB aligned for 64-bit support
+  FW_PAYLOAD_OFFSET=0x200000
+endif
+FW_PAYLOAD_FDT_ADDR=0x82200000
+FW_PAYLOAD_ALIGN=0x1000
diff --git a/platform/generic/config.mk b/platform/generic/config.mk
deleted file mode 100644
index 8151974..0000000
--- a/platform/generic/config.mk
+++ /dev/null
@@ -1,40 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2020 Western Digital Corporation or its affiliates.
-#
-# Authors:
-#   Anup Patel <anup.patel at wdc.com>
-#
-
-# Compiler flags
-platform-cppflags-y =
-platform-cflags-y =
-platform-asflags-y =
-platform-ldflags-y =
-
-# Command for platform specific "make run"
-platform-runcmd = qemu-system-riscv$(PLATFORM_RISCV_XLEN) -M virt -m 256M \
-  -nographic -bios $(build_dir)/platform/generic/firmware/fw_payload.elf
-
-# Blobs to build
-FW_TEXT_START=0x80000000
-FW_DYNAMIC=y
-FW_JUMP=y
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-  # This needs to be 4MB aligned for 32-bit system
-  FW_JUMP_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x400000)))
-else
-  # This needs to be 2MB aligned for 64-bit system
-  FW_JUMP_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x200000)))
-endif
-FW_JUMP_FDT_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x2200000)))
-FW_PAYLOAD=y
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-  # This needs to be 4MB aligned for 32-bit system
-  FW_PAYLOAD_OFFSET=0x400000
-else
-  # This needs to be 2MB aligned for 64-bit system
-  FW_PAYLOAD_OFFSET=0x200000
-endif
-FW_PAYLOAD_FDT_ADDR=$(FW_JUMP_FDT_ADDR)
diff --git a/platform/generic/objects.mk b/platform/generic/objects.mk
index 4907754..136853e 100644
--- a/platform/generic/objects.mk
+++ b/platform/generic/objects.mk
@@ -7,5 +7,38 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
+# Compiler flags
+platform-cppflags-y =
+platform-cflags-y =
+platform-asflags-y =
+platform-ldflags-y =
+
+# Command for platform specific "make run"
+platform-runcmd = qemu-system-riscv$(PLATFORM_RISCV_XLEN) -M virt -m 256M \
+  -nographic -bios $(build_dir)/platform/generic/firmware/fw_payload.elf
+
+# Objects to build
 platform-objs-y += platform.o
 platform-objs-y += platform_override_modules.o
+
+# Blobs to build
+FW_TEXT_START=0x80000000
+FW_DYNAMIC=y
+FW_JUMP=y
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+  # This needs to be 4MB aligned for 32-bit system
+  FW_JUMP_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x400000)))
+else
+  # This needs to be 2MB aligned for 64-bit system
+  FW_JUMP_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x200000)))
+endif
+FW_JUMP_FDT_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x2200000)))
+FW_PAYLOAD=y
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+  # This needs to be 4MB aligned for 32-bit system
+  FW_PAYLOAD_OFFSET=0x400000
+else
+  # This needs to be 2MB aligned for 64-bit system
+  FW_PAYLOAD_OFFSET=0x200000
+endif
+FW_PAYLOAD_FDT_ADDR=$(FW_JUMP_FDT_ADDR)
diff --git a/platform/kendryte/k210/config.mk b/platform/kendryte/k210/config.mk
deleted file mode 100644
index 8a9b814..0000000
--- a/platform/kendryte/k210/config.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2019 Western Digital Corporation or its affiliates.
-#
-# Authors:
-#   Damien Le Moal <damien.lemoal at wdc.com>
-#
-
-# Compiler flags
-platform-cppflags-y =
-platform-cflags-y =
-platform-asflags-y =
-platform-ldflags-y =
-
-# Blobs to build
-FW_TEXT_START=0x80000000
-FW_PAYLOAD=y
-FW_PAYLOAD_ALIGN=0x1000
diff --git a/platform/kendryte/k210/objects.mk b/platform/kendryte/k210/objects.mk
index b74da74..1bfb898 100644
--- a/platform/kendryte/k210/objects.mk
+++ b/platform/kendryte/k210/objects.mk
@@ -7,8 +7,20 @@
 #   Damien Le Moal <damien.lemoal at wdc.com>
 #
 
+# Compiler flags
+platform-cppflags-y =
+platform-cflags-y =
+platform-asflags-y =
+platform-ldflags-y =
+
+# Objects to build
 platform-objs-y += platform.o
 
 platform-objs-y += k210.o
 platform-varprefix-k210.o = dt_k210
 platform-padding-k210.o = 2048
+
+# Blobs to build
+FW_TEXT_START=0x80000000
+FW_PAYLOAD=y
+FW_PAYLOAD_ALIGN=0x1000
diff --git a/platform/nuclei/ux600/config.mk b/platform/nuclei/ux600/config.mk
deleted file mode 100644
index dddcc4e..0000000
--- a/platform/nuclei/ux600/config.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2020 Nuclei Corporation or its affiliates.
-#
-# Authors:
-#   lujun <lujun at nucleisys.com>
-#   hqfang <578567190 at qq.com>
-#
-
-# Compiler flags
-platform-cppflags-y =
-platform-cflags-y =
-platform-asflags-y =
-platform-ldflags-y =
-
-# Command for platform specific "make run"
-platform-runcmd = xl_spike \
-  $(build_dir)/platform/nuclei/ux600/firmware/fw_payload.elf
-
-# Blobs to build
-FW_TEXT_START=0xA0000000
-FW_DYNAMIC=y
-FW_JUMP=y
-
-FW_JUMP_ADDR=0xA0200000
-FW_JUMP_FDT_ADDR=0xA8000000
-FW_PAYLOAD=y
-FW_PAYLOAD_OFFSET=0x200000
-FW_PAYLOAD_FDT_ADDR=0xA8000000
diff --git a/platform/nuclei/ux600/objects.mk b/platform/nuclei/ux600/objects.mk
index 8c36c90..7c429e0 100644
--- a/platform/nuclei/ux600/objects.mk
+++ b/platform/nuclei/ux600/objects.mk
@@ -8,4 +8,26 @@
 #   hqfang <578567190 at qq.com>
 #
 
+# Compiler flags
+platform-cppflags-y =
+platform-cflags-y =
+platform-asflags-y =
+platform-ldflags-y =
+
+# Command for platform specific "make run"
+platform-runcmd = xl_spike \
+  $(build_dir)/platform/nuclei/ux600/firmware/fw_payload.elf
+
+# Objects to build
 platform-objs-y += platform.o
+
+# Blobs to build
+FW_TEXT_START=0xA0000000
+FW_DYNAMIC=y
+FW_JUMP=y
+
+FW_JUMP_ADDR=0xA0200000
+FW_JUMP_FDT_ADDR=0xA8000000
+FW_PAYLOAD=y
+FW_PAYLOAD_OFFSET=0x200000
+FW_PAYLOAD_FDT_ADDR=0xA8000000
diff --git a/platform/template/config.mk b/platform/template/config.mk
deleted file mode 100644
index f817342..0000000
--- a/platform/template/config.mk
+++ /dev/null
@@ -1,77 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2019 Western Digital Corporation or its affiliates.
-#
-
-# Compiler pre-processor flags
-platform-cppflags-y =
-
-# C Compiler and assembler flags.
-platform-cflags-y =
-platform-asflags-y =
-
-# Linker flags: additional libraries and object files that the platform
-# code needs can be added here
-platform-ldflags-y =
-
-#
-# Command for platform specific "make run"
-# Useful for development and debugging on plaftform simulator (such as QEMU)
-#
-# platform-runcmd = your_platform_run.sh
-
-#
-# Platform RISC-V XLEN, ABI, ISA and Code Model configuration.
-# These are optional parameters but platforms can optionaly provide it.
-# Some of these are guessed based on GCC compiler capabilities
-#
-# PLATFORM_RISCV_XLEN = 64
-# PLATFORM_RISCV_ABI = lp64
-# PLATFORM_RISCV_ISA = rv64imafdc
-# PLATFORM_RISCV_CODE_MODEL = medany
-
-# Firmware load address configuration. This is mandatory.
-FW_TEXT_START=0x80000000
-
-# Optional parameter for path to external FDT
-# FW_FDT_PATH="path to platform flattened device tree file"
-
-#
-# Dynamic firmware configuration.
-# Optional parameters are commented out. Uncomment and define these parameters
-# as needed.
-#
-FW_DYNAMIC=<y|n>
-
-#
-# Jump firmware configuration.
-# Optional parameters are commented out. Uncomment and define these parameters
-# as needed.
-#
-FW_JUMP=<y|n>
-# This needs to be 4MB aligned for 32-bit support
-# This needs to be 2MB aligned for 64-bit support
-# ifeq ($(PLATFORM_RISCV_XLEN), 32)
-# FW_JUMP_ADDR=0x80400000
-# else
-# FW_JUMP_ADDR=0x80200000
-# endif
-# FW_JUMP_FDT_ADDR=0x82200000
-
-#
-# Firmware with payload configuration.
-# Optional parameters are commented out. Uncomment and define these parameters
-# as needed.
-#
-FW_PAYLOAD=<y|n>
-# This needs to be 4MB aligned for 32-bit support
-# This needs to be 2MB aligned for 64-bit support
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-FW_PAYLOAD_OFFSET=0x400000
-else
-FW_PAYLOAD_OFFSET=0x200000
-endif
-# FW_PAYLOAD_ALIGN=0x1000
-# FW_PAYLOAD_PATH="path to next boot stage binary image file"
-# FW_PAYLOAD_FDT_ADDR=0x82200000
diff --git a/platform/template/objects.mk b/platform/template/objects.mk
index caaec2b..0025928 100644
--- a/platform/template/objects.mk
+++ b/platform/template/objects.mk
@@ -4,6 +4,33 @@
 # Copyright (c) 2019 Western Digital Corporation or its affiliates.
 #
 
+# Compiler pre-processor flags
+platform-cppflags-y =
+
+# C Compiler and assembler flags.
+platform-cflags-y =
+platform-asflags-y =
+
+# Linker flags: additional libraries and object files that the platform
+# code needs can be added here
+platform-ldflags-y =
+
+#
+# Command for platform specific "make run"
+# Useful for development and debugging on plaftform simulator (such as QEMU)
+#
+# platform-runcmd = your_platform_run.sh
+
+#
+# Platform RISC-V XLEN, ABI, ISA and Code Model configuration.
+# These are optional parameters but platforms can optionaly provide it.
+# Some of these are guessed based on GCC compiler capabilities
+#
+# PLATFORM_RISCV_XLEN = 64
+# PLATFORM_RISCV_ABI = lp64
+# PLATFORM_RISCV_ISA = rv64imafdc
+# PLATFORM_RISCV_CODE_MODEL = medany
+
 # Space separated list of object file names to be compiled for the platform
 platform-objs-y += platform.o
 
@@ -13,3 +40,48 @@ platform-objs-y += platform.o
 # source file be in the form <dt file name>.dts
 #
 # platform-objs-y += <dt file name>.o
+
+# Firmware load address configuration. This is mandatory.
+FW_TEXT_START=0x80000000
+
+# Optional parameter for path to external FDT
+# FW_FDT_PATH="path to platform flattened device tree file"
+
+#
+# Dynamic firmware configuration.
+# Optional parameters are commented out. Uncomment and define these parameters
+# as needed.
+#
+FW_DYNAMIC=<y|n>
+
+#
+# Jump firmware configuration.
+# Optional parameters are commented out. Uncomment and define these parameters
+# as needed.
+#
+FW_JUMP=<y|n>
+# This needs to be 4MB aligned for 32-bit support
+# This needs to be 2MB aligned for 64-bit support
+# ifeq ($(PLATFORM_RISCV_XLEN), 32)
+# FW_JUMP_ADDR=0x80400000
+# else
+# FW_JUMP_ADDR=0x80200000
+# endif
+# FW_JUMP_FDT_ADDR=0x82200000
+
+#
+# Firmware with payload configuration.
+# Optional parameters are commented out. Uncomment and define these parameters
+# as needed.
+#
+FW_PAYLOAD=<y|n>
+# This needs to be 4MB aligned for 32-bit support
+# This needs to be 2MB aligned for 64-bit support
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+FW_PAYLOAD_OFFSET=0x400000
+else
+FW_PAYLOAD_OFFSET=0x200000
+endif
+# FW_PAYLOAD_ALIGN=0x1000
+# FW_PAYLOAD_PATH="path to next boot stage binary image file"
+# FW_PAYLOAD_FDT_ADDR=0x82200000
diff --git a/platform/template/platform.c b/platform/template/platform.c
index 5524847..f3802da 100644
--- a/platform/template/platform.c
+++ b/platform/template/platform.c
@@ -11,7 +11,7 @@
 
 /*
  * Include these files as needed.
- * See config.mk PLATFORM_xxx configuration parameters.
+ * See objects.mk PLATFORM_xxx configuration parameters.
  */
 #include <sbi_utils/ipi/aclint_mswi.h>
 #include <sbi_utils/irqchip/plic.h>
-- 
2.34.1



From apatel at ventanamicro.com  Mon Jul 18 07:56:11 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Mon, 18 Jul 2022 20:26:11 +0530
Subject: [PATCH v2 14/14] docs: Update documentation for kconfig support
In-Reply-To: <20220718145611.2211638-1-apatel@ventanamicro.com>
References: <20220718145611.2211638-1-apatel@ventanamicro.com>
Message-ID: <20220718145611.2211638-15-apatel@ventanamicro.com>

We update all documentation files to:
1) Remove references to platform specific config.mk file since it is
   has been removed.
2) Add details about platform specific configs/defconfig and Kconfig
   files mandatory for each platform.
3) Add required packages in top-level README.md

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 README.md                   | 25 +++++++++++++++++++++++--
 docs/firmware/fw_dynamic.md |  2 +-
 docs/firmware/fw_jump.md    |  4 ++--
 docs/firmware/fw_payload.md |  4 ++--
 docs/platform/platform.md   |  4 ++--
 docs/platform_guide.md      |  9 +++++----
 6 files changed, 35 insertions(+), 13 deletions(-)

diff --git a/README.md b/README.md
index 54fabe4..cd852cb 100644
--- a/README.md
+++ b/README.md
@@ -92,8 +92,8 @@ N.B. Any S-mode boot loader (i.e. U-Boot) doesn't need to support HSM extension,
 as it doesn't need to boot all the harts. The operating system should be
 capable enough to bring up all other non-booting harts using HSM extension.
 
-Required Toolchain
-------------------
+Required Toolchain and Packages
+-------------------------------
 
 OpenSBI can be compiled natively or cross-compiled on a x86 host. For
 cross-compilation, you can build your own toolchain, download a prebuilt one
@@ -115,6 +115,14 @@ triple is used (e.g. *-target riscv64-unknown-elf*).
 Please note that only a 64-bit version of the toolchain is available in
 the Bootlin toolchain repository for now.
 
+In addition to a toolchain, OpenSBI also requires following packages on
+the host:
+
+1. device-tree-compiler: The device tree compiler for compiling device
+   tree sources (DTS files).
+2. kconfig-frontends: A standalone implementation of the Linux Kconfig
+   parser and frontends for OpenSBI platform configuration management.
+
 Building and Installing the OpenSBI Platform-Independent Library
 ----------------------------------------------------------------
 
@@ -196,6 +204,19 @@ top-level make command line. These options, such as *PLATFORM_<xyz>* or
 *docs/platform/<platform_name>.md* files and
 *docs/firmware/<firmware_name>.md* files.
 
+All OpenSBI platforms support Kconfig style build-time configuration. Users
+can change the build-time configuration of a platform using a graphical
+interface as follows:
+```
+make PLATFORM=<platform_subdir> menuconfig
+```
+
+Alternately, an OpenSBI platform can have multiple default configurations
+and users can select a custom default configuration as follows:
+```
+make PLATFORM=<platform_subdir> PLATFORM_DEFCONFIG=<platform_custom_defconfig>
+```
+
 Building 32-bit / 64-bit OpenSBI Images
 ---------------------------------------
 By default, building OpenSBI generates 32-bit or 64-bit images based on the
diff --git a/docs/firmware/fw_dynamic.md b/docs/firmware/fw_dynamic.md
index 7b9b192..e251488 100644
--- a/docs/firmware/fw_dynamic.md
+++ b/docs/firmware/fw_dynamic.md
@@ -20,7 +20,7 @@ the booting stage binary to follow OpenSBI firmware.
 A platform can enable *FW_DYNAMIC* firmware using any of the following methods.
 
 1. Specifying `FW_DYNAMIC=y` on the top level `make` command line.
-2. Specifying `FW_DYNAMIC=y` in the target platform *config.mk* configuration
+2. Specifying `FW_DYNAMIC=y` in the target platform *objects.mk* configuration
 file.
 
 The compiled *FW_DYNAMIC* firmware ELF file is named *fw_dynamic.elf*. It's
diff --git a/docs/firmware/fw_jump.md b/docs/firmware/fw_jump.md
index eea3013..35a4301 100644
--- a/docs/firmware/fw_jump.md
+++ b/docs/firmware/fw_jump.md
@@ -15,7 +15,7 @@ and the booting stage binary to follow the OpenSBI firmware.
 A platform *FW_JUMP* firmware can be enabled by any of the following methods:
 
 1. Specifying `FW_JUMP=y` on the top level `make` command line.
-2. Specifying `FW_JUMP=y` in the target platform *config.mk* configuration file.
+2. Specifying `FW_JUMP=y` in the target platform *objects.mk* configuration file.
 
 The compiled *FW_JUMP* firmware ELF file is named *fw_jump.elf*. Its expanded
 image file is *fw_jump.bin*. Both files are created in the platform-specific
@@ -26,7 +26,7 @@ build directory under the *build/platform/<platform_subdir>/firmware* directory.
 
 To operate correctly, a *FW_JUMP* firmware requires some configuration
 parameters to be defined using either the top level `make` command line or the
-target platform *config.mk* configuration file. The possible parameters are as
+target platform *objects.mk* configuration file. The possible parameters are as
 follows:
 
 * **FW_JUMP_ADDR** - Address of the entry point of the booting stage to be
diff --git a/docs/firmware/fw_payload.md b/docs/firmware/fw_payload.md
index 0947448..3bb918f 100644
--- a/docs/firmware/fw_payload.md
+++ b/docs/firmware/fw_payload.md
@@ -20,7 +20,7 @@ Enabling *FW_PAYLOAD* compilation
 The *FW_PAYLOAD* firmware can be enabled by any of the following methods:
 
 1. Specifying `FW_PAYLOAD=y` on the top level `make` command line.
-2. Specifying `FW_PAYLOAD=y` in the target platform *config.mk* configuration
+2. Specifying `FW_PAYLOAD=y` in the target platform *objects.mk* configuration
    file.
 
 The compiled *FW_PAYLOAD* firmware ELF file is named *fw_jump.elf*. Its
@@ -33,7 +33,7 @@ Configuration Options
 
 A *FW_PAYLOAD* firmware is built according to configuration parameters and
 options. These configuration parameters can be defined using either the top
-level `make` command line or the target platform *config.mk* configuration
+level `make` command line or the target platform *objects.mk* configuration
 file. The parameters currently defined are as follows:
 
 * **FW_PAYLOAD_OFFSET** - Offset from *FW_TEXT_BASE* where the payload binary
diff --git a/docs/platform/platform.md b/docs/platform/platform.md
index cb4bec6..b726e1d 100644
--- a/docs/platform/platform.md
+++ b/docs/platform/platform.md
@@ -42,8 +42,8 @@ OpenSBI currently supports the following virtual and hardware platforms:
 The code for these supported platforms can be used as example to implement
 support for other platforms. The *platform/template* directory also provides
 template files for implementing support for a new platform. The *object.mk*,
-*config.mk* and *platform.c* template files provides enough comments to
-facilitate the implementation.
+*Kconfig*, *configs/defconfig* and *platform.c* template files provides enough
+comments to facilitate the implementation.
 
 [generic.md]: generic.md
 [qemu_virt.md]: qemu_virt.md
diff --git a/docs/platform_guide.md b/docs/platform_guide.md
index b6c2c2e..8ac5aa0 100644
--- a/docs/platform_guide.md
+++ b/docs/platform_guide.md
@@ -28,11 +28,12 @@ Adding support for a new platform
 Support for a new platform named *&lt;xyz&gt;* can be added as follows:
 
 1. Create a directory named *&lt;xyz&gt;* under the *platform/* directory.
-2. Create a platform configuration file named *config.mk* under the
-   *platform/&lt;xyz&gt;/* directory. This configuration file will provide
+2. Create a platform configuration files named *Kconfig* and *configs/defconfig*
+   under the *platform/&lt;xyz&gt;/* directory. This configuration files will
+   provide the build time configuration for the sources to be compiled.
+3. Create a *platform/&lt;xyz&gt;/objects.mk* file for listing the platform
+   object files to be compiled. This file also provides platform-specific
    compiler flags, and select firmware options.
-3. Create a *platform/&lt;xyz&gt;/objects.mk* file for listing the
-   platform-specific object files to be compiled.
 4. Create a *platform/&lt;xyz&gt;/platform.c* file providing a
    *struct sbi_platform* instance.
 
-- 
2.34.1



From ajones at ventanamicro.com  Mon Jul 18 10:20:24 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Mon, 18 Jul 2022 19:20:24 +0200
Subject: [PATCH 0/4] lib: utils/serial: Collection of UART code improvements
Message-ID: <20220718172028.2006166-1-ajones@ventanamicro.com>

This series is a collection of changes I made while reviewing the UART
code. I've compile tested it and boot it on QEMU (which of course only
tests uart8250). I did also pass in a DTB with 0 for 'current-speed' in
the UART node and nothing interesting happened, so I guess that's good.

Andrew Jones (4):
  lib: utils/fdt: Factor out common uart node code
  lib: utils/serial: Initialize platform_uart_data to zero
  lib: serial: Clean up coding style in sifive-uart.c
  lib: utils/serial: Ensure baudrate is non-zero before using

 lib/utils/fdt/fdt_helper.c                  | 131 ++++++--------------
 lib/utils/serial/fdt_serial_gaisler.c       |   2 +-
 lib/utils/serial/fdt_serial_shakti.c        |   2 +-
 lib/utils/serial/fdt_serial_sifive.c        |   2 +-
 lib/utils/serial/fdt_serial_uart8250.c      |   2 +-
 lib/utils/serial/fdt_serial_xlnx_uartlite.c |   2 +-
 lib/utils/serial/gaisler-uart.c             |   2 +-
 lib/utils/serial/shakti-uart.c              |   8 +-
 lib/utils/serial/sifive-uart.c              |  13 +-
 lib/utils/serial/uart8250.c                 |   7 +-
 platform/fpga/openpiton/platform.c          |   2 +-
 11 files changed, 66 insertions(+), 107 deletions(-)

-- 
2.36.1



From ajones at ventanamicro.com  Mon Jul 18 10:20:26 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Mon, 18 Jul 2022 19:20:26 +0200
Subject: [PATCH 2/4] lib: utils/serial: Initialize platform_uart_data to zero
In-Reply-To: <20220718172028.2006166-1-ajones@ventanamicro.com>
References: <20220718172028.2006166-1-ajones@ventanamicro.com>
Message-ID: <20220718172028.2006166-3-ajones@ventanamicro.com>

While it doesn't look like there are any current cases of using
uninitialized data, let's zero all the UART data members to be
safe. Zero may not actually be better than a random number in
some cases, so all structure members should still be validated
before use, but at least zero is usually easier to debug than
some random stack garbage...

Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
---
 lib/utils/serial/fdt_serial_gaisler.c       | 2 +-
 lib/utils/serial/fdt_serial_shakti.c        | 2 +-
 lib/utils/serial/fdt_serial_sifive.c        | 2 +-
 lib/utils/serial/fdt_serial_uart8250.c      | 2 +-
 lib/utils/serial/fdt_serial_xlnx_uartlite.c | 2 +-
 platform/fpga/openpiton/platform.c          | 2 +-
 6 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/lib/utils/serial/fdt_serial_gaisler.c b/lib/utils/serial/fdt_serial_gaisler.c
index 9a9f9a8b7962..74988e34bcd8 100644
--- a/lib/utils/serial/fdt_serial_gaisler.c
+++ b/lib/utils/serial/fdt_serial_gaisler.c
@@ -15,7 +15,7 @@ static int serial_gaisler_init(void *fdt, int nodeoff,
 			       const struct fdt_match *match)
 {
 	int rc;
-	struct platform_uart_data uart;
+	struct platform_uart_data uart = { 0 };
 
 	rc = fdt_parse_gaisler_uart_node(fdt, nodeoff, &uart);
 	if (rc)
diff --git a/lib/utils/serial/fdt_serial_shakti.c b/lib/utils/serial/fdt_serial_shakti.c
index 4f91419ef53c..0e056303dbb5 100644
--- a/lib/utils/serial/fdt_serial_shakti.c
+++ b/lib/utils/serial/fdt_serial_shakti.c
@@ -13,7 +13,7 @@ static int serial_shakti_init(void *fdt, int nodeoff,
 				const struct fdt_match *match)
 {
 	int rc;
-	struct platform_uart_data uart;
+	struct platform_uart_data uart = { 0 };
 
 	rc = fdt_parse_shakti_uart_node(fdt, nodeoff, &uart);
 	if (rc)
diff --git a/lib/utils/serial/fdt_serial_sifive.c b/lib/utils/serial/fdt_serial_sifive.c
index f4c833c1573d..3ca10913eee3 100644
--- a/lib/utils/serial/fdt_serial_sifive.c
+++ b/lib/utils/serial/fdt_serial_sifive.c
@@ -15,7 +15,7 @@ static int serial_sifive_init(void *fdt, int nodeoff,
 				const struct fdt_match *match)
 {
 	int rc;
-	struct platform_uart_data uart;
+	struct platform_uart_data uart = { 0 };
 
 	rc = fdt_parse_sifive_uart_node(fdt, nodeoff, &uart);
 	if (rc)
diff --git a/lib/utils/serial/fdt_serial_uart8250.c b/lib/utils/serial/fdt_serial_uart8250.c
index 544b7416de42..0b95f2d9e44e 100644
--- a/lib/utils/serial/fdt_serial_uart8250.c
+++ b/lib/utils/serial/fdt_serial_uart8250.c
@@ -15,7 +15,7 @@ static int serial_uart8250_init(void *fdt, int nodeoff,
 				const struct fdt_match *match)
 {
 	int rc;
-	struct platform_uart_data uart;
+	struct platform_uart_data uart = { 0 };
 
 	rc = fdt_parse_uart8250_node(fdt, nodeoff, &uart);
 	if (rc)
diff --git a/lib/utils/serial/fdt_serial_xlnx_uartlite.c b/lib/utils/serial/fdt_serial_xlnx_uartlite.c
index 466e16e82d8c..9f04aea3673b 100644
--- a/lib/utils/serial/fdt_serial_xlnx_uartlite.c
+++ b/lib/utils/serial/fdt_serial_xlnx_uartlite.c
@@ -15,7 +15,7 @@ static int serial_xlnx_uartlite_init(void *fdt, int nodeoff,
 				const struct fdt_match *match)
 {
 	int rc;
-	struct platform_uart_data uart;
+	struct platform_uart_data uart = { 0 };
 
 	rc = fdt_parse_xlnx_uartlite_node(fdt, nodeoff, &uart);
 	if (rc)
diff --git a/platform/fpga/openpiton/platform.c b/platform/fpga/openpiton/platform.c
index 7ca21236bfef..5ff7d200aba9 100644
--- a/platform/fpga/openpiton/platform.c
+++ b/platform/fpga/openpiton/platform.c
@@ -69,7 +69,7 @@ static struct aclint_mtimer_data mtimer = {
 static int openpiton_early_init(bool cold_boot)
 {
 	void *fdt;
-	struct platform_uart_data uart_data;
+	struct platform_uart_data uart_data = { 0 };
 	struct plic_data plic_data;
 	unsigned long aclint_freq;
 	uint64_t clint_addr;
-- 
2.36.1



From ajones at ventanamicro.com  Mon Jul 18 10:20:28 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Mon, 18 Jul 2022 19:20:28 +0200
Subject: [PATCH 4/4] lib: utils/serial: Ensure baudrate is non-zero before using
In-Reply-To: <20220718172028.2006166-1-ajones@ventanamicro.com>
References: <20220718172028.2006166-1-ajones@ventanamicro.com>
Message-ID: <20220718172028.2006166-5-ajones@ventanamicro.com>

RISC-V doesn't generate exceptions on divide-by-zero, but the result,
all bits set, is not likely what people expect either. In all cases
where we divide by baudrate there's a chance it's zero (when the DT
it came from is "bad"). To avoid difficult to debug situations, leave
baudrate dependent registers alone when baudrate is zero, as, also in
all cases, it appears we can skip initialization of those registers
and still [hopefully] have a functioning UART.

Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
---
 lib/utils/serial/gaisler-uart.c | 2 +-
 lib/utils/serial/shakti-uart.c  | 8 ++++++--
 lib/utils/serial/sifive-uart.c  | 2 +-
 lib/utils/serial/uart8250.c     | 7 +++++--
 4 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/lib/utils/serial/gaisler-uart.c b/lib/utils/serial/gaisler-uart.c
index 5f30ee43b719..eec75cca0c98 100644
--- a/lib/utils/serial/gaisler-uart.c
+++ b/lib/utils/serial/gaisler-uart.c
@@ -70,7 +70,7 @@ int gaisler_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
 	uart_base = (volatile char *)base;
 
 	/* Configure baudrate */
-	if (in_freq)
+	if (in_freq && baudrate)
 		set_reg(UART_REG_SCALER, in_freq / (baudrate * 8 + 7));
 
 	ctrl = get_reg(UART_REG_CTRL);
diff --git a/lib/utils/serial/shakti-uart.c b/lib/utils/serial/shakti-uart.c
index 5f2fe75c9f33..2f09f141b9cf 100644
--- a/lib/utils/serial/shakti-uart.c
+++ b/lib/utils/serial/shakti-uart.c
@@ -47,8 +47,12 @@ static struct sbi_console_device shakti_console = {
 int shakti_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
 {
 	uart_base = (volatile char *)base;
-	u16 baud = (u16)(in_freq/(16 * baudrate));
-	writew(baud, uart_base + REG_BAUD);
+	u16 baud;
+
+	if (baudrate) {
+		baud = (u16)(in_freq / (16 * baudrate));
+		writew(baud, uart_base + REG_BAUD);
+	}
 
 	sbi_console_set_device(&shakti_console);
 
diff --git a/lib/utils/serial/sifive-uart.c b/lib/utils/serial/sifive-uart.c
index 7078611a5274..3581d47a6d4b 100644
--- a/lib/utils/serial/sifive-uart.c
+++ b/lib/utils/serial/sifive-uart.c
@@ -97,7 +97,7 @@ int sifive_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
 	uart_baudrate = baudrate;
 
 	/* Configure baudrate */
-	if (in_freq)
+	if (in_freq && baudrate)
 		set_reg(UART_REG_DIV, uart_min_clk_divisor(in_freq, baudrate));
 
 	/* Disable interrupts */
diff --git a/lib/utils/serial/uart8250.c b/lib/utils/serial/uart8250.c
index 38ea11af4d31..99bf1bf733f2 100644
--- a/lib/utils/serial/uart8250.c
+++ b/lib/utils/serial/uart8250.c
@@ -93,7 +93,7 @@ static struct sbi_console_device uart8250_console = {
 int uart8250_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift,
 		  u32 reg_width, u32 reg_offset)
 {
-	u16 bdiv;
+	u16 bdiv = 0;
 
 	uart8250_base      = (volatile char *)base + reg_offset;
 	uart8250_reg_shift = reg_shift;
@@ -101,7 +101,10 @@ int uart8250_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift,
 	uart8250_in_freq   = in_freq;
 	uart8250_baudrate  = baudrate;
 
-	bdiv = (uart8250_in_freq + 8 * uart8250_baudrate) / (16 * uart8250_baudrate);
+	if (uart8250_baudrate) {
+		bdiv = (uart8250_in_freq + 8 * uart8250_baudrate) /
+		       (16 * uart8250_baudrate);
+	}
 
 	/* Disable all interrupts */
 	set_reg(UART_IER_OFFSET, 0x00);
-- 
2.36.1



From ajones at ventanamicro.com  Mon Jul 18 10:20:25 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Mon, 18 Jul 2022 19:20:25 +0200
Subject: [PATCH 1/4] lib: utils/fdt: Factor out common uart node code
In-Reply-To: <20220718172028.2006166-1-ajones@ventanamicro.com>
References: <20220718172028.2006166-1-ajones@ventanamicro.com>
Message-ID: <20220718172028.2006166-2-ajones@ventanamicro.com>

Factor out the common code used by the fdt UART node parsers,
allowing us to drop duplicate code.

Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
---
 lib/utils/fdt/fdt_helper.c | 131 +++++++++++--------------------------
 1 file changed, 39 insertions(+), 92 deletions(-)

diff --git a/lib/utils/fdt/fdt_helper.c b/lib/utils/fdt/fdt_helper.c
index 2bdb1ef23e55..c6abf804014d 100644
--- a/lib/utils/fdt/fdt_helper.c
+++ b/lib/utils/fdt/fdt_helper.c
@@ -314,8 +314,10 @@ int fdt_parse_timebase_frequency(void *fdt, unsigned long *freq)
 	return 0;
 }
 
-int fdt_parse_gaisler_uart_node(void *fdt, int nodeoffset,
-				struct platform_uart_data *uart)
+static int fdt_parse_uart_node_common(void *fdt, int nodeoffset,
+				      struct platform_uart_data *uart,
+				      unsigned long default_freq,
+				      unsigned long default_baud)
 {
 	int len, rc;
 	const fdt32_t *val;
@@ -338,13 +340,28 @@ int fdt_parse_gaisler_uart_node(void *fdt, int nodeoffset,
 	if (len > 0 && val)
 		uart->freq = fdt32_to_cpu(*val);
 	else
-		uart->freq = DEFAULT_UART_FREQ;
+		uart->freq = default_freq;
 
 	val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "current-speed", &len);
 	if (len > 0 && val)
 		uart->baud = fdt32_to_cpu(*val);
 	else
-		uart->baud = DEFAULT_UART_BAUD;
+		uart->baud = default_baud;
+
+	return 0;
+}
+
+int fdt_parse_gaisler_uart_node(void *fdt, int nodeoffset,
+				struct platform_uart_data *uart)
+{
+	int rc;
+
+	rc = fdt_parse_uart_node_common(fdt, nodeoffset, uart,
+					DEFAULT_UART_FREQ,
+					DEFAULT_UART_BAUD);
+
+	if (rc)
+		return rc;
 
 	/* For Gaisler APBUART, the reg-shift and reg-io-width are fixed .*/
 	uart->reg_shift	   = DEFAULT_UART_REG_SHIFT;
@@ -356,69 +373,26 @@ int fdt_parse_gaisler_uart_node(void *fdt, int nodeoffset,
 int fdt_parse_shakti_uart_node(void *fdt, int nodeoffset,
 			       struct platform_uart_data *uart)
 {
-	int len, rc;
-	const fdt32_t *val;
-	uint64_t reg_addr, reg_size;
-
-	if (nodeoffset < 0 || !uart || !fdt)
-		return SBI_ENODEV;
-
-	rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
-				    &reg_addr, &reg_size);
-	if (rc < 0 || !reg_addr || !reg_size)
-		return SBI_ENODEV;
-	uart->addr = reg_addr;
+	int rc;
 
-	/**
-	 * UART address is mandaotry. clock-frequency and current-speed
-	 * may not be present. Don't return error.
-	 */
-	val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "clock-frequency", &len);
-	if (len > 0 && val)
-		uart->freq = fdt32_to_cpu(*val);
-	else
-		uart->freq = DEFAULT_SHAKTI_UART_FREQ;
+	rc = fdt_parse_uart_node_common(fdt, nodeoffset, uart,
+					DEFAULT_SHAKTI_UART_FREQ,
+					DEFAULT_SHAKTI_UART_BAUD);
 
-	val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "current-speed", &len);
-	if (len > 0 && val)
-		uart->baud = fdt32_to_cpu(*val);
-	else
-		uart->baud = DEFAULT_SHAKTI_UART_BAUD;
-
-	return 0;
+	return rc ? : 0;
 }
 
 int fdt_parse_sifive_uart_node(void *fdt, int nodeoffset,
 			       struct platform_uart_data *uart)
 {
-	int len, rc;
-	const fdt32_t *val;
-	uint64_t reg_addr, reg_size;
-
-	if (nodeoffset < 0 || !uart || !fdt)
-		return SBI_ENODEV;
-
-	rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
-				    &reg_addr, &reg_size);
-	if (rc < 0 || !reg_addr || !reg_size)
-		return SBI_ENODEV;
-	uart->addr = reg_addr;
+	int rc;
 
-	/**
-	 * UART address is mandaotry. clock-frequency and current-speed
-	 * may not be present. Don't return error.
-	 */
-	val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "clock-frequency", &len);
-	if (len > 0 && val)
-		uart->freq = fdt32_to_cpu(*val);
-	else
-		uart->freq = DEFAULT_SIFIVE_UART_FREQ;
+	rc = fdt_parse_uart_node_common(fdt, nodeoffset, uart,
+					DEFAULT_SIFIVE_UART_FREQ,
+					DEFAULT_SIFIVE_UART_BAUD);
 
-	val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "current-speed", &len);
-	if (len > 0 && val)
-		uart->baud = fdt32_to_cpu(*val);
-	else
-		uart->baud = DEFAULT_SIFIVE_UART_BAUD;
+	if (rc)
+		return rc;
 
 	/* For SiFive UART, the reg-shift and reg-io-width are fixed .*/
 	uart->reg_shift = DEFAULT_SIFIVE_UART_REG_SHIFT;
@@ -432,32 +406,13 @@ int fdt_parse_uart8250_node(void *fdt, int nodeoffset,
 {
 	int len, rc;
 	const fdt32_t *val;
-	uint64_t reg_addr, reg_size;
-
-	if (nodeoffset < 0 || !uart || !fdt)
-		return SBI_ENODEV;
-
-	rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
-				    &reg_addr, &reg_size);
-	if (rc < 0 || !reg_addr || !reg_size)
-		return SBI_ENODEV;
-	uart->addr = reg_addr;
 
-	/**
-	 * UART address is mandaotry. clock-frequency and current-speed
-	 * may not be present. Don't return error.
-	 */
-	val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "clock-frequency", &len);
-	if (len > 0 && val)
-		uart->freq = fdt32_to_cpu(*val);
-	else
-		uart->freq = DEFAULT_UART_FREQ;
+	rc = fdt_parse_uart_node_common(fdt, nodeoffset, uart,
+					DEFAULT_UART_FREQ,
+					DEFAULT_UART_BAUD);
 
-	val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "current-speed", &len);
-	if (len > 0 && val)
-		uart->baud = fdt32_to_cpu(*val);
-	else
-		uart->baud = DEFAULT_UART_BAUD;
+	if (rc)
+		return rc;
 
 	val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "reg-shift", &len);
 	if (len > 0 && val)
@@ -499,18 +454,10 @@ int fdt_parse_xlnx_uartlite_node(void *fdt, int nodeoffset,
 			       struct platform_uart_data *uart)
 {
 	int rc;
-	uint64_t reg_addr, reg_size;
 
-	if (nodeoffset < 0 || !uart || !fdt)
-		return SBI_ENODEV;
+	rc = fdt_parse_uart_node_common(fdt, nodeoffset, uart, 0, 0);
 
-	rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
-				    &reg_addr, &reg_size);
-	if (rc < 0 || !reg_addr || !reg_size)
-		return SBI_ENODEV;
-	uart->addr = reg_addr;
-
-	return 0;
+	return rc ? : 0;
 }
 
 int fdt_parse_aplic_node(void *fdt, int nodeoff, struct aplic_data *aplic)
-- 
2.36.1



From ajones at ventanamicro.com  Mon Jul 18 10:20:27 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Mon, 18 Jul 2022 19:20:27 +0200
Subject: [PATCH 3/4] lib: serial: Clean up coding style in sifive-uart.c
In-Reply-To: <20220718172028.2006166-1-ajones@ventanamicro.com>
References: <20220718172028.2006166-1-ajones@ventanamicro.com>
Message-ID: <20220718172028.2006166-4-ajones@ventanamicro.com>

Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
---
 lib/utils/serial/sifive-uart.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/lib/utils/serial/sifive-uart.c b/lib/utils/serial/sifive-uart.c
index 9478a77f8054..7078611a5274 100644
--- a/lib/utils/serial/sifive-uart.c
+++ b/lib/utils/serial/sifive-uart.c
@@ -48,12 +48,12 @@ static inline unsigned int uart_min_clk_divisor(uint64_t in_freq,
 						uint64_t max_target_hz)
 {
 	uint64_t quotient = (in_freq + max_target_hz - 1) / (max_target_hz);
+
 	/* Avoid underflow */
-	if (quotient == 0) {
+	if (quotient == 0)
 		return 0;
-	} else {
+	else
 		return quotient - 1;
-	}
 }
 
 static u32 get_reg(u32 num)
@@ -77,8 +77,10 @@ static void sifive_uart_putc(char ch)
 static int sifive_uart_getc(void)
 {
 	u32 ret = get_reg(UART_REG_RXFIFO);
+
 	if (!(ret & UART_RXFIFO_EMPTY))
 		return ret & UART_RXFIFO_DATA;
+
 	return -1;
 }
 
@@ -97,10 +99,13 @@ int sifive_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
 	/* Configure baudrate */
 	if (in_freq)
 		set_reg(UART_REG_DIV, uart_min_clk_divisor(in_freq, baudrate));
+
 	/* Disable interrupts */
 	set_reg(UART_REG_IE, 0);
+
 	/* Enable TX */
 	set_reg(UART_REG_TXCTRL, UART_TXCTRL_TXEN);
+
 	/* Enable Rx */
 	set_reg(UART_REG_RXCTRL, UART_RXCTRL_RXEN);
 
-- 
2.36.1



From jrtc27 at jrtc27.com  Mon Jul 18 10:42:40 2022
From: jrtc27 at jrtc27.com (Jessica Clarke)
Date: Mon, 18 Jul 2022 18:42:40 +0100
Subject: [PATCH 2/4] lib: utils/serial: Initialize platform_uart_data to
 zero
In-Reply-To: <20220718172028.2006166-3-ajones@ventanamicro.com>
References: <20220718172028.2006166-1-ajones@ventanamicro.com>
 <20220718172028.2006166-3-ajones@ventanamicro.com>
Message-ID: <1D1800C8-676E-43E0-9F22-29DC5862E994@jrtc27.com>

On 18 Jul 2022, at 18:20, Andrew Jones <ajones at ventanamicro.com> wrote:
> 
> While it doesn't look like there are any current cases of using
> uninitialized data, let's zero all the UART data members to be
> safe. Zero may not actually be better than a random number in
> some cases, so all structure members should still be validated
> before use, but at least zero is usually easier to debug than
> some random stack garbage...

If fdt_parse_foo_uart_node leaves the platform_uart_data uninitialised
(at least, parts that matter) then that seems like a bug to me. This
just masks such bugs from any kind of sanitiser...

Jess

> Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
> ---
> lib/utils/serial/fdt_serial_gaisler.c       | 2 +-
> lib/utils/serial/fdt_serial_shakti.c        | 2 +-
> lib/utils/serial/fdt_serial_sifive.c        | 2 +-
> lib/utils/serial/fdt_serial_uart8250.c      | 2 +-
> lib/utils/serial/fdt_serial_xlnx_uartlite.c | 2 +-
> platform/fpga/openpiton/platform.c          | 2 +-
> 6 files changed, 6 insertions(+), 6 deletions(-)
> 
> diff --git a/lib/utils/serial/fdt_serial_gaisler.c b/lib/utils/serial/fdt_serial_gaisler.c
> index 9a9f9a8b7962..74988e34bcd8 100644
> --- a/lib/utils/serial/fdt_serial_gaisler.c
> +++ b/lib/utils/serial/fdt_serial_gaisler.c
> @@ -15,7 +15,7 @@ static int serial_gaisler_init(void *fdt, int nodeoff,
> 			       const struct fdt_match *match)
> {
> 	int rc;
> -	struct platform_uart_data uart;
> +	struct platform_uart_data uart = { 0 };
> 
> 	rc = fdt_parse_gaisler_uart_node(fdt, nodeoff, &uart);
> 	if (rc)
> diff --git a/lib/utils/serial/fdt_serial_shakti.c b/lib/utils/serial/fdt_serial_shakti.c
> index 4f91419ef53c..0e056303dbb5 100644
> --- a/lib/utils/serial/fdt_serial_shakti.c
> +++ b/lib/utils/serial/fdt_serial_shakti.c
> @@ -13,7 +13,7 @@ static int serial_shakti_init(void *fdt, int nodeoff,
> 				const struct fdt_match *match)
> {
> 	int rc;
> -	struct platform_uart_data uart;
> +	struct platform_uart_data uart = { 0 };
> 
> 	rc = fdt_parse_shakti_uart_node(fdt, nodeoff, &uart);
> 	if (rc)
> diff --git a/lib/utils/serial/fdt_serial_sifive.c b/lib/utils/serial/fdt_serial_sifive.c
> index f4c833c1573d..3ca10913eee3 100644
> --- a/lib/utils/serial/fdt_serial_sifive.c
> +++ b/lib/utils/serial/fdt_serial_sifive.c
> @@ -15,7 +15,7 @@ static int serial_sifive_init(void *fdt, int nodeoff,
> 				const struct fdt_match *match)
> {
> 	int rc;
> -	struct platform_uart_data uart;
> +	struct platform_uart_data uart = { 0 };
> 
> 	rc = fdt_parse_sifive_uart_node(fdt, nodeoff, &uart);
> 	if (rc)
> diff --git a/lib/utils/serial/fdt_serial_uart8250.c b/lib/utils/serial/fdt_serial_uart8250.c
> index 544b7416de42..0b95f2d9e44e 100644
> --- a/lib/utils/serial/fdt_serial_uart8250.c
> +++ b/lib/utils/serial/fdt_serial_uart8250.c
> @@ -15,7 +15,7 @@ static int serial_uart8250_init(void *fdt, int nodeoff,
> 				const struct fdt_match *match)
> {
> 	int rc;
> -	struct platform_uart_data uart;
> +	struct platform_uart_data uart = { 0 };
> 
> 	rc = fdt_parse_uart8250_node(fdt, nodeoff, &uart);
> 	if (rc)
> diff --git a/lib/utils/serial/fdt_serial_xlnx_uartlite.c b/lib/utils/serial/fdt_serial_xlnx_uartlite.c
> index 466e16e82d8c..9f04aea3673b 100644
> --- a/lib/utils/serial/fdt_serial_xlnx_uartlite.c
> +++ b/lib/utils/serial/fdt_serial_xlnx_uartlite.c
> @@ -15,7 +15,7 @@ static int serial_xlnx_uartlite_init(void *fdt, int nodeoff,
> 				const struct fdt_match *match)
> {
> 	int rc;
> -	struct platform_uart_data uart;
> +	struct platform_uart_data uart = { 0 };
> 
> 	rc = fdt_parse_xlnx_uartlite_node(fdt, nodeoff, &uart);
> 	if (rc)
> diff --git a/platform/fpga/openpiton/platform.c b/platform/fpga/openpiton/platform.c
> index 7ca21236bfef..5ff7d200aba9 100644
> --- a/platform/fpga/openpiton/platform.c
> +++ b/platform/fpga/openpiton/platform.c
> @@ -69,7 +69,7 @@ static struct aclint_mtimer_data mtimer = {
> static int openpiton_early_init(bool cold_boot)
> {
> 	void *fdt;
> -	struct platform_uart_data uart_data;
> +	struct platform_uart_data uart_data = { 0 };
> 	struct plic_data plic_data;
> 	unsigned long aclint_freq;
> 	uint64_t clint_addr;
> -- 
> 2.36.1
> 
> 
> -- 
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi



From ajones at ventanamicro.com  Mon Jul 18 11:35:33 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Mon, 18 Jul 2022 20:35:33 +0200
Subject: [PATCH 2/4] lib: utils/serial: Initialize platform_uart_data to
 zero
In-Reply-To: <1D1800C8-676E-43E0-9F22-29DC5862E994@jrtc27.com>
References: <20220718172028.2006166-1-ajones@ventanamicro.com>
 <20220718172028.2006166-3-ajones@ventanamicro.com>
 <1D1800C8-676E-43E0-9F22-29DC5862E994@jrtc27.com>
Message-ID: <20220718183533.7pdyuyfpae6xjfxc@kamzik>

On Mon, Jul 18, 2022 at 06:42:40PM +0100, Jessica Clarke wrote:
> On 18 Jul 2022, at 18:20, Andrew Jones <ajones at ventanamicro.com> wrote:
> > 
> > While it doesn't look like there are any current cases of using
> > uninitialized data, let's zero all the UART data members to be
> > safe. Zero may not actually be better than a random number in
> > some cases, so all structure members should still be validated
> > before use, but at least zero is usually easier to debug than
> > some random stack garbage...
> 
> If fdt_parse_foo_uart_node leaves the platform_uart_data uninitialised
> (at least, parts that matter) then that seems like a bug to me. This
> just masks such bugs from any kind of sanitiser...

Hi Jess,

That's an interesting approach. Which tool(s) will point that out?
I just tried compiling with both gcc[1] and clang[2], noting that we
compile with -Wall, but neither complained when I purposely commented
out a used UART field.

[1] https://github.com/riscv/riscv-gnu-toolchain
[2] clang-14.0.0-1.fc36.x86_64

Thanks,
drew

> 
> Jess
> 
> > Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
> > ---
> > lib/utils/serial/fdt_serial_gaisler.c       | 2 +-
> > lib/utils/serial/fdt_serial_shakti.c        | 2 +-
> > lib/utils/serial/fdt_serial_sifive.c        | 2 +-
> > lib/utils/serial/fdt_serial_uart8250.c      | 2 +-
> > lib/utils/serial/fdt_serial_xlnx_uartlite.c | 2 +-
> > platform/fpga/openpiton/platform.c          | 2 +-
> > 6 files changed, 6 insertions(+), 6 deletions(-)
> > 
> > diff --git a/lib/utils/serial/fdt_serial_gaisler.c b/lib/utils/serial/fdt_serial_gaisler.c
> > index 9a9f9a8b7962..74988e34bcd8 100644
> > --- a/lib/utils/serial/fdt_serial_gaisler.c
> > +++ b/lib/utils/serial/fdt_serial_gaisler.c
> > @@ -15,7 +15,7 @@ static int serial_gaisler_init(void *fdt, int nodeoff,
> > 			       const struct fdt_match *match)
> > {
> > 	int rc;
> > -	struct platform_uart_data uart;
> > +	struct platform_uart_data uart = { 0 };
> > 
> > 	rc = fdt_parse_gaisler_uart_node(fdt, nodeoff, &uart);
> > 	if (rc)
> > diff --git a/lib/utils/serial/fdt_serial_shakti.c b/lib/utils/serial/fdt_serial_shakti.c
> > index 4f91419ef53c..0e056303dbb5 100644
> > --- a/lib/utils/serial/fdt_serial_shakti.c
> > +++ b/lib/utils/serial/fdt_serial_shakti.c
> > @@ -13,7 +13,7 @@ static int serial_shakti_init(void *fdt, int nodeoff,
> > 				const struct fdt_match *match)
> > {
> > 	int rc;
> > -	struct platform_uart_data uart;
> > +	struct platform_uart_data uart = { 0 };
> > 
> > 	rc = fdt_parse_shakti_uart_node(fdt, nodeoff, &uart);
> > 	if (rc)
> > diff --git a/lib/utils/serial/fdt_serial_sifive.c b/lib/utils/serial/fdt_serial_sifive.c
> > index f4c833c1573d..3ca10913eee3 100644
> > --- a/lib/utils/serial/fdt_serial_sifive.c
> > +++ b/lib/utils/serial/fdt_serial_sifive.c
> > @@ -15,7 +15,7 @@ static int serial_sifive_init(void *fdt, int nodeoff,
> > 				const struct fdt_match *match)
> > {
> > 	int rc;
> > -	struct platform_uart_data uart;
> > +	struct platform_uart_data uart = { 0 };
> > 
> > 	rc = fdt_parse_sifive_uart_node(fdt, nodeoff, &uart);
> > 	if (rc)
> > diff --git a/lib/utils/serial/fdt_serial_uart8250.c b/lib/utils/serial/fdt_serial_uart8250.c
> > index 544b7416de42..0b95f2d9e44e 100644
> > --- a/lib/utils/serial/fdt_serial_uart8250.c
> > +++ b/lib/utils/serial/fdt_serial_uart8250.c
> > @@ -15,7 +15,7 @@ static int serial_uart8250_init(void *fdt, int nodeoff,
> > 				const struct fdt_match *match)
> > {
> > 	int rc;
> > -	struct platform_uart_data uart;
> > +	struct platform_uart_data uart = { 0 };
> > 
> > 	rc = fdt_parse_uart8250_node(fdt, nodeoff, &uart);
> > 	if (rc)
> > diff --git a/lib/utils/serial/fdt_serial_xlnx_uartlite.c b/lib/utils/serial/fdt_serial_xlnx_uartlite.c
> > index 466e16e82d8c..9f04aea3673b 100644
> > --- a/lib/utils/serial/fdt_serial_xlnx_uartlite.c
> > +++ b/lib/utils/serial/fdt_serial_xlnx_uartlite.c
> > @@ -15,7 +15,7 @@ static int serial_xlnx_uartlite_init(void *fdt, int nodeoff,
> > 				const struct fdt_match *match)
> > {
> > 	int rc;
> > -	struct platform_uart_data uart;
> > +	struct platform_uart_data uart = { 0 };
> > 
> > 	rc = fdt_parse_xlnx_uartlite_node(fdt, nodeoff, &uart);
> > 	if (rc)
> > diff --git a/platform/fpga/openpiton/platform.c b/platform/fpga/openpiton/platform.c
> > index 7ca21236bfef..5ff7d200aba9 100644
> > --- a/platform/fpga/openpiton/platform.c
> > +++ b/platform/fpga/openpiton/platform.c
> > @@ -69,7 +69,7 @@ static struct aclint_mtimer_data mtimer = {
> > static int openpiton_early_init(bool cold_boot)
> > {
> > 	void *fdt;
> > -	struct platform_uart_data uart_data;
> > +	struct platform_uart_data uart_data = { 0 };
> > 	struct plic_data plic_data;
> > 	unsigned long aclint_freq;
> > 	uint64_t clint_addr;
> > -- 
> > 2.36.1
> > 
> > 
> > -- 
> > opensbi mailing list
> > opensbi at lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/opensbi
> 


From jrtc27 at jrtc27.com  Mon Jul 18 11:39:23 2022
From: jrtc27 at jrtc27.com (Jessica Clarke)
Date: Mon, 18 Jul 2022 19:39:23 +0100
Subject: [PATCH 2/4] lib: utils/serial: Initialize platform_uart_data to
 zero
In-Reply-To: <20220718183533.7pdyuyfpae6xjfxc@kamzik>
References: <20220718172028.2006166-1-ajones@ventanamicro.com>
 <20220718172028.2006166-3-ajones@ventanamicro.com>
 <1D1800C8-676E-43E0-9F22-29DC5862E994@jrtc27.com>
 <20220718183533.7pdyuyfpae6xjfxc@kamzik>
Message-ID: <CD32E693-6D4E-4F48-AE14-4F47A08AD491@jrtc27.com>

On 18 Jul 2022, at 19:35, Andrew Jones <ajones at ventanamicro.com> wrote:
> 
> On Mon, Jul 18, 2022 at 06:42:40PM +0100, Jessica Clarke wrote:
>> On 18 Jul 2022, at 18:20, Andrew Jones <ajones at ventanamicro.com> wrote:
>>> 
>>> While it doesn't look like there are any current cases of using
>>> uninitialized data, let's zero all the UART data members to be
>>> safe. Zero may not actually be better than a random number in
>>> some cases, so all structure members should still be validated
>>> before use, but at least zero is usually easier to debug than
>>> some random stack garbage...
>> 
>> If fdt_parse_foo_uart_node leaves the platform_uart_data uninitialised
>> (at least, parts that matter) then that seems like a bug to me. This
>> just masks such bugs from any kind of sanitiser...
> 
> Hi Jess,
> 
> That's an interesting approach. Which tool(s) will point that out?
> I just tried compiling with both gcc[1] and clang[2], noting that we
> compile with -Wall, but neither complained when I purposely commented
> out a used UART field.
> 
> [1] https://github.com/riscv/riscv-gnu-toolchain
> [2] clang-14.0.0-1.fc36.x86_64

Like other sanitisers it needs some runtime support, but MSan, i.e.
-fsanitize=memory.

Jess

> Thanks,
> drew
> 
>> 
>> Jess
>> 
>>> Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
>>> ---
>>> lib/utils/serial/fdt_serial_gaisler.c       | 2 +-
>>> lib/utils/serial/fdt_serial_shakti.c        | 2 +-
>>> lib/utils/serial/fdt_serial_sifive.c        | 2 +-
>>> lib/utils/serial/fdt_serial_uart8250.c      | 2 +-
>>> lib/utils/serial/fdt_serial_xlnx_uartlite.c | 2 +-
>>> platform/fpga/openpiton/platform.c          | 2 +-
>>> 6 files changed, 6 insertions(+), 6 deletions(-)
>>> 
>>> diff --git a/lib/utils/serial/fdt_serial_gaisler.c b/lib/utils/serial/fdt_serial_gaisler.c
>>> index 9a9f9a8b7962..74988e34bcd8 100644
>>> --- a/lib/utils/serial/fdt_serial_gaisler.c
>>> +++ b/lib/utils/serial/fdt_serial_gaisler.c
>>> @@ -15,7 +15,7 @@ static int serial_gaisler_init(void *fdt, int nodeoff,
>>> 			       const struct fdt_match *match)
>>> {
>>> 	int rc;
>>> -	struct platform_uart_data uart;
>>> +	struct platform_uart_data uart = { 0 };
>>> 
>>> 	rc = fdt_parse_gaisler_uart_node(fdt, nodeoff, &uart);
>>> 	if (rc)
>>> diff --git a/lib/utils/serial/fdt_serial_shakti.c b/lib/utils/serial/fdt_serial_shakti.c
>>> index 4f91419ef53c..0e056303dbb5 100644
>>> --- a/lib/utils/serial/fdt_serial_shakti.c
>>> +++ b/lib/utils/serial/fdt_serial_shakti.c
>>> @@ -13,7 +13,7 @@ static int serial_shakti_init(void *fdt, int nodeoff,
>>> 				const struct fdt_match *match)
>>> {
>>> 	int rc;
>>> -	struct platform_uart_data uart;
>>> +	struct platform_uart_data uart = { 0 };
>>> 
>>> 	rc = fdt_parse_shakti_uart_node(fdt, nodeoff, &uart);
>>> 	if (rc)
>>> diff --git a/lib/utils/serial/fdt_serial_sifive.c b/lib/utils/serial/fdt_serial_sifive.c
>>> index f4c833c1573d..3ca10913eee3 100644
>>> --- a/lib/utils/serial/fdt_serial_sifive.c
>>> +++ b/lib/utils/serial/fdt_serial_sifive.c
>>> @@ -15,7 +15,7 @@ static int serial_sifive_init(void *fdt, int nodeoff,
>>> 				const struct fdt_match *match)
>>> {
>>> 	int rc;
>>> -	struct platform_uart_data uart;
>>> +	struct platform_uart_data uart = { 0 };
>>> 
>>> 	rc = fdt_parse_sifive_uart_node(fdt, nodeoff, &uart);
>>> 	if (rc)
>>> diff --git a/lib/utils/serial/fdt_serial_uart8250.c b/lib/utils/serial/fdt_serial_uart8250.c
>>> index 544b7416de42..0b95f2d9e44e 100644
>>> --- a/lib/utils/serial/fdt_serial_uart8250.c
>>> +++ b/lib/utils/serial/fdt_serial_uart8250.c
>>> @@ -15,7 +15,7 @@ static int serial_uart8250_init(void *fdt, int nodeoff,
>>> 				const struct fdt_match *match)
>>> {
>>> 	int rc;
>>> -	struct platform_uart_data uart;
>>> +	struct platform_uart_data uart = { 0 };
>>> 
>>> 	rc = fdt_parse_uart8250_node(fdt, nodeoff, &uart);
>>> 	if (rc)
>>> diff --git a/lib/utils/serial/fdt_serial_xlnx_uartlite.c b/lib/utils/serial/fdt_serial_xlnx_uartlite.c
>>> index 466e16e82d8c..9f04aea3673b 100644
>>> --- a/lib/utils/serial/fdt_serial_xlnx_uartlite.c
>>> +++ b/lib/utils/serial/fdt_serial_xlnx_uartlite.c
>>> @@ -15,7 +15,7 @@ static int serial_xlnx_uartlite_init(void *fdt, int nodeoff,
>>> 				const struct fdt_match *match)
>>> {
>>> 	int rc;
>>> -	struct platform_uart_data uart;
>>> +	struct platform_uart_data uart = { 0 };
>>> 
>>> 	rc = fdt_parse_xlnx_uartlite_node(fdt, nodeoff, &uart);
>>> 	if (rc)
>>> diff --git a/platform/fpga/openpiton/platform.c b/platform/fpga/openpiton/platform.c
>>> index 7ca21236bfef..5ff7d200aba9 100644
>>> --- a/platform/fpga/openpiton/platform.c
>>> +++ b/platform/fpga/openpiton/platform.c
>>> @@ -69,7 +69,7 @@ static struct aclint_mtimer_data mtimer = {
>>> static int openpiton_early_init(bool cold_boot)
>>> {
>>> 	void *fdt;
>>> -	struct platform_uart_data uart_data;
>>> +	struct platform_uart_data uart_data = { 0 };
>>> 	struct plic_data plic_data;
>>> 	unsigned long aclint_freq;
>>> 	uint64_t clint_addr;
>>> -- 
>>> 2.36.1
>>> 
>>> 
>>> -- 
>>> opensbi mailing list
>>> opensbi at lists.infradead.org
>>> http://lists.infradead.org/mailman/listinfo/opensbi
>> 



From ajones at ventanamicro.com  Mon Jul 18 11:48:07 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Mon, 18 Jul 2022 20:48:07 +0200
Subject: [PATCH v2 00/14] OpenSBI Kconfig Support
In-Reply-To: <20220718145611.2211638-1-apatel@ventanamicro.com>
References: <20220718145611.2211638-1-apatel@ventanamicro.com>
Message-ID: <20220718184807.dac3r5bvsublyj2n@kamzik>

On Mon, Jul 18, 2022 at 08:25:57PM +0530, Anup Patel wrote:
> In future, we will see more drivers in OpenSBI for system reset, serial port,
> power management, etc platform specific devices. We need platform specific
> compile time configuration to allow users control the size of OpenSBI generic
> firmware binaries by only enabling desired drivers. In other words, users
> should be allowed to restrict the set of RISC-V platforms on which OpenSBI
> generic firmwares built by them can run.
> 
> To address above, we add Kconfig support for OpenSBI platforms using the
> kconfig-frontends project which is a standalone kconfig parser.
> 
> The kconfig-frontends is available as a package/receipe on various distros
> (such as Debian, UBuntu, OpenSUSE, etc) and Yocto project.

Hi Anup,

It doesn't look like Fedora provides that package. Also, is the source
for the packages of those other distros maintained? If I found the
correct source, then it's [1], which hasn't had any attention in five
years, even though there have been hundreds of commits to Linux's
scripts/kconfig. I wonder if we shouldn't consider adopting
scripts/minikconf.py from the QEMU sources and sticking to the kconfig
language subset that parser supports?

[1] http://ymorin.is-a-geek.org/projects/kconfig-frontends

Thanks,
drew

> 
> To try OpenSBI Kconfig support with generic platform, here are few
> example commands:
> make PLATFORM=generic             <-- builds OpenSBI using "defconfig"
> make PLATFORM=generic menuconfig  <-- launches graphical interface for changes
> 
> These patches can also be found in the kconfig_v2 branch at:
> https://github.com/avpatel/opensbi.git
> 
> Changes since v1:
>  - Fix SPDX license specifier in new Kconfig files
> 
> Anup Patel (14):
>   Makefile: Add initial kconfig support for each platform
>   Makefile: Compile lib/utils sources separately for each platform
>   lib: utils/serial: Use kconfig for enabling/disabling drivers
>   lib: utils/reset: Use kconfig for enabling/disabling drivers
>   lib: utils/sys: Use kconfig for enabling/disabling drivers
>   lib: utils/timer: Use kconfig for enabling/disabling drivers
>   lib: utils/ipi: Use kconfig for enabling/disabling drivers
>   lib: utils/irqchip: Use kconfig for enabling/disabling drivers
>   lib: utils/i2c: Use kconfig for enabling/disabling drivers
>   lib: utils/gpio: Use kconfig for enabling/disabling drivers
>   lib: utils/fdt: Use kconfig for enabling/disabling
>   platform: generic: Use kconfig for enabling/disabling overrides
>   platform: Remove redundant config.mk from all platforms
>   docs: Update documentation for kconfig support
> 
>  Kconfig                                   |  23 +++++
>  Makefile                                  | 102 ++++++++++++++++------
>  README.md                                 |  25 +++++-
>  docs/firmware/fw_dynamic.md               |   2 +-
>  docs/firmware/fw_jump.md                  |   4 +-
>  docs/firmware/fw_payload.md               |   4 +-
>  docs/library_usage.md                     |   6 +-
>  docs/platform/platform.md                 |   4 +-
>  docs/platform_guide.md                    |   9 +-
>  firmware/Kconfig                          |   1 +
>  include/sbi_utils/fdt/fdt_domain.h        |   9 ++
>  include/sbi_utils/fdt/fdt_pmu.h           |  10 +++
>  include/sbi_utils/ipi/fdt_ipi.h           |   9 ++
>  include/sbi_utils/irqchip/fdt_irqchip.h   |  10 +++
>  include/sbi_utils/irqchip/imsic.h         |  10 +++
>  include/sbi_utils/reset/fdt_reset.h       |  12 +++
>  include/sbi_utils/serial/fdt_serial.h     |   8 ++
>  include/sbi_utils/timer/fdt_timer.h       |   9 ++
>  lib/utils/Kconfig                         |  25 ++++++
>  lib/utils/fdt/Kconfig                     |  18 ++++
>  lib/utils/fdt/objects.mk                  |   8 +-
>  lib/utils/gpio/Kconfig                    |  23 +++++
>  lib/utils/gpio/objects.mk                 |  10 +--
>  lib/utils/i2c/Kconfig                     |  23 +++++
>  lib/utils/i2c/objects.mk                  |  10 +--
>  lib/utils/ipi/Kconfig                     |  23 +++++
>  lib/utils/ipi/objects.mk                  |  10 +--
>  lib/utils/irqchip/Kconfig                 |  41 +++++++++
>  lib/utils/irqchip/objects.mk              |  22 ++---
>  lib/utils/libfdt/Kconfig                  |   5 ++
>  lib/utils/libfdt/objects.mk               |   4 +-
>  lib/utils/reset/Kconfig                   |  37 ++++++++
>  lib/utils/reset/objects.mk                |  28 +++---
>  lib/utils/serial/Kconfig                  |  73 ++++++++++++++++
>  lib/utils/serial/objects.mk               |  44 +++++-----
>  lib/utils/sys/Kconfig                     |  13 +++
>  lib/utils/sys/objects.mk                  |   4 +-
>  lib/utils/timer/Kconfig                   |  23 +++++
>  lib/utils/timer/objects.mk                |  10 +--
>  platform/andes/ae350/Kconfig              |   8 ++
>  platform/andes/ae350/config.mk            |  36 --------
>  platform/andes/ae350/configs/defconfig    |   0
>  platform/andes/ae350/objects.mk           |  29 ++++++
>  platform/fpga/ariane/Kconfig              |  10 +++
>  platform/fpga/ariane/config.mk            |  36 --------
>  platform/fpga/ariane/configs/defconfig    |   0
>  platform/fpga/ariane/objects.mk           |  35 ++++++++
>  platform/fpga/openpiton/Kconfig           |  10 +++
>  platform/fpga/openpiton/config.mk         |  35 --------
>  platform/fpga/openpiton/configs/defconfig |   0
>  platform/fpga/openpiton/objects.mk        |  35 ++++++++
>  platform/generic/Kconfig                  |  26 ++++++
>  platform/generic/allwinner/objects.mk     |   4 +-
>  platform/generic/config.mk                |  40 ---------
>  platform/generic/configs/defconfig        |  29 ++++++
>  platform/generic/objects.mk               |  33 +++++++
>  platform/generic/sifive/objects.mk        |   8 +-
>  platform/kendryte/k210/Kconfig            |  10 +++
>  platform/kendryte/k210/config.mk          |  19 ----
>  platform/kendryte/k210/configs/defconfig  |   0
>  platform/kendryte/k210/objects.mk         |  12 +++
>  platform/nuclei/ux600/Kconfig             |  10 +++
>  platform/nuclei/ux600/config.mk           |  30 -------
>  platform/nuclei/ux600/configs/defconfig   |   0
>  platform/nuclei/ux600/objects.mk          |  22 +++++
>  platform/nuclei/ux600/platform.c          |   1 -
>  platform/template/Kconfig                 |   9 ++
>  platform/template/config.mk               |  77 ----------------
>  platform/template/configs/defconfig       |   0
>  platform/template/objects.mk              |  72 +++++++++++++++
>  platform/template/platform.c              |   2 +-
>  scripts/create-binary-archive.sh          |   2 +-
>  72 files changed, 948 insertions(+), 403 deletions(-)
>  create mode 100644 Kconfig
>  create mode 100644 firmware/Kconfig
>  create mode 100644 lib/utils/Kconfig
>  create mode 100644 lib/utils/fdt/Kconfig
>  create mode 100644 lib/utils/gpio/Kconfig
>  create mode 100644 lib/utils/i2c/Kconfig
>  create mode 100644 lib/utils/ipi/Kconfig
>  create mode 100644 lib/utils/irqchip/Kconfig
>  create mode 100644 lib/utils/libfdt/Kconfig
>  create mode 100644 lib/utils/reset/Kconfig
>  create mode 100644 lib/utils/serial/Kconfig
>  create mode 100644 lib/utils/sys/Kconfig
>  create mode 100644 lib/utils/timer/Kconfig
>  create mode 100644 platform/andes/ae350/Kconfig
>  delete mode 100644 platform/andes/ae350/config.mk
>  create mode 100644 platform/andes/ae350/configs/defconfig
>  create mode 100644 platform/fpga/ariane/Kconfig
>  delete mode 100644 platform/fpga/ariane/config.mk
>  create mode 100644 platform/fpga/ariane/configs/defconfig
>  create mode 100644 platform/fpga/openpiton/Kconfig
>  delete mode 100644 platform/fpga/openpiton/config.mk
>  create mode 100644 platform/fpga/openpiton/configs/defconfig
>  create mode 100644 platform/generic/Kconfig
>  delete mode 100644 platform/generic/config.mk
>  create mode 100644 platform/generic/configs/defconfig
>  create mode 100644 platform/kendryte/k210/Kconfig
>  delete mode 100644 platform/kendryte/k210/config.mk
>  create mode 100644 platform/kendryte/k210/configs/defconfig
>  create mode 100644 platform/nuclei/ux600/Kconfig
>  delete mode 100644 platform/nuclei/ux600/config.mk
>  create mode 100644 platform/nuclei/ux600/configs/defconfig
>  create mode 100644 platform/template/Kconfig
>  delete mode 100644 platform/template/config.mk
>  create mode 100644 platform/template/configs/defconfig
> 
> -- 
> 2.34.1
> 
> 
> -- 
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi


From atishp at rivosinc.com  Mon Jul 18 13:04:57 2022
From: atishp at rivosinc.com (Atish Patra)
Date: Mon, 18 Jul 2022 13:04:57 -0700
Subject: [PATCH 1/2] lib: sbi: Add the bound check for firmware events
Message-ID: <20220718200458.3015600-1-atishp@rivosinc.com>

Currently, there is no sanity check for firmware event code. We don't see
any issue as Linux kernel driver does a bound check on firmware events
already. However, OpenSBI can't assume sane supervisor mode software
always. Thus, an invalid event idx can cause a buffer overflow error.

Improve the sanity check for firmware events.

Signed-off-by: Atish Patra <atishp at rivosinc.com>
---
 lib/sbi/sbi_pmu.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
index 3f5fd1031b25..65bc9dd63f6b 100644
--- a/lib/sbi/sbi_pmu.c
+++ b/lib/sbi/sbi_pmu.c
@@ -123,6 +123,9 @@ static int pmu_ctr_validate(uint32_t cidx, uint32_t *event_idx_code)
 		return SBI_EINVAL;
 
 	*event_idx_code = get_cidx_code(event_idx_val);
+	if ((event_idx_type == SBI_PMU_EVENT_TYPE_FW) &&
+	    (*event_idx_code >= SBI_PMU_FW_MAX))
+		return SBI_EINVAL;
 
 	return event_idx_type;
 }
@@ -652,6 +655,8 @@ skip_match:
 			pmu_ctr_start_hw(ctr_idx, 0, false);
 	} else if (event_type == SBI_PMU_EVENT_TYPE_FW) {
 		fw_evt_code = get_cidx_code(event_idx);
+		if (unlikely(fw_evt_code >= SBI_PMU_FW_MAX))
+			return SBI_ENOTSUPP;
 		fevent = &fw_event_map[hartid][fw_evt_code];
 		if (flags & SBI_PMU_CFG_FLAG_CLEAR_VALUE)
 			fevent->curr_count = 0;
@@ -721,7 +726,7 @@ static void pmu_reset_event_map(u32 hartid)
 	/* Initialize the counter to event mapping table */
 	for (j = 3; j < total_ctrs; j++)
 		active_events[hartid][j] = SBI_PMU_EVENT_IDX_INVALID;
-	for (j = 0; j < SBI_PMU_FW_CTR_MAX; j++)
+	for (j = 0; j < SBI_PMU_FW_EVENT_MAX; j++)
 		sbi_memset(&fw_event_map[hartid][j], 0,
 			   sizeof(struct sbi_pmu_fw_event));
 }
-- 
2.25.1



From atishp at rivosinc.com  Mon Jul 18 13:04:58 2022
From: atishp at rivosinc.com (Atish Patra)
Date: Mon, 18 Jul 2022 13:04:58 -0700
Subject: [PATCH 2/2] lib: sbi: Fix counter index sanity check
In-Reply-To: <20220718200458.3015600-1-atishp@rivosinc.com>
References: <20220718200458.3015600-1-atishp@rivosinc.com>
Message-ID: <20220718200458.3015600-2-atishp@rivosinc.com>

The current implementation computes the possible counter range
by doing a left shift of counter base. However, this may overflow depending
on the counter base value. In case of overflow, the highest counter id
may be computed incorrectly. As per the SBI specification, the respective
function should return an error if any of the counter is not valid.

Fix the counter index check by avoiding left shifting while doing the
sanity checks.

Signed-off-by: Atish Patra <atishp at rivosinc.com>
---
 lib/sbi/sbi_pmu.c | 33 +++++++++++++++++----------------
 1 file changed, 17 insertions(+), 16 deletions(-)

diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
index 65bc9dd63f6b..29e7e2954ae3 100644
--- a/lib/sbi/sbi_pmu.c
+++ b/lib/sbi/sbi_pmu.c
@@ -346,25 +346,26 @@ int sbi_pmu_ctr_start(unsigned long cbase, unsigned long cmask,
 {
 	int event_idx_type;
 	uint32_t event_code;
-	unsigned long ctr_mask = cmask << cbase;
 	int ret = SBI_EINVAL;
 	bool bUpdate = FALSE;
+	int i, cidx;
 
-	if (sbi_fls(ctr_mask) >= total_ctrs)
+	if ((cbase + sbi_fls(cmask)) >= total_ctrs)
 		return ret;
 
 	if (flags & SBI_PMU_START_FLAG_SET_INIT_VALUE)
 		bUpdate = TRUE;
 
-	for_each_set_bit_from(cbase, &ctr_mask, total_ctrs) {
-		event_idx_type = pmu_ctr_validate(cbase, &event_code);
+	for_each_set_bit(i, &cmask, total_ctrs) {
+		cidx = i + cbase;
+		event_idx_type = pmu_ctr_validate(cidx, &event_code);
 		if (event_idx_type < 0)
 			/* Continue the start operation for other counters */
 			continue;
 		else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW)
-			ret = pmu_ctr_start_fw(cbase, event_code, ival, bUpdate);
+			ret = pmu_ctr_start_fw(cidx, event_code, ival, bUpdate);
 		else
-			ret = pmu_ctr_start_hw(cbase, ival, bUpdate);
+			ret = pmu_ctr_start_hw(cidx, ival, bUpdate);
 	}
 
 	return ret;
@@ -424,25 +425,26 @@ int sbi_pmu_ctr_stop(unsigned long cbase, unsigned long cmask,
 	int ret = SBI_EINVAL;
 	int event_idx_type;
 	uint32_t event_code;
-	unsigned long ctr_mask = cmask << cbase;
+	int i, cidx;
 
-	if (sbi_fls(ctr_mask) >= total_ctrs)
+	if ((cbase + sbi_fls(cmask)) >= total_ctrs)
 		return SBI_EINVAL;
 
-	for_each_set_bit_from(cbase, &ctr_mask, total_ctrs) {
-		event_idx_type = pmu_ctr_validate(cbase, &event_code);
+	for_each_set_bit(i, &cmask, total_ctrs) {
+		cidx = i + cbase;
+		event_idx_type = pmu_ctr_validate(cidx, &event_code);
 		if (event_idx_type < 0)
 			/* Continue the stop operation for other counters */
 			continue;
 
 		else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW)
-			ret = pmu_ctr_stop_fw(cbase, event_code);
+			ret = pmu_ctr_stop_fw(cidx, event_code);
 		else
-			ret = pmu_ctr_stop_hw(cbase);
+			ret = pmu_ctr_stop_hw(cidx);
 
 		if (flag & SBI_PMU_STOP_FLAG_RESET) {
-			active_events[hartid][cbase] = SBI_PMU_EVENT_IDX_INVALID;
-			pmu_reset_hw_mhpmevent(cbase);
+			active_events[hartid][cidx] = SBI_PMU_EVENT_IDX_INVALID;
+			pmu_reset_hw_mhpmevent(cidx);
 		}
 	}
 
@@ -618,10 +620,9 @@ int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask,
 	int event_type = get_cidx_type(event_idx);
 	struct sbi_pmu_fw_event *fevent;
 	uint32_t fw_evt_code;
-	unsigned long tmp = cidx_mask << cidx_base;
 
 	/* Do a basic sanity check of counter base & mask */
-	if (sbi_fls(tmp) >= total_ctrs || event_type >= SBI_PMU_EVENT_TYPE_MAX)
+	if ((cidx_base + sbi_fls(cidx_mask)) >= total_ctrs || event_type >= SBI_PMU_EVENT_TYPE_MAX)
 		return SBI_EINVAL;
 
 	if (flags & SBI_PMU_CFG_FLAG_SKIP_MATCH) {
-- 
2.25.1



From david.abdurachmanov at gmail.com  Mon Jul 18 14:34:59 2022
From: david.abdurachmanov at gmail.com (David Abdurachmanov)
Date: Mon, 18 Jul 2022 23:34:59 +0200
Subject: [PATCH v2 00/14] OpenSBI Kconfig Support
In-Reply-To: <20220718184807.dac3r5bvsublyj2n@kamzik>
References: <20220718145611.2211638-1-apatel@ventanamicro.com> <20220718184807.dac3r5bvsublyj2n@kamzik>
Message-ID: <CAEn-LToptF6LHHZ3nUzCe6KYXBT6FHk0uyVrNfubjC8MppSfhA@mail.gmail.com>

On Mon, Jul 18, 2022 at 8:49 PM Andrew Jones <ajones at ventanamicro.com> wrote:
>
> On Mon, Jul 18, 2022 at 08:25:57PM +0530, Anup Patel wrote:
> > In future, we will see more drivers in OpenSBI for system reset, serial port,
> > power management, etc platform specific devices. We need platform specific
> > compile time configuration to allow users control the size of OpenSBI generic
> > firmware binaries by only enabling desired drivers. In other words, users
> > should be allowed to restrict the set of RISC-V platforms on which OpenSBI
> > generic firmwares built by them can run.
> >
> > To address above, we add Kconfig support for OpenSBI platforms using the
> > kconfig-frontends project which is a standalone kconfig parser.
> >
> > The kconfig-frontends is available as a package/receipe on various distros
> > (such as Debian, UBuntu, OpenSUSE, etc) and Yocto project.
>
> Hi Anup,
>
> It doesn't look like Fedora provides that package. Also, is the source
> for the packages of those other distros maintained? If I found the
> correct source, then it's [1], which hasn't had any attention in five
> years, even though there have been hundreds of commits to Linux's
> scripts/kconfig. I wonder if we shouldn't consider adopting
> scripts/minikconf.py from the QEMU sources and sticking to the kconfig
> language subset that parser supports?

After spending a bit of time googling about kconfig-frontends it seems
to be a dead project.

Would kconfiglib work? That seems to be alive. While not directly
available in Fedora too it's on pip.

>
> [1] http://ymorin.is-a-geek.org/projects/kconfig-frontends
>
> Thanks,
> drew
>
> >
> > To try OpenSBI Kconfig support with generic platform, here are few
> > example commands:
> > make PLATFORM=generic             <-- builds OpenSBI using "defconfig"
> > make PLATFORM=generic menuconfig  <-- launches graphical interface for changes
> >
> > These patches can also be found in the kconfig_v2 branch at:
> > https://github.com/avpatel/opensbi.git
> >
> > Changes since v1:
> >  - Fix SPDX license specifier in new Kconfig files
> >
> > Anup Patel (14):
> >   Makefile: Add initial kconfig support for each platform
> >   Makefile: Compile lib/utils sources separately for each platform
> >   lib: utils/serial: Use kconfig for enabling/disabling drivers
> >   lib: utils/reset: Use kconfig for enabling/disabling drivers
> >   lib: utils/sys: Use kconfig for enabling/disabling drivers
> >   lib: utils/timer: Use kconfig for enabling/disabling drivers
> >   lib: utils/ipi: Use kconfig for enabling/disabling drivers
> >   lib: utils/irqchip: Use kconfig for enabling/disabling drivers
> >   lib: utils/i2c: Use kconfig for enabling/disabling drivers
> >   lib: utils/gpio: Use kconfig for enabling/disabling drivers
> >   lib: utils/fdt: Use kconfig for enabling/disabling
> >   platform: generic: Use kconfig for enabling/disabling overrides
> >   platform: Remove redundant config.mk from all platforms
> >   docs: Update documentation for kconfig support
> >
> >  Kconfig                                   |  23 +++++
> >  Makefile                                  | 102 ++++++++++++++++------
> >  README.md                                 |  25 +++++-
> >  docs/firmware/fw_dynamic.md               |   2 +-
> >  docs/firmware/fw_jump.md                  |   4 +-
> >  docs/firmware/fw_payload.md               |   4 +-
> >  docs/library_usage.md                     |   6 +-
> >  docs/platform/platform.md                 |   4 +-
> >  docs/platform_guide.md                    |   9 +-
> >  firmware/Kconfig                          |   1 +
> >  include/sbi_utils/fdt/fdt_domain.h        |   9 ++
> >  include/sbi_utils/fdt/fdt_pmu.h           |  10 +++
> >  include/sbi_utils/ipi/fdt_ipi.h           |   9 ++
> >  include/sbi_utils/irqchip/fdt_irqchip.h   |  10 +++
> >  include/sbi_utils/irqchip/imsic.h         |  10 +++
> >  include/sbi_utils/reset/fdt_reset.h       |  12 +++
> >  include/sbi_utils/serial/fdt_serial.h     |   8 ++
> >  include/sbi_utils/timer/fdt_timer.h       |   9 ++
> >  lib/utils/Kconfig                         |  25 ++++++
> >  lib/utils/fdt/Kconfig                     |  18 ++++
> >  lib/utils/fdt/objects.mk                  |   8 +-
> >  lib/utils/gpio/Kconfig                    |  23 +++++
> >  lib/utils/gpio/objects.mk                 |  10 +--
> >  lib/utils/i2c/Kconfig                     |  23 +++++
> >  lib/utils/i2c/objects.mk                  |  10 +--
> >  lib/utils/ipi/Kconfig                     |  23 +++++
> >  lib/utils/ipi/objects.mk                  |  10 +--
> >  lib/utils/irqchip/Kconfig                 |  41 +++++++++
> >  lib/utils/irqchip/objects.mk              |  22 ++---
> >  lib/utils/libfdt/Kconfig                  |   5 ++
> >  lib/utils/libfdt/objects.mk               |   4 +-
> >  lib/utils/reset/Kconfig                   |  37 ++++++++
> >  lib/utils/reset/objects.mk                |  28 +++---
> >  lib/utils/serial/Kconfig                  |  73 ++++++++++++++++
> >  lib/utils/serial/objects.mk               |  44 +++++-----
> >  lib/utils/sys/Kconfig                     |  13 +++
> >  lib/utils/sys/objects.mk                  |   4 +-
> >  lib/utils/timer/Kconfig                   |  23 +++++
> >  lib/utils/timer/objects.mk                |  10 +--
> >  platform/andes/ae350/Kconfig              |   8 ++
> >  platform/andes/ae350/config.mk            |  36 --------
> >  platform/andes/ae350/configs/defconfig    |   0
> >  platform/andes/ae350/objects.mk           |  29 ++++++
> >  platform/fpga/ariane/Kconfig              |  10 +++
> >  platform/fpga/ariane/config.mk            |  36 --------
> >  platform/fpga/ariane/configs/defconfig    |   0
> >  platform/fpga/ariane/objects.mk           |  35 ++++++++
> >  platform/fpga/openpiton/Kconfig           |  10 +++
> >  platform/fpga/openpiton/config.mk         |  35 --------
> >  platform/fpga/openpiton/configs/defconfig |   0
> >  platform/fpga/openpiton/objects.mk        |  35 ++++++++
> >  platform/generic/Kconfig                  |  26 ++++++
> >  platform/generic/allwinner/objects.mk     |   4 +-
> >  platform/generic/config.mk                |  40 ---------
> >  platform/generic/configs/defconfig        |  29 ++++++
> >  platform/generic/objects.mk               |  33 +++++++
> >  platform/generic/sifive/objects.mk        |   8 +-
> >  platform/kendryte/k210/Kconfig            |  10 +++
> >  platform/kendryte/k210/config.mk          |  19 ----
> >  platform/kendryte/k210/configs/defconfig  |   0
> >  platform/kendryte/k210/objects.mk         |  12 +++
> >  platform/nuclei/ux600/Kconfig             |  10 +++
> >  platform/nuclei/ux600/config.mk           |  30 -------
> >  platform/nuclei/ux600/configs/defconfig   |   0
> >  platform/nuclei/ux600/objects.mk          |  22 +++++
> >  platform/nuclei/ux600/platform.c          |   1 -
> >  platform/template/Kconfig                 |   9 ++
> >  platform/template/config.mk               |  77 ----------------
> >  platform/template/configs/defconfig       |   0
> >  platform/template/objects.mk              |  72 +++++++++++++++
> >  platform/template/platform.c              |   2 +-
> >  scripts/create-binary-archive.sh          |   2 +-
> >  72 files changed, 948 insertions(+), 403 deletions(-)
> >  create mode 100644 Kconfig
> >  create mode 100644 firmware/Kconfig
> >  create mode 100644 lib/utils/Kconfig
> >  create mode 100644 lib/utils/fdt/Kconfig
> >  create mode 100644 lib/utils/gpio/Kconfig
> >  create mode 100644 lib/utils/i2c/Kconfig
> >  create mode 100644 lib/utils/ipi/Kconfig
> >  create mode 100644 lib/utils/irqchip/Kconfig
> >  create mode 100644 lib/utils/libfdt/Kconfig
> >  create mode 100644 lib/utils/reset/Kconfig
> >  create mode 100644 lib/utils/serial/Kconfig
> >  create mode 100644 lib/utils/sys/Kconfig
> >  create mode 100644 lib/utils/timer/Kconfig
> >  create mode 100644 platform/andes/ae350/Kconfig
> >  delete mode 100644 platform/andes/ae350/config.mk
> >  create mode 100644 platform/andes/ae350/configs/defconfig
> >  create mode 100644 platform/fpga/ariane/Kconfig
> >  delete mode 100644 platform/fpga/ariane/config.mk
> >  create mode 100644 platform/fpga/ariane/configs/defconfig
> >  create mode 100644 platform/fpga/openpiton/Kconfig
> >  delete mode 100644 platform/fpga/openpiton/config.mk
> >  create mode 100644 platform/fpga/openpiton/configs/defconfig
> >  create mode 100644 platform/generic/Kconfig
> >  delete mode 100644 platform/generic/config.mk
> >  create mode 100644 platform/generic/configs/defconfig
> >  create mode 100644 platform/kendryte/k210/Kconfig
> >  delete mode 100644 platform/kendryte/k210/config.mk
> >  create mode 100644 platform/kendryte/k210/configs/defconfig
> >  create mode 100644 platform/nuclei/ux600/Kconfig
> >  delete mode 100644 platform/nuclei/ux600/config.mk
> >  create mode 100644 platform/nuclei/ux600/configs/defconfig
> >  create mode 100644 platform/template/Kconfig
> >  delete mode 100644 platform/template/config.mk
> >  create mode 100644 platform/template/configs/defconfig
> >
> > --
> > 2.34.1
> >
> >
> > --
> > opensbi mailing list
> > opensbi at lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/opensbi
>
> --
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi


From Alistair.Francis at wdc.com  Mon Jul 18 16:17:36 2022
From: Alistair.Francis at wdc.com (Alistair Francis)
Date: Mon, 18 Jul 2022 23:17:36 +0000
Subject: Is anyone seeing OpenSBI v1.0 flakiness?
In-Reply-To: <TYYP286MB1439E4718D5B2F685BFE2022C6899@TYYP286MB1439.JPNP286.PROD.OUTLOOK.COM>
References: <mhng-3a4b2b8b-da0b-4e32-875c-5f52dcc53f20@palmer-mbp2014>
	 <TYYP286MB1439E4718D5B2F685BFE2022C6899@TYYP286MB1439.JPNP286.PROD.OUTLOOK.COM>
Message-ID: <4ba1ed2439c31761764f451af874b916a0a0d382.camel@wdc.com>

On Wed, 2022-07-13 at 15:51 +0800, dramforever wrote:
> On 7/13/22 10:28, Palmer Dabbelt wrote:
> > I recently bumped QEMU to v7.0.0 in my main test repos, which is
> > causing some flakiness: around 1% of kernels are failing to boot,
> > it
> > seems to not really depend on the Kconfig or QEMU config (though
> > I'm
> > always running virt tests).? It's just an early boot hang, I
> > haven't
> > poked around much deeper as I've just been trying to bisect the
> > problem.? I think I've tracked this down to the OpenSBI update
> > (from
> > 0.9 to 1.0), but it seems like the boot failures come and go so I'm
> > not 100% sure on that -- at least the commit before the OpenSBI
> > update
> > seems safe, I ran about ten thousand boots without any issues.?
> > That's
> > about as far as I've getten bisecting, the low failure rate is
> > causing
> > headaches.
> > 
> > I'm back on v6.1.0 so I can try and get some Linux code merged this
> > week, I'm way behind so I probably won't be able to look all that
> > much.? I'm going to try and get OpenSBI master into my testing flow
> > more cleanly, but I figured I'd start a thread in case anyone else
> > is
> > seeing issues.
> > 
> This is just a guess, but QEMU 7.0.0 has a bug in setting mtval/stval
> for illegal instruction handling [1]. The behavior is dependent on
> the
> history of illegal instructions executed, and OpenSBI does install an
> illegal instruction handler, so there might be some interaction with
> whatever Linux is doing, like emulating the wrong instruction.

That could be the issue. It would be great if you could test with the
latest master QEMU

Alistair

> 
> I have a workaround patch in my own thing [2] but since it basically
> negates the benefit of mtvec/stvec on illegal instruction it's not
> upstreamed.
> 
> Regards,
> dram
> 
> [1]: https://gitlab.com/qemu-project/qemu/-/issues/1060
> [2]:
> https://github.com/dramforever/opensbi-h/commit/e580cb14139b4cf5792e3d9adf2d9356670a3039
> 
> 


From apatel at ventanamicro.com  Mon Jul 18 20:55:35 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Tue, 19 Jul 2022 09:25:35 +0530
Subject: [PATCH v2 00/14] OpenSBI Kconfig Support
In-Reply-To: <20220718184807.dac3r5bvsublyj2n@kamzik>
References: <20220718145611.2211638-1-apatel@ventanamicro.com> <20220718184807.dac3r5bvsublyj2n@kamzik>
Message-ID: <CAK9=C2VfU1ekpr5CqJd4n2-1NCVBcKrM7d3W6iGbMTX=kggPHA@mail.gmail.com>

+Heinrich
On Tue, Jul 19, 2022 at 12:18 AM Andrew Jones <ajones at ventanamicro.com> wrote:
>
> On Mon, Jul 18, 2022 at 08:25:57PM +0530, Anup Patel wrote:
> > In future, we will see more drivers in OpenSBI for system reset, serial port,
> > power management, etc platform specific devices. We need platform specific
> > compile time configuration to allow users control the size of OpenSBI generic
> > firmware binaries by only enabling desired drivers. In other words, users
> > should be allowed to restrict the set of RISC-V platforms on which OpenSBI
> > generic firmwares built by them can run.
> >
> > To address above, we add Kconfig support for OpenSBI platforms using the
> > kconfig-frontends project which is a standalone kconfig parser.
> >
> > The kconfig-frontends is available as a package/receipe on various distros
> > (such as Debian, UBuntu, OpenSUSE, etc) and Yocto project.
>
> Hi Anup,
>
> It doesn't look like Fedora provides that package. Also, is the source
> for the packages of those other distros maintained? If I found the
> correct source, then it's [1], which hasn't had any attention in five
> years, even though there have been hundreds of commits to Linux's
> scripts/kconfig. I wonder if we shouldn't consider adopting
> scripts/minikconf.py from the QEMU sources and sticking to the kconfig
> language subset that parser supports?
>
> [1] http://ymorin.is-a-geek.org/projects/kconfig-frontends

Actually, it seems quite a few distros (particularly debian, ubuntu,
and yocto) have already included the kconfig-frontends package so
I wonder why Fedora did not include a package for it ?

The QEMU scripts/minikconf.py is pretty compact and would be
our next best alternative but it doesn't have menuconfig support.

Regards,
Anup

>
> Thanks,
> drew
>
> >
> > To try OpenSBI Kconfig support with generic platform, here are few
> > example commands:
> > make PLATFORM=generic             <-- builds OpenSBI using "defconfig"
> > make PLATFORM=generic menuconfig  <-- launches graphical interface for changes
> >
> > These patches can also be found in the kconfig_v2 branch at:
> > https://github.com/avpatel/opensbi.git
> >
> > Changes since v1:
> >  - Fix SPDX license specifier in new Kconfig files
> >
> > Anup Patel (14):
> >   Makefile: Add initial kconfig support for each platform
> >   Makefile: Compile lib/utils sources separately for each platform
> >   lib: utils/serial: Use kconfig for enabling/disabling drivers
> >   lib: utils/reset: Use kconfig for enabling/disabling drivers
> >   lib: utils/sys: Use kconfig for enabling/disabling drivers
> >   lib: utils/timer: Use kconfig for enabling/disabling drivers
> >   lib: utils/ipi: Use kconfig for enabling/disabling drivers
> >   lib: utils/irqchip: Use kconfig for enabling/disabling drivers
> >   lib: utils/i2c: Use kconfig for enabling/disabling drivers
> >   lib: utils/gpio: Use kconfig for enabling/disabling drivers
> >   lib: utils/fdt: Use kconfig for enabling/disabling
> >   platform: generic: Use kconfig for enabling/disabling overrides
> >   platform: Remove redundant config.mk from all platforms
> >   docs: Update documentation for kconfig support
> >
> >  Kconfig                                   |  23 +++++
> >  Makefile                                  | 102 ++++++++++++++++------
> >  README.md                                 |  25 +++++-
> >  docs/firmware/fw_dynamic.md               |   2 +-
> >  docs/firmware/fw_jump.md                  |   4 +-
> >  docs/firmware/fw_payload.md               |   4 +-
> >  docs/library_usage.md                     |   6 +-
> >  docs/platform/platform.md                 |   4 +-
> >  docs/platform_guide.md                    |   9 +-
> >  firmware/Kconfig                          |   1 +
> >  include/sbi_utils/fdt/fdt_domain.h        |   9 ++
> >  include/sbi_utils/fdt/fdt_pmu.h           |  10 +++
> >  include/sbi_utils/ipi/fdt_ipi.h           |   9 ++
> >  include/sbi_utils/irqchip/fdt_irqchip.h   |  10 +++
> >  include/sbi_utils/irqchip/imsic.h         |  10 +++
> >  include/sbi_utils/reset/fdt_reset.h       |  12 +++
> >  include/sbi_utils/serial/fdt_serial.h     |   8 ++
> >  include/sbi_utils/timer/fdt_timer.h       |   9 ++
> >  lib/utils/Kconfig                         |  25 ++++++
> >  lib/utils/fdt/Kconfig                     |  18 ++++
> >  lib/utils/fdt/objects.mk                  |   8 +-
> >  lib/utils/gpio/Kconfig                    |  23 +++++
> >  lib/utils/gpio/objects.mk                 |  10 +--
> >  lib/utils/i2c/Kconfig                     |  23 +++++
> >  lib/utils/i2c/objects.mk                  |  10 +--
> >  lib/utils/ipi/Kconfig                     |  23 +++++
> >  lib/utils/ipi/objects.mk                  |  10 +--
> >  lib/utils/irqchip/Kconfig                 |  41 +++++++++
> >  lib/utils/irqchip/objects.mk              |  22 ++---
> >  lib/utils/libfdt/Kconfig                  |   5 ++
> >  lib/utils/libfdt/objects.mk               |   4 +-
> >  lib/utils/reset/Kconfig                   |  37 ++++++++
> >  lib/utils/reset/objects.mk                |  28 +++---
> >  lib/utils/serial/Kconfig                  |  73 ++++++++++++++++
> >  lib/utils/serial/objects.mk               |  44 +++++-----
> >  lib/utils/sys/Kconfig                     |  13 +++
> >  lib/utils/sys/objects.mk                  |   4 +-
> >  lib/utils/timer/Kconfig                   |  23 +++++
> >  lib/utils/timer/objects.mk                |  10 +--
> >  platform/andes/ae350/Kconfig              |   8 ++
> >  platform/andes/ae350/config.mk            |  36 --------
> >  platform/andes/ae350/configs/defconfig    |   0
> >  platform/andes/ae350/objects.mk           |  29 ++++++
> >  platform/fpga/ariane/Kconfig              |  10 +++
> >  platform/fpga/ariane/config.mk            |  36 --------
> >  platform/fpga/ariane/configs/defconfig    |   0
> >  platform/fpga/ariane/objects.mk           |  35 ++++++++
> >  platform/fpga/openpiton/Kconfig           |  10 +++
> >  platform/fpga/openpiton/config.mk         |  35 --------
> >  platform/fpga/openpiton/configs/defconfig |   0
> >  platform/fpga/openpiton/objects.mk        |  35 ++++++++
> >  platform/generic/Kconfig                  |  26 ++++++
> >  platform/generic/allwinner/objects.mk     |   4 +-
> >  platform/generic/config.mk                |  40 ---------
> >  platform/generic/configs/defconfig        |  29 ++++++
> >  platform/generic/objects.mk               |  33 +++++++
> >  platform/generic/sifive/objects.mk        |   8 +-
> >  platform/kendryte/k210/Kconfig            |  10 +++
> >  platform/kendryte/k210/config.mk          |  19 ----
> >  platform/kendryte/k210/configs/defconfig  |   0
> >  platform/kendryte/k210/objects.mk         |  12 +++
> >  platform/nuclei/ux600/Kconfig             |  10 +++
> >  platform/nuclei/ux600/config.mk           |  30 -------
> >  platform/nuclei/ux600/configs/defconfig   |   0
> >  platform/nuclei/ux600/objects.mk          |  22 +++++
> >  platform/nuclei/ux600/platform.c          |   1 -
> >  platform/template/Kconfig                 |   9 ++
> >  platform/template/config.mk               |  77 ----------------
> >  platform/template/configs/defconfig       |   0
> >  platform/template/objects.mk              |  72 +++++++++++++++
> >  platform/template/platform.c              |   2 +-
> >  scripts/create-binary-archive.sh          |   2 +-
> >  72 files changed, 948 insertions(+), 403 deletions(-)
> >  create mode 100644 Kconfig
> >  create mode 100644 firmware/Kconfig
> >  create mode 100644 lib/utils/Kconfig
> >  create mode 100644 lib/utils/fdt/Kconfig
> >  create mode 100644 lib/utils/gpio/Kconfig
> >  create mode 100644 lib/utils/i2c/Kconfig
> >  create mode 100644 lib/utils/ipi/Kconfig
> >  create mode 100644 lib/utils/irqchip/Kconfig
> >  create mode 100644 lib/utils/libfdt/Kconfig
> >  create mode 100644 lib/utils/reset/Kconfig
> >  create mode 100644 lib/utils/serial/Kconfig
> >  create mode 100644 lib/utils/sys/Kconfig
> >  create mode 100644 lib/utils/timer/Kconfig
> >  create mode 100644 platform/andes/ae350/Kconfig
> >  delete mode 100644 platform/andes/ae350/config.mk
> >  create mode 100644 platform/andes/ae350/configs/defconfig
> >  create mode 100644 platform/fpga/ariane/Kconfig
> >  delete mode 100644 platform/fpga/ariane/config.mk
> >  create mode 100644 platform/fpga/ariane/configs/defconfig
> >  create mode 100644 platform/fpga/openpiton/Kconfig
> >  delete mode 100644 platform/fpga/openpiton/config.mk
> >  create mode 100644 platform/fpga/openpiton/configs/defconfig
> >  create mode 100644 platform/generic/Kconfig
> >  delete mode 100644 platform/generic/config.mk
> >  create mode 100644 platform/generic/configs/defconfig
> >  create mode 100644 platform/kendryte/k210/Kconfig
> >  delete mode 100644 platform/kendryte/k210/config.mk
> >  create mode 100644 platform/kendryte/k210/configs/defconfig
> >  create mode 100644 platform/nuclei/ux600/Kconfig
> >  delete mode 100644 platform/nuclei/ux600/config.mk
> >  create mode 100644 platform/nuclei/ux600/configs/defconfig
> >  create mode 100644 platform/template/Kconfig
> >  delete mode 100644 platform/template/config.mk
> >  create mode 100644 platform/template/configs/defconfig
> >
> > --
> > 2.34.1
> >
> >
> > --
> > opensbi mailing list
> > opensbi at lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/opensbi


From apatel at ventanamicro.com  Mon Jul 18 21:04:34 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Tue, 19 Jul 2022 09:34:34 +0530
Subject: [PATCH v2 00/14] OpenSBI Kconfig Support
In-Reply-To: <CAEn-LToptF6LHHZ3nUzCe6KYXBT6FHk0uyVrNfubjC8MppSfhA@mail.gmail.com>
References: <20220718145611.2211638-1-apatel@ventanamicro.com>
 <20220718184807.dac3r5bvsublyj2n@kamzik> <CAEn-LToptF6LHHZ3nUzCe6KYXBT6FHk0uyVrNfubjC8MppSfhA@mail.gmail.com>
Message-ID: <CAK9=C2UE088wC3TxUFhrRUNMYQpfzvaCZAt4AGMjBcjnvktqWw@mail.gmail.com>

+Heinrich

On Tue, Jul 19, 2022 at 3:05 AM David Abdurachmanov
<david.abdurachmanov at gmail.com> wrote:
>
> On Mon, Jul 18, 2022 at 8:49 PM Andrew Jones <ajones at ventanamicro.com> wrote:
> >
> > On Mon, Jul 18, 2022 at 08:25:57PM +0530, Anup Patel wrote:
> > > In future, we will see more drivers in OpenSBI for system reset, serial port,
> > > power management, etc platform specific devices. We need platform specific
> > > compile time configuration to allow users control the size of OpenSBI generic
> > > firmware binaries by only enabling desired drivers. In other words, users
> > > should be allowed to restrict the set of RISC-V platforms on which OpenSBI
> > > generic firmwares built by them can run.
> > >
> > > To address above, we add Kconfig support for OpenSBI platforms using the
> > > kconfig-frontends project which is a standalone kconfig parser.
> > >
> > > The kconfig-frontends is available as a package/receipe on various distros
> > > (such as Debian, UBuntu, OpenSUSE, etc) and Yocto project.
> >
> > Hi Anup,
> >
> > It doesn't look like Fedora provides that package. Also, is the source
> > for the packages of those other distros maintained? If I found the
> > correct source, then it's [1], which hasn't had any attention in five
> > years, even though there have been hundreds of commits to Linux's
> > scripts/kconfig. I wonder if we shouldn't consider adopting
> > scripts/minikconf.py from the QEMU sources and sticking to the kconfig
> > language subset that parser supports?
>
> After spending a bit of time googling about kconfig-frontends it seems
> to be a dead project.

Yes, the kconfig-frontends project is not being developed but there are
forks of this project since quite a few distros and yocto have support for
it.

Is there a requirement that Fedora only adds packages for active projects ?

>
> Would kconfiglib work? That seems to be alive. While not directly
> available in Fedora too it's on pip.

I explored the kconfiglib project. It is only a library and we will endup
maintaining our own tools for menuconfig, defconfig, etc in the OpenSBI
sources.

The best thing would be if we can avoid adding more tools and scripts
in OpenSBI for kconfig. The kconfig-frontends allows us to do that.

If we have to add some tool/script in OpenSBI then I would prefer
something (like QEMU scripts/minikconf.py) which is minimalistic
and does not add maintenance burden.

Regards,
Anup

>
> >
> > [1] http://ymorin.is-a-geek.org/projects/kconfig-frontends
> >
> > Thanks,
> > drew
> >
> > >
> > > To try OpenSBI Kconfig support with generic platform, here are few
> > > example commands:
> > > make PLATFORM=generic             <-- builds OpenSBI using "defconfig"
> > > make PLATFORM=generic menuconfig  <-- launches graphical interface for changes
> > >
> > > These patches can also be found in the kconfig_v2 branch at:
> > > https://github.com/avpatel/opensbi.git
> > >
> > > Changes since v1:
> > >  - Fix SPDX license specifier in new Kconfig files
> > >
> > > Anup Patel (14):
> > >   Makefile: Add initial kconfig support for each platform
> > >   Makefile: Compile lib/utils sources separately for each platform
> > >   lib: utils/serial: Use kconfig for enabling/disabling drivers
> > >   lib: utils/reset: Use kconfig for enabling/disabling drivers
> > >   lib: utils/sys: Use kconfig for enabling/disabling drivers
> > >   lib: utils/timer: Use kconfig for enabling/disabling drivers
> > >   lib: utils/ipi: Use kconfig for enabling/disabling drivers
> > >   lib: utils/irqchip: Use kconfig for enabling/disabling drivers
> > >   lib: utils/i2c: Use kconfig for enabling/disabling drivers
> > >   lib: utils/gpio: Use kconfig for enabling/disabling drivers
> > >   lib: utils/fdt: Use kconfig for enabling/disabling
> > >   platform: generic: Use kconfig for enabling/disabling overrides
> > >   platform: Remove redundant config.mk from all platforms
> > >   docs: Update documentation for kconfig support
> > >
> > >  Kconfig                                   |  23 +++++
> > >  Makefile                                  | 102 ++++++++++++++++------
> > >  README.md                                 |  25 +++++-
> > >  docs/firmware/fw_dynamic.md               |   2 +-
> > >  docs/firmware/fw_jump.md                  |   4 +-
> > >  docs/firmware/fw_payload.md               |   4 +-
> > >  docs/library_usage.md                     |   6 +-
> > >  docs/platform/platform.md                 |   4 +-
> > >  docs/platform_guide.md                    |   9 +-
> > >  firmware/Kconfig                          |   1 +
> > >  include/sbi_utils/fdt/fdt_domain.h        |   9 ++
> > >  include/sbi_utils/fdt/fdt_pmu.h           |  10 +++
> > >  include/sbi_utils/ipi/fdt_ipi.h           |   9 ++
> > >  include/sbi_utils/irqchip/fdt_irqchip.h   |  10 +++
> > >  include/sbi_utils/irqchip/imsic.h         |  10 +++
> > >  include/sbi_utils/reset/fdt_reset.h       |  12 +++
> > >  include/sbi_utils/serial/fdt_serial.h     |   8 ++
> > >  include/sbi_utils/timer/fdt_timer.h       |   9 ++
> > >  lib/utils/Kconfig                         |  25 ++++++
> > >  lib/utils/fdt/Kconfig                     |  18 ++++
> > >  lib/utils/fdt/objects.mk                  |   8 +-
> > >  lib/utils/gpio/Kconfig                    |  23 +++++
> > >  lib/utils/gpio/objects.mk                 |  10 +--
> > >  lib/utils/i2c/Kconfig                     |  23 +++++
> > >  lib/utils/i2c/objects.mk                  |  10 +--
> > >  lib/utils/ipi/Kconfig                     |  23 +++++
> > >  lib/utils/ipi/objects.mk                  |  10 +--
> > >  lib/utils/irqchip/Kconfig                 |  41 +++++++++
> > >  lib/utils/irqchip/objects.mk              |  22 ++---
> > >  lib/utils/libfdt/Kconfig                  |   5 ++
> > >  lib/utils/libfdt/objects.mk               |   4 +-
> > >  lib/utils/reset/Kconfig                   |  37 ++++++++
> > >  lib/utils/reset/objects.mk                |  28 +++---
> > >  lib/utils/serial/Kconfig                  |  73 ++++++++++++++++
> > >  lib/utils/serial/objects.mk               |  44 +++++-----
> > >  lib/utils/sys/Kconfig                     |  13 +++
> > >  lib/utils/sys/objects.mk                  |   4 +-
> > >  lib/utils/timer/Kconfig                   |  23 +++++
> > >  lib/utils/timer/objects.mk                |  10 +--
> > >  platform/andes/ae350/Kconfig              |   8 ++
> > >  platform/andes/ae350/config.mk            |  36 --------
> > >  platform/andes/ae350/configs/defconfig    |   0
> > >  platform/andes/ae350/objects.mk           |  29 ++++++
> > >  platform/fpga/ariane/Kconfig              |  10 +++
> > >  platform/fpga/ariane/config.mk            |  36 --------
> > >  platform/fpga/ariane/configs/defconfig    |   0
> > >  platform/fpga/ariane/objects.mk           |  35 ++++++++
> > >  platform/fpga/openpiton/Kconfig           |  10 +++
> > >  platform/fpga/openpiton/config.mk         |  35 --------
> > >  platform/fpga/openpiton/configs/defconfig |   0
> > >  platform/fpga/openpiton/objects.mk        |  35 ++++++++
> > >  platform/generic/Kconfig                  |  26 ++++++
> > >  platform/generic/allwinner/objects.mk     |   4 +-
> > >  platform/generic/config.mk                |  40 ---------
> > >  platform/generic/configs/defconfig        |  29 ++++++
> > >  platform/generic/objects.mk               |  33 +++++++
> > >  platform/generic/sifive/objects.mk        |   8 +-
> > >  platform/kendryte/k210/Kconfig            |  10 +++
> > >  platform/kendryte/k210/config.mk          |  19 ----
> > >  platform/kendryte/k210/configs/defconfig  |   0
> > >  platform/kendryte/k210/objects.mk         |  12 +++
> > >  platform/nuclei/ux600/Kconfig             |  10 +++
> > >  platform/nuclei/ux600/config.mk           |  30 -------
> > >  platform/nuclei/ux600/configs/defconfig   |   0
> > >  platform/nuclei/ux600/objects.mk          |  22 +++++
> > >  platform/nuclei/ux600/platform.c          |   1 -
> > >  platform/template/Kconfig                 |   9 ++
> > >  platform/template/config.mk               |  77 ----------------
> > >  platform/template/configs/defconfig       |   0
> > >  platform/template/objects.mk              |  72 +++++++++++++++
> > >  platform/template/platform.c              |   2 +-
> > >  scripts/create-binary-archive.sh          |   2 +-
> > >  72 files changed, 948 insertions(+), 403 deletions(-)
> > >  create mode 100644 Kconfig
> > >  create mode 100644 firmware/Kconfig
> > >  create mode 100644 lib/utils/Kconfig
> > >  create mode 100644 lib/utils/fdt/Kconfig
> > >  create mode 100644 lib/utils/gpio/Kconfig
> > >  create mode 100644 lib/utils/i2c/Kconfig
> > >  create mode 100644 lib/utils/ipi/Kconfig
> > >  create mode 100644 lib/utils/irqchip/Kconfig
> > >  create mode 100644 lib/utils/libfdt/Kconfig
> > >  create mode 100644 lib/utils/reset/Kconfig
> > >  create mode 100644 lib/utils/serial/Kconfig
> > >  create mode 100644 lib/utils/sys/Kconfig
> > >  create mode 100644 lib/utils/timer/Kconfig
> > >  create mode 100644 platform/andes/ae350/Kconfig
> > >  delete mode 100644 platform/andes/ae350/config.mk
> > >  create mode 100644 platform/andes/ae350/configs/defconfig
> > >  create mode 100644 platform/fpga/ariane/Kconfig
> > >  delete mode 100644 platform/fpga/ariane/config.mk
> > >  create mode 100644 platform/fpga/ariane/configs/defconfig
> > >  create mode 100644 platform/fpga/openpiton/Kconfig
> > >  delete mode 100644 platform/fpga/openpiton/config.mk
> > >  create mode 100644 platform/fpga/openpiton/configs/defconfig
> > >  create mode 100644 platform/generic/Kconfig
> > >  delete mode 100644 platform/generic/config.mk
> > >  create mode 100644 platform/generic/configs/defconfig
> > >  create mode 100644 platform/kendryte/k210/Kconfig
> > >  delete mode 100644 platform/kendryte/k210/config.mk
> > >  create mode 100644 platform/kendryte/k210/configs/defconfig
> > >  create mode 100644 platform/nuclei/ux600/Kconfig
> > >  delete mode 100644 platform/nuclei/ux600/config.mk
> > >  create mode 100644 platform/nuclei/ux600/configs/defconfig
> > >  create mode 100644 platform/template/Kconfig
> > >  delete mode 100644 platform/template/config.mk
> > >  create mode 100644 platform/template/configs/defconfig
> > >
> > > --
> > > 2.34.1
> > >
> > >
> > > --
> > > opensbi mailing list
> > > opensbi at lists.infradead.org
> > > http://lists.infradead.org/mailman/listinfo/opensbi
> >
> > --
> > opensbi mailing list
> > opensbi at lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/opensbi


From apatel at ventanamicro.com  Mon Jul 18 21:17:28 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Tue, 19 Jul 2022 09:47:28 +0530
Subject: [PATCH v2 00/14] OpenSBI Kconfig Support
In-Reply-To: <CAK9=C2UE088wC3TxUFhrRUNMYQpfzvaCZAt4AGMjBcjnvktqWw@mail.gmail.com>
References: <20220718145611.2211638-1-apatel@ventanamicro.com>
 <20220718184807.dac3r5bvsublyj2n@kamzik> <CAEn-LToptF6LHHZ3nUzCe6KYXBT6FHk0uyVrNfubjC8MppSfhA@mail.gmail.com>
 <CAK9=C2UE088wC3TxUFhrRUNMYQpfzvaCZAt4AGMjBcjnvktqWw@mail.gmail.com>
Message-ID: <CAK9=C2U7s1FKYw+FqBbZz5mTP=Gz9mRJ-CLr4m3+iqg9PWiQ8A@mail.gmail.com>

On Tue, Jul 19, 2022 at 9:34 AM Anup Patel <apatel at ventanamicro.com> wrote:
>
> +Heinrich
>
> On Tue, Jul 19, 2022 at 3:05 AM David Abdurachmanov
> <david.abdurachmanov at gmail.com> wrote:
> >
> > On Mon, Jul 18, 2022 at 8:49 PM Andrew Jones <ajones at ventanamicro.com> wrote:
> > >
> > > On Mon, Jul 18, 2022 at 08:25:57PM +0530, Anup Patel wrote:
> > > > In future, we will see more drivers in OpenSBI for system reset, serial port,
> > > > power management, etc platform specific devices. We need platform specific
> > > > compile time configuration to allow users control the size of OpenSBI generic
> > > > firmware binaries by only enabling desired drivers. In other words, users
> > > > should be allowed to restrict the set of RISC-V platforms on which OpenSBI
> > > > generic firmwares built by them can run.
> > > >
> > > > To address above, we add Kconfig support for OpenSBI platforms using the
> > > > kconfig-frontends project which is a standalone kconfig parser.
> > > >
> > > > The kconfig-frontends is available as a package/receipe on various distros
> > > > (such as Debian, UBuntu, OpenSUSE, etc) and Yocto project.
> > >
> > > Hi Anup,
> > >
> > > It doesn't look like Fedora provides that package. Also, is the source
> > > for the packages of those other distros maintained? If I found the
> > > correct source, then it's [1], which hasn't had any attention in five
> > > years, even though there have been hundreds of commits to Linux's
> > > scripts/kconfig. I wonder if we shouldn't consider adopting
> > > scripts/minikconf.py from the QEMU sources and sticking to the kconfig
> > > language subset that parser supports?
> >
> > After spending a bit of time googling about kconfig-frontends it seems
> > to be a dead project.
>
> Yes, the kconfig-frontends project is not being developed but there are
> forks of this project since quite a few distros and yocto have support for
> it.
>
> Is there a requirement that Fedora only adds packages for active projects ?
>
> >
> > Would kconfiglib work? That seems to be alive. While not directly
> > available in Fedora too it's on pip.
>
> I explored the kconfiglib project. It is only a library and we will endup
> maintaining our own tools for menuconfig, defconfig, etc in the OpenSBI
> sources.

I stand corrected. The kconfiglib project when installed using python-pip
will install various tools menuconfig, defconfig, etc along with it which is
very nice.

If we go with the kconfiglib project then we should expect users to install
the kconfiglib package before building OpenSBI. Is this a reasonable
requirement for Fedora and UBuntu ?

@Heinrich are you okay with this ?

Regards,
Anup

>
> The best thing would be if we can avoid adding more tools and scripts
> in OpenSBI for kconfig. The kconfig-frontends allows us to do that.
>
> If we have to add some tool/script in OpenSBI then I would prefer
> something (like QEMU scripts/minikconf.py) which is minimalistic
> and does not add maintenance burden.
>
> Regards,
> Anup
>
> >
> > >
> > > [1] http://ymorin.is-a-geek.org/projects/kconfig-frontends
> > >
> > > Thanks,
> > > drew
> > >
> > > >
> > > > To try OpenSBI Kconfig support with generic platform, here are few
> > > > example commands:
> > > > make PLATFORM=generic             <-- builds OpenSBI using "defconfig"
> > > > make PLATFORM=generic menuconfig  <-- launches graphical interface for changes
> > > >
> > > > These patches can also be found in the kconfig_v2 branch at:
> > > > https://github.com/avpatel/opensbi.git
> > > >
> > > > Changes since v1:
> > > >  - Fix SPDX license specifier in new Kconfig files
> > > >
> > > > Anup Patel (14):
> > > >   Makefile: Add initial kconfig support for each platform
> > > >   Makefile: Compile lib/utils sources separately for each platform
> > > >   lib: utils/serial: Use kconfig for enabling/disabling drivers
> > > >   lib: utils/reset: Use kconfig for enabling/disabling drivers
> > > >   lib: utils/sys: Use kconfig for enabling/disabling drivers
> > > >   lib: utils/timer: Use kconfig for enabling/disabling drivers
> > > >   lib: utils/ipi: Use kconfig for enabling/disabling drivers
> > > >   lib: utils/irqchip: Use kconfig for enabling/disabling drivers
> > > >   lib: utils/i2c: Use kconfig for enabling/disabling drivers
> > > >   lib: utils/gpio: Use kconfig for enabling/disabling drivers
> > > >   lib: utils/fdt: Use kconfig for enabling/disabling
> > > >   platform: generic: Use kconfig for enabling/disabling overrides
> > > >   platform: Remove redundant config.mk from all platforms
> > > >   docs: Update documentation for kconfig support
> > > >
> > > >  Kconfig                                   |  23 +++++
> > > >  Makefile                                  | 102 ++++++++++++++++------
> > > >  README.md                                 |  25 +++++-
> > > >  docs/firmware/fw_dynamic.md               |   2 +-
> > > >  docs/firmware/fw_jump.md                  |   4 +-
> > > >  docs/firmware/fw_payload.md               |   4 +-
> > > >  docs/library_usage.md                     |   6 +-
> > > >  docs/platform/platform.md                 |   4 +-
> > > >  docs/platform_guide.md                    |   9 +-
> > > >  firmware/Kconfig                          |   1 +
> > > >  include/sbi_utils/fdt/fdt_domain.h        |   9 ++
> > > >  include/sbi_utils/fdt/fdt_pmu.h           |  10 +++
> > > >  include/sbi_utils/ipi/fdt_ipi.h           |   9 ++
> > > >  include/sbi_utils/irqchip/fdt_irqchip.h   |  10 +++
> > > >  include/sbi_utils/irqchip/imsic.h         |  10 +++
> > > >  include/sbi_utils/reset/fdt_reset.h       |  12 +++
> > > >  include/sbi_utils/serial/fdt_serial.h     |   8 ++
> > > >  include/sbi_utils/timer/fdt_timer.h       |   9 ++
> > > >  lib/utils/Kconfig                         |  25 ++++++
> > > >  lib/utils/fdt/Kconfig                     |  18 ++++
> > > >  lib/utils/fdt/objects.mk                  |   8 +-
> > > >  lib/utils/gpio/Kconfig                    |  23 +++++
> > > >  lib/utils/gpio/objects.mk                 |  10 +--
> > > >  lib/utils/i2c/Kconfig                     |  23 +++++
> > > >  lib/utils/i2c/objects.mk                  |  10 +--
> > > >  lib/utils/ipi/Kconfig                     |  23 +++++
> > > >  lib/utils/ipi/objects.mk                  |  10 +--
> > > >  lib/utils/irqchip/Kconfig                 |  41 +++++++++
> > > >  lib/utils/irqchip/objects.mk              |  22 ++---
> > > >  lib/utils/libfdt/Kconfig                  |   5 ++
> > > >  lib/utils/libfdt/objects.mk               |   4 +-
> > > >  lib/utils/reset/Kconfig                   |  37 ++++++++
> > > >  lib/utils/reset/objects.mk                |  28 +++---
> > > >  lib/utils/serial/Kconfig                  |  73 ++++++++++++++++
> > > >  lib/utils/serial/objects.mk               |  44 +++++-----
> > > >  lib/utils/sys/Kconfig                     |  13 +++
> > > >  lib/utils/sys/objects.mk                  |   4 +-
> > > >  lib/utils/timer/Kconfig                   |  23 +++++
> > > >  lib/utils/timer/objects.mk                |  10 +--
> > > >  platform/andes/ae350/Kconfig              |   8 ++
> > > >  platform/andes/ae350/config.mk            |  36 --------
> > > >  platform/andes/ae350/configs/defconfig    |   0
> > > >  platform/andes/ae350/objects.mk           |  29 ++++++
> > > >  platform/fpga/ariane/Kconfig              |  10 +++
> > > >  platform/fpga/ariane/config.mk            |  36 --------
> > > >  platform/fpga/ariane/configs/defconfig    |   0
> > > >  platform/fpga/ariane/objects.mk           |  35 ++++++++
> > > >  platform/fpga/openpiton/Kconfig           |  10 +++
> > > >  platform/fpga/openpiton/config.mk         |  35 --------
> > > >  platform/fpga/openpiton/configs/defconfig |   0
> > > >  platform/fpga/openpiton/objects.mk        |  35 ++++++++
> > > >  platform/generic/Kconfig                  |  26 ++++++
> > > >  platform/generic/allwinner/objects.mk     |   4 +-
> > > >  platform/generic/config.mk                |  40 ---------
> > > >  platform/generic/configs/defconfig        |  29 ++++++
> > > >  platform/generic/objects.mk               |  33 +++++++
> > > >  platform/generic/sifive/objects.mk        |   8 +-
> > > >  platform/kendryte/k210/Kconfig            |  10 +++
> > > >  platform/kendryte/k210/config.mk          |  19 ----
> > > >  platform/kendryte/k210/configs/defconfig  |   0
> > > >  platform/kendryte/k210/objects.mk         |  12 +++
> > > >  platform/nuclei/ux600/Kconfig             |  10 +++
> > > >  platform/nuclei/ux600/config.mk           |  30 -------
> > > >  platform/nuclei/ux600/configs/defconfig   |   0
> > > >  platform/nuclei/ux600/objects.mk          |  22 +++++
> > > >  platform/nuclei/ux600/platform.c          |   1 -
> > > >  platform/template/Kconfig                 |   9 ++
> > > >  platform/template/config.mk               |  77 ----------------
> > > >  platform/template/configs/defconfig       |   0
> > > >  platform/template/objects.mk              |  72 +++++++++++++++
> > > >  platform/template/platform.c              |   2 +-
> > > >  scripts/create-binary-archive.sh          |   2 +-
> > > >  72 files changed, 948 insertions(+), 403 deletions(-)
> > > >  create mode 100644 Kconfig
> > > >  create mode 100644 firmware/Kconfig
> > > >  create mode 100644 lib/utils/Kconfig
> > > >  create mode 100644 lib/utils/fdt/Kconfig
> > > >  create mode 100644 lib/utils/gpio/Kconfig
> > > >  create mode 100644 lib/utils/i2c/Kconfig
> > > >  create mode 100644 lib/utils/ipi/Kconfig
> > > >  create mode 100644 lib/utils/irqchip/Kconfig
> > > >  create mode 100644 lib/utils/libfdt/Kconfig
> > > >  create mode 100644 lib/utils/reset/Kconfig
> > > >  create mode 100644 lib/utils/serial/Kconfig
> > > >  create mode 100644 lib/utils/sys/Kconfig
> > > >  create mode 100644 lib/utils/timer/Kconfig
> > > >  create mode 100644 platform/andes/ae350/Kconfig
> > > >  delete mode 100644 platform/andes/ae350/config.mk
> > > >  create mode 100644 platform/andes/ae350/configs/defconfig
> > > >  create mode 100644 platform/fpga/ariane/Kconfig
> > > >  delete mode 100644 platform/fpga/ariane/config.mk
> > > >  create mode 100644 platform/fpga/ariane/configs/defconfig
> > > >  create mode 100644 platform/fpga/openpiton/Kconfig
> > > >  delete mode 100644 platform/fpga/openpiton/config.mk
> > > >  create mode 100644 platform/fpga/openpiton/configs/defconfig
> > > >  create mode 100644 platform/generic/Kconfig
> > > >  delete mode 100644 platform/generic/config.mk
> > > >  create mode 100644 platform/generic/configs/defconfig
> > > >  create mode 100644 platform/kendryte/k210/Kconfig
> > > >  delete mode 100644 platform/kendryte/k210/config.mk
> > > >  create mode 100644 platform/kendryte/k210/configs/defconfig
> > > >  create mode 100644 platform/nuclei/ux600/Kconfig
> > > >  delete mode 100644 platform/nuclei/ux600/config.mk
> > > >  create mode 100644 platform/nuclei/ux600/configs/defconfig
> > > >  create mode 100644 platform/template/Kconfig
> > > >  delete mode 100644 platform/template/config.mk
> > > >  create mode 100644 platform/template/configs/defconfig
> > > >
> > > > --
> > > > 2.34.1
> > > >
> > > >
> > > > --
> > > > opensbi mailing list
> > > > opensbi at lists.infradead.org
> > > > http://lists.infradead.org/mailman/listinfo/opensbi
> > >
> > > --
> > > opensbi mailing list
> > > opensbi at lists.infradead.org
> > > http://lists.infradead.org/mailman/listinfo/opensbi


From heinrich.schuchardt at canonical.com  Mon Jul 18 23:21:19 2022
From: heinrich.schuchardt at canonical.com (Heinrich Schuchardt)
Date: Tue, 19 Jul 2022 08:21:19 +0200
Subject: [PATCH v2 00/14] OpenSBI Kconfig Support
In-Reply-To: <CAK9=C2U7s1FKYw+FqBbZz5mTP=Gz9mRJ-CLr4m3+iqg9PWiQ8A@mail.gmail.com>
References: <20220718145611.2211638-1-apatel@ventanamicro.com>
 <20220718184807.dac3r5bvsublyj2n@kamzik>
 <CAEn-LToptF6LHHZ3nUzCe6KYXBT6FHk0uyVrNfubjC8MppSfhA@mail.gmail.com>
 <CAK9=C2UE088wC3TxUFhrRUNMYQpfzvaCZAt4AGMjBcjnvktqWw@mail.gmail.com>
 <CAK9=C2U7s1FKYw+FqBbZz5mTP=Gz9mRJ-CLr4m3+iqg9PWiQ8A@mail.gmail.com>
Message-ID: <44b673ad-6458-4d01-fc1d-5cb0a35946dc@canonical.com>

On 7/19/22 06:17, Anup Patel wrote:
> On Tue, Jul 19, 2022 at 9:34 AM Anup Patel <apatel at ventanamicro.com> wrote:
>>
>> +Heinrich
>>
>> On Tue, Jul 19, 2022 at 3:05 AM David Abdurachmanov
>> <david.abdurachmanov at gmail.com> wrote:
>>>
>>> On Mon, Jul 18, 2022 at 8:49 PM Andrew Jones <ajones at ventanamicro.com> wrote:
>>>>
>>>> On Mon, Jul 18, 2022 at 08:25:57PM +0530, Anup Patel wrote:
>>>>> In future, we will see more drivers in OpenSBI for system reset, serial port,
>>>>> power management, etc platform specific devices. We need platform specific
>>>>> compile time configuration to allow users control the size of OpenSBI generic
>>>>> firmware binaries by only enabling desired drivers. In other words, users
>>>>> should be allowed to restrict the set of RISC-V platforms on which OpenSBI
>>>>> generic firmwares built by them can run.
>>>>>
>>>>> To address above, we add Kconfig support for OpenSBI platforms using the
>>>>> kconfig-frontends project which is a standalone kconfig parser.
>>>>>
>>>>> The kconfig-frontends is available as a package/receipe on various distros
>>>>> (such as Debian, UBuntu, OpenSUSE, etc) and Yocto project.
>>>>
>>>> Hi Anup,
>>>>
>>>> It doesn't look like Fedora provides that package. Also, is the source
>>>> for the packages of those other distros maintained? If I found the
>>>> correct source, then it's [1], which hasn't had any attention in five
>>>> years, even though there have been hundreds of commits to Linux's
>>>> scripts/kconfig. I wonder if we shouldn't consider adopting
>>>> scripts/minikconf.py from the QEMU sources and sticking to the kconfig
>>>> language subset that parser supports?
>>>
>>> After spending a bit of time googling about kconfig-frontends it seems
>>> to be a dead project.
>>
>> Yes, the kconfig-frontends project is not being developed but there are
>> forks of this project since quite a few distros and yocto have support for
>> it.

Ubuntu's package is based on 
https://salsa.debian.org/philou/kconfig-frontends which has not seen a 
code update for four years. The upstream project is obviously also dead.

>>
>> Is there a requirement that Fedora only adds packages for active projects ?
>>
>>>
>>> Would kconfiglib work? That seems to be alive. While not directly
>>> available in Fedora too it's on pip.
>>
>> I explored the kconfiglib project. It is only a library and we will endup
>> maintaining our own tools for menuconfig, defconfig, etc in the OpenSBI
>> sources.
> 
> I stand corrected. The kconfiglib project when installed using python-pip
> will install various tools menuconfig, defconfig, etc along with it which is
> very nice.

Securitywise pip is a not recommendable. There is no code review 
required before uploading to the Python package repository.

> 
> If we go with the kconfiglib project then we should expect users to install
> the kconfiglib package before building OpenSBI. Is this a reasonable
> requirement for Fedora and UBuntu ?
> 
> @Heinrich are you okay with this 

Ubuntu provides OpenSBI as a package. So it is package maintainers not 
users that are concerned. kconfig-frontends currently is in the Universe 
repository (community supported). This would have to change since 
OpenSBI is in the main repository (Canonical supported). Putting 
software based on a a dead project into main is not preferred.

Maintaining OpenSBI for a distro is easier with a single version of the 
the software instead of maintaining a package for each board. So I was 
quite happy with PLATFORM=generic ruling them all.

Kconfig is licensed under GPL. It will not be available in BSD 
distributions. Please, include BSD distros in your considerations.

Best regards

Heinrich

> 
> Regards,
> Anup
> 
>>
>> The best thing would be if we can avoid adding more tools and scripts
>> in OpenSBI for kconfig. The kconfig-frontends allows us to do that.
>>
>> If we have to add some tool/script in OpenSBI then I would prefer
>> something (like QEMU scripts/minikconf.py) which is minimalistic
>> and does not add maintenance burden.
>>
>> Regards,
>> Anup
>>
>>>
>>>>
>>>> [1] http://ymorin.is-a-geek.org/projects/kconfig-frontends
>>>>
>>>> Thanks,
>>>> drew
>>>>
>>>>>
>>>>> To try OpenSBI Kconfig support with generic platform, here are few
>>>>> example commands:
>>>>> make PLATFORM=generic             <-- builds OpenSBI using "defconfig"
>>>>> make PLATFORM=generic menuconfig  <-- launches graphical interface for changes
>>>>>
>>>>> These patches can also be found in the kconfig_v2 branch at:
>>>>> https://github.com/avpatel/opensbi.git
>>>>>
>>>>> Changes since v1:
>>>>>   - Fix SPDX license specifier in new Kconfig files
>>>>>
>>>>> Anup Patel (14):
>>>>>    Makefile: Add initial kconfig support for each platform
>>>>>    Makefile: Compile lib/utils sources separately for each platform
>>>>>    lib: utils/serial: Use kconfig for enabling/disabling drivers
>>>>>    lib: utils/reset: Use kconfig for enabling/disabling drivers
>>>>>    lib: utils/sys: Use kconfig for enabling/disabling drivers
>>>>>    lib: utils/timer: Use kconfig for enabling/disabling drivers
>>>>>    lib: utils/ipi: Use kconfig for enabling/disabling drivers
>>>>>    lib: utils/irqchip: Use kconfig for enabling/disabling drivers
>>>>>    lib: utils/i2c: Use kconfig for enabling/disabling drivers
>>>>>    lib: utils/gpio: Use kconfig for enabling/disabling drivers
>>>>>    lib: utils/fdt: Use kconfig for enabling/disabling
>>>>>    platform: generic: Use kconfig for enabling/disabling overrides
>>>>>    platform: Remove redundant config.mk from all platforms
>>>>>    docs: Update documentation for kconfig support
>>>>>
>>>>>   Kconfig                                   |  23 +++++
>>>>>   Makefile                                  | 102 ++++++++++++++++------
>>>>>   README.md                                 |  25 +++++-
>>>>>   docs/firmware/fw_dynamic.md               |   2 +-
>>>>>   docs/firmware/fw_jump.md                  |   4 +-
>>>>>   docs/firmware/fw_payload.md               |   4 +-
>>>>>   docs/library_usage.md                     |   6 +-
>>>>>   docs/platform/platform.md                 |   4 +-
>>>>>   docs/platform_guide.md                    |   9 +-
>>>>>   firmware/Kconfig                          |   1 +
>>>>>   include/sbi_utils/fdt/fdt_domain.h        |   9 ++
>>>>>   include/sbi_utils/fdt/fdt_pmu.h           |  10 +++
>>>>>   include/sbi_utils/ipi/fdt_ipi.h           |   9 ++
>>>>>   include/sbi_utils/irqchip/fdt_irqchip.h   |  10 +++
>>>>>   include/sbi_utils/irqchip/imsic.h         |  10 +++
>>>>>   include/sbi_utils/reset/fdt_reset.h       |  12 +++
>>>>>   include/sbi_utils/serial/fdt_serial.h     |   8 ++
>>>>>   include/sbi_utils/timer/fdt_timer.h       |   9 ++
>>>>>   lib/utils/Kconfig                         |  25 ++++++
>>>>>   lib/utils/fdt/Kconfig                     |  18 ++++
>>>>>   lib/utils/fdt/objects.mk                  |   8 +-
>>>>>   lib/utils/gpio/Kconfig                    |  23 +++++
>>>>>   lib/utils/gpio/objects.mk                 |  10 +--
>>>>>   lib/utils/i2c/Kconfig                     |  23 +++++
>>>>>   lib/utils/i2c/objects.mk                  |  10 +--
>>>>>   lib/utils/ipi/Kconfig                     |  23 +++++
>>>>>   lib/utils/ipi/objects.mk                  |  10 +--
>>>>>   lib/utils/irqchip/Kconfig                 |  41 +++++++++
>>>>>   lib/utils/irqchip/objects.mk              |  22 ++---
>>>>>   lib/utils/libfdt/Kconfig                  |   5 ++
>>>>>   lib/utils/libfdt/objects.mk               |   4 +-
>>>>>   lib/utils/reset/Kconfig                   |  37 ++++++++
>>>>>   lib/utils/reset/objects.mk                |  28 +++---
>>>>>   lib/utils/serial/Kconfig                  |  73 ++++++++++++++++
>>>>>   lib/utils/serial/objects.mk               |  44 +++++-----
>>>>>   lib/utils/sys/Kconfig                     |  13 +++
>>>>>   lib/utils/sys/objects.mk                  |   4 +-
>>>>>   lib/utils/timer/Kconfig                   |  23 +++++
>>>>>   lib/utils/timer/objects.mk                |  10 +--
>>>>>   platform/andes/ae350/Kconfig              |   8 ++
>>>>>   platform/andes/ae350/config.mk            |  36 --------
>>>>>   platform/andes/ae350/configs/defconfig    |   0
>>>>>   platform/andes/ae350/objects.mk           |  29 ++++++
>>>>>   platform/fpga/ariane/Kconfig              |  10 +++
>>>>>   platform/fpga/ariane/config.mk            |  36 --------
>>>>>   platform/fpga/ariane/configs/defconfig    |   0
>>>>>   platform/fpga/ariane/objects.mk           |  35 ++++++++
>>>>>   platform/fpga/openpiton/Kconfig           |  10 +++
>>>>>   platform/fpga/openpiton/config.mk         |  35 --------
>>>>>   platform/fpga/openpiton/configs/defconfig |   0
>>>>>   platform/fpga/openpiton/objects.mk        |  35 ++++++++
>>>>>   platform/generic/Kconfig                  |  26 ++++++
>>>>>   platform/generic/allwinner/objects.mk     |   4 +-
>>>>>   platform/generic/config.mk                |  40 ---------
>>>>>   platform/generic/configs/defconfig        |  29 ++++++
>>>>>   platform/generic/objects.mk               |  33 +++++++
>>>>>   platform/generic/sifive/objects.mk        |   8 +-
>>>>>   platform/kendryte/k210/Kconfig            |  10 +++
>>>>>   platform/kendryte/k210/config.mk          |  19 ----
>>>>>   platform/kendryte/k210/configs/defconfig  |   0
>>>>>   platform/kendryte/k210/objects.mk         |  12 +++
>>>>>   platform/nuclei/ux600/Kconfig             |  10 +++
>>>>>   platform/nuclei/ux600/config.mk           |  30 -------
>>>>>   platform/nuclei/ux600/configs/defconfig   |   0
>>>>>   platform/nuclei/ux600/objects.mk          |  22 +++++
>>>>>   platform/nuclei/ux600/platform.c          |   1 -
>>>>>   platform/template/Kconfig                 |   9 ++
>>>>>   platform/template/config.mk               |  77 ----------------
>>>>>   platform/template/configs/defconfig       |   0
>>>>>   platform/template/objects.mk              |  72 +++++++++++++++
>>>>>   platform/template/platform.c              |   2 +-
>>>>>   scripts/create-binary-archive.sh          |   2 +-
>>>>>   72 files changed, 948 insertions(+), 403 deletions(-)
>>>>>   create mode 100644 Kconfig
>>>>>   create mode 100644 firmware/Kconfig
>>>>>   create mode 100644 lib/utils/Kconfig
>>>>>   create mode 100644 lib/utils/fdt/Kconfig
>>>>>   create mode 100644 lib/utils/gpio/Kconfig
>>>>>   create mode 100644 lib/utils/i2c/Kconfig
>>>>>   create mode 100644 lib/utils/ipi/Kconfig
>>>>>   create mode 100644 lib/utils/irqchip/Kconfig
>>>>>   create mode 100644 lib/utils/libfdt/Kconfig
>>>>>   create mode 100644 lib/utils/reset/Kconfig
>>>>>   create mode 100644 lib/utils/serial/Kconfig
>>>>>   create mode 100644 lib/utils/sys/Kconfig
>>>>>   create mode 100644 lib/utils/timer/Kconfig
>>>>>   create mode 100644 platform/andes/ae350/Kconfig
>>>>>   delete mode 100644 platform/andes/ae350/config.mk
>>>>>   create mode 100644 platform/andes/ae350/configs/defconfig
>>>>>   create mode 100644 platform/fpga/ariane/Kconfig
>>>>>   delete mode 100644 platform/fpga/ariane/config.mk
>>>>>   create mode 100644 platform/fpga/ariane/configs/defconfig
>>>>>   create mode 100644 platform/fpga/openpiton/Kconfig
>>>>>   delete mode 100644 platform/fpga/openpiton/config.mk
>>>>>   create mode 100644 platform/fpga/openpiton/configs/defconfig
>>>>>   create mode 100644 platform/generic/Kconfig
>>>>>   delete mode 100644 platform/generic/config.mk
>>>>>   create mode 100644 platform/generic/configs/defconfig
>>>>>   create mode 100644 platform/kendryte/k210/Kconfig
>>>>>   delete mode 100644 platform/kendryte/k210/config.mk
>>>>>   create mode 100644 platform/kendryte/k210/configs/defconfig
>>>>>   create mode 100644 platform/nuclei/ux600/Kconfig
>>>>>   delete mode 100644 platform/nuclei/ux600/config.mk
>>>>>   create mode 100644 platform/nuclei/ux600/configs/defconfig
>>>>>   create mode 100644 platform/template/Kconfig
>>>>>   delete mode 100644 platform/template/config.mk
>>>>>   create mode 100644 platform/template/configs/defconfig
>>>>>
>>>>> --
>>>>> 2.34.1
>>>>>
>>>>>
>>>>> --
>>>>> opensbi mailing list
>>>>> opensbi at lists.infradead.org
>>>>> http://lists.infradead.org/mailman/listinfo/opensbi
>>>>
>>>> --
>>>> opensbi mailing list
>>>> opensbi at lists.infradead.org
>>>> http://lists.infradead.org/mailman/listinfo/opensbi



From ajones at ventanamicro.com  Tue Jul 19 00:02:35 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Tue, 19 Jul 2022 09:02:35 +0200
Subject: [PATCH 1/2] lib: sbi: Add the bound check for firmware events
In-Reply-To: <20220718200458.3015600-1-atishp@rivosinc.com>
References: <20220718200458.3015600-1-atishp@rivosinc.com>
Message-ID: <20220719070235.yzfne4bwamh7a43n@kamzik>

On Mon, Jul 18, 2022 at 01:04:57PM -0700, Atish Patra wrote:
> Currently, there is no sanity check for firmware event code. We don't see
> any issue as Linux kernel driver does a bound check on firmware events
> already. However, OpenSBI can't assume sane supervisor mode software
> always. Thus, an invalid event idx can cause a buffer overflow error.
> 
> Improve the sanity check for firmware events.
> 
> Signed-off-by: Atish Patra <atishp at rivosinc.com>
> ---
>  lib/sbi/sbi_pmu.c | 7 ++++++-
>  1 file changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
> index 3f5fd1031b25..65bc9dd63f6b 100644
> --- a/lib/sbi/sbi_pmu.c
> +++ b/lib/sbi/sbi_pmu.c
> @@ -123,6 +123,9 @@ static int pmu_ctr_validate(uint32_t cidx, uint32_t *event_idx_code)
>  		return SBI_EINVAL;
>  
>  	*event_idx_code = get_cidx_code(event_idx_val);
> +	if ((event_idx_type == SBI_PMU_EVENT_TYPE_FW) &&
> +	    (*event_idx_code >= SBI_PMU_FW_MAX))
> +		return SBI_EINVAL;

I think it would be better to only write *event_idx_code if we know
it's valid. Actually, maybe at the start of pmu_ctr_validate() we
should initialize it to something we know is invalid and then at the
bottom write it after we're sure we have something worth writing.

If pmu_ctr_validate() should be used to validate all event types, then
shouldn't we have a switch here, something like...

  switch (event_idx_type) {
  case SBI_PMU_EVENT_TYPE_HW:
    if (code >= SBI_PMU_HW_MAX)
        return SBI_EINVAL;
    break;
  case SBI_PMU_EVENT_TYPE_HW_CACHE:
    if (code >= SBI_PMU_HW_CACHE_MAX)
        return SBI_EINVAL;
    break;
  ...

>  
>  	return event_idx_type;
>  }
> @@ -652,6 +655,8 @@ skip_match:
>  			pmu_ctr_start_hw(ctr_idx, 0, false);
>  	} else if (event_type == SBI_PMU_EVENT_TYPE_FW) {
>  		fw_evt_code = get_cidx_code(event_idx);
> +		if (unlikely(fw_evt_code >= SBI_PMU_FW_MAX))
> +			return SBI_ENOTSUPP;

Validation buried down here is a bit scary. It would be much nicer if we
could pull all the validation up to the ecall entry points.

>  		fevent = &fw_event_map[hartid][fw_evt_code];
>  		if (flags & SBI_PMU_CFG_FLAG_CLEAR_VALUE)
>  			fevent->curr_count = 0;
> @@ -721,7 +726,7 @@ static void pmu_reset_event_map(u32 hartid)
>  	/* Initialize the counter to event mapping table */
>  	for (j = 3; j < total_ctrs; j++)
>  		active_events[hartid][j] = SBI_PMU_EVENT_IDX_INVALID;
> -	for (j = 0; j < SBI_PMU_FW_CTR_MAX; j++)
> +	for (j = 0; j < SBI_PMU_FW_EVENT_MAX; j++)

This change is not related to what the commit message is talking about,
so it belongs in a separate patch.

Thanks,
drew

>  		sbi_memset(&fw_event_map[hartid][j], 0,
>  			   sizeof(struct sbi_pmu_fw_event));
>  }
> -- 
> 2.25.1
> 
> 
> -- 
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi


From ajones at ventanamicro.com  Tue Jul 19 00:10:29 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Tue, 19 Jul 2022 09:10:29 +0200
Subject: [PATCH 1/2] lib: sbi: Add the bound check for firmware events
In-Reply-To: <20220718200458.3015600-1-atishp@rivosinc.com>
References: <20220718200458.3015600-1-atishp@rivosinc.com>
Message-ID: <20220719071029.te2tqptafw7kilxm@kamzik>


Hi Atish,

Top posting because there's no cover letter to reply to. While reviewing
for this patch I see another buffer overrun possibility in
pmu_ctr_validate()

static int pmu_ctr_validate(uint32_t cidx, uint32_t *event_idx_code)
{
        uint32_t event_idx_val;
        uint32_t event_idx_type;
        u32 hartid = current_hartid();

        event_idx_val = active_events[hartid][cidx]; <--- Here, because...

...we have...

int sbi_pmu_ctr_read(uint32_t cidx, unsigned long *cval)
{
        int event_idx_type;
        uint32_t event_code;
        uint64_t cval64;

        event_idx_type = pmu_ctr_validate(cidx, &event_code);

...where sbi_pmu_ctr_read() is called directly from the ecall handler
passing a0 for cidx.

Thanks,
drew


On Mon, Jul 18, 2022 at 01:04:57PM -0700, Atish Patra wrote:
> Currently, there is no sanity check for firmware event code. We don't see
> any issue as Linux kernel driver does a bound check on firmware events
> already. However, OpenSBI can't assume sane supervisor mode software
> always. Thus, an invalid event idx can cause a buffer overflow error.
> 
> Improve the sanity check for firmware events.
> 
> Signed-off-by: Atish Patra <atishp at rivosinc.com>
> ---
>  lib/sbi/sbi_pmu.c | 7 ++++++-
>  1 file changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
> index 3f5fd1031b25..65bc9dd63f6b 100644
> --- a/lib/sbi/sbi_pmu.c
> +++ b/lib/sbi/sbi_pmu.c
> @@ -123,6 +123,9 @@ static int pmu_ctr_validate(uint32_t cidx, uint32_t *event_idx_code)
>  		return SBI_EINVAL;
>  
>  	*event_idx_code = get_cidx_code(event_idx_val);
> +	if ((event_idx_type == SBI_PMU_EVENT_TYPE_FW) &&
> +	    (*event_idx_code >= SBI_PMU_FW_MAX))
> +		return SBI_EINVAL;
>  
>  	return event_idx_type;
>  }
> @@ -652,6 +655,8 @@ skip_match:
>  			pmu_ctr_start_hw(ctr_idx, 0, false);
>  	} else if (event_type == SBI_PMU_EVENT_TYPE_FW) {
>  		fw_evt_code = get_cidx_code(event_idx);
> +		if (unlikely(fw_evt_code >= SBI_PMU_FW_MAX))
> +			return SBI_ENOTSUPP;
>  		fevent = &fw_event_map[hartid][fw_evt_code];
>  		if (flags & SBI_PMU_CFG_FLAG_CLEAR_VALUE)
>  			fevent->curr_count = 0;
> @@ -721,7 +726,7 @@ static void pmu_reset_event_map(u32 hartid)
>  	/* Initialize the counter to event mapping table */
>  	for (j = 3; j < total_ctrs; j++)
>  		active_events[hartid][j] = SBI_PMU_EVENT_IDX_INVALID;
> -	for (j = 0; j < SBI_PMU_FW_CTR_MAX; j++)
> +	for (j = 0; j < SBI_PMU_FW_EVENT_MAX; j++)
>  		sbi_memset(&fw_event_map[hartid][j], 0,
>  			   sizeof(struct sbi_pmu_fw_event));
>  }
> -- 
> 2.25.1
> 
> 
> -- 
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi


From schwab at suse.de  Tue Jul 19 00:41:41 2022
From: schwab at suse.de (Andreas Schwab)
Date: Tue, 19 Jul 2022 09:41:41 +0200
Subject: [PATCH v2 00/14] OpenSBI Kconfig Support
In-Reply-To: <44b673ad-6458-4d01-fc1d-5cb0a35946dc@canonical.com> (Heinrich
	Schuchardt's message of "Tue, 19 Jul 2022 08:21:19 +0200")
References: <20220718145611.2211638-1-apatel@ventanamicro.com>
	<20220718184807.dac3r5bvsublyj2n@kamzik>
	<CAEn-LToptF6LHHZ3nUzCe6KYXBT6FHk0uyVrNfubjC8MppSfhA@mail.gmail.com>
	<CAK9=C2UE088wC3TxUFhrRUNMYQpfzvaCZAt4AGMjBcjnvktqWw@mail.gmail.com>
	<CAK9=C2U7s1FKYw+FqBbZz5mTP=Gz9mRJ-CLr4m3+iqg9PWiQ8A@mail.gmail.com>
	<44b673ad-6458-4d01-fc1d-5cb0a35946dc@canonical.com>
Message-ID: <mvmtu7dft1m.fsf@suse.de>

On Jul 19 2022, Heinrich Schuchardt wrote:

> Ubuntu provides OpenSBI as a package. So it is package maintainers not
> users that are concerned. kconfig-frontends currently is in the Universe
> repository (community supported). This would have to change since OpenSBI
> is in the main repository (Canonical supported). Putting software based on
> a a dead project into main is not preferred.

kconfig-frontends is not available on openSUSE Factory either.

-- 
Andreas Schwab, SUSE Labs, schwab at suse.de
GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE  1748 E4D4 88E3 0EEA B9D7
"And now for something completely different."


From ajones at ventanamicro.com  Tue Jul 19 01:47:15 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Tue, 19 Jul 2022 10:47:15 +0200
Subject: [PATCH 2/2] lib: sbi: Fix counter index sanity check
In-Reply-To: <20220718200458.3015600-2-atishp@rivosinc.com>
References: <20220718200458.3015600-1-atishp@rivosinc.com>
 <20220718200458.3015600-2-atishp@rivosinc.com>
Message-ID: <20220719084715.mpuogf5g2x4qy4me@kamzik>

On Mon, Jul 18, 2022 at 01:04:58PM -0700, Atish Patra wrote:
> The current implementation computes the possible counter range
> by doing a left shift of counter base. However, this may overflow depending
> on the counter base value. In case of overflow, the highest counter id
> may be computed incorrectly. As per the SBI specification, the respective
> function should return an error if any of the counter is not valid.
> 
> Fix the counter index check by avoiding left shifting while doing the
> sanity checks.
> 
> Signed-off-by: Atish Patra <atishp at rivosinc.com>
> ---
>  lib/sbi/sbi_pmu.c | 33 +++++++++++++++++----------------
>  1 file changed, 17 insertions(+), 16 deletions(-)
> 
> diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
> index 65bc9dd63f6b..29e7e2954ae3 100644
> --- a/lib/sbi/sbi_pmu.c
> +++ b/lib/sbi/sbi_pmu.c
> @@ -346,25 +346,26 @@ int sbi_pmu_ctr_start(unsigned long cbase, unsigned long cmask,
>  {
>  	int event_idx_type;
>  	uint32_t event_code;
> -	unsigned long ctr_mask = cmask << cbase;
>  	int ret = SBI_EINVAL;
>  	bool bUpdate = FALSE;
> +	int i, cidx;
>  
> -	if (sbi_fls(ctr_mask) >= total_ctrs)
> +	if ((cbase + sbi_fls(cmask)) >= total_ctrs)
>  		return ret;
>  
>  	if (flags & SBI_PMU_START_FLAG_SET_INIT_VALUE)
>  		bUpdate = TRUE;
>  
> -	for_each_set_bit_from(cbase, &ctr_mask, total_ctrs) {
> -		event_idx_type = pmu_ctr_validate(cbase, &event_code);
> +	for_each_set_bit(i, &cmask, total_ctrs) {
> +		cidx = i + cbase;

I guess this for_each_set_bit_from() to for_each_set_bit() change is
necessary to avoid skipping cidx == cbase after we no longer shift
the mask. Might be nice to point that out in the commit message since
it's a bit subtle.

> +		event_idx_type = pmu_ctr_validate(cidx, &event_code);
>  		if (event_idx_type < 0)
>  			/* Continue the start operation for other counters */
>  			continue;
>  		else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW)
> -			ret = pmu_ctr_start_fw(cbase, event_code, ival, bUpdate);
> +			ret = pmu_ctr_start_fw(cidx, event_code, ival, bUpdate);
>  		else
> -			ret = pmu_ctr_start_hw(cbase, ival, bUpdate);
> +			ret = pmu_ctr_start_hw(cidx, ival, bUpdate);
>  	}
>  
>  	return ret;
> @@ -424,25 +425,26 @@ int sbi_pmu_ctr_stop(unsigned long cbase, unsigned long cmask,
>  	int ret = SBI_EINVAL;
>  	int event_idx_type;
>  	uint32_t event_code;
> -	unsigned long ctr_mask = cmask << cbase;
> +	int i, cidx;
>  
> -	if (sbi_fls(ctr_mask) >= total_ctrs)
> +	if ((cbase + sbi_fls(cmask)) >= total_ctrs)
>  		return SBI_EINVAL;
>  
> -	for_each_set_bit_from(cbase, &ctr_mask, total_ctrs) {
> -		event_idx_type = pmu_ctr_validate(cbase, &event_code);
> +	for_each_set_bit(i, &cmask, total_ctrs) {
> +		cidx = i + cbase;
> +		event_idx_type = pmu_ctr_validate(cidx, &event_code);
>  		if (event_idx_type < 0)
>  			/* Continue the stop operation for other counters */
>  			continue;
>  
>  		else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW)
> -			ret = pmu_ctr_stop_fw(cbase, event_code);
> +			ret = pmu_ctr_stop_fw(cidx, event_code);
>  		else
> -			ret = pmu_ctr_stop_hw(cbase);
> +			ret = pmu_ctr_stop_hw(cidx);
>  
>  		if (flag & SBI_PMU_STOP_FLAG_RESET) {
> -			active_events[hartid][cbase] = SBI_PMU_EVENT_IDX_INVALID;
> -			pmu_reset_hw_mhpmevent(cbase);
> +			active_events[hartid][cidx] = SBI_PMU_EVENT_IDX_INVALID;
> +			pmu_reset_hw_mhpmevent(cidx);
>  		}
>  	}
>  
> @@ -618,10 +620,9 @@ int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask,
>  	int event_type = get_cidx_type(event_idx);
>  	struct sbi_pmu_fw_event *fevent;
>  	uint32_t fw_evt_code;
> -	unsigned long tmp = cidx_mask << cidx_base;
>  
>  	/* Do a basic sanity check of counter base & mask */
> -	if (sbi_fls(tmp) >= total_ctrs || event_type >= SBI_PMU_EVENT_TYPE_MAX)
> +	if ((cidx_base + sbi_fls(cidx_mask)) >= total_ctrs || event_type >= SBI_PMU_EVENT_TYPE_MAX)
>  		return SBI_EINVAL;
>  
>  	if (flags & SBI_PMU_CFG_FLAG_SKIP_MATCH) {
> -- 
> 2.25.1
>

Reviewed-by: Andrew Jones <ajones at ventanamicro.com>


From ajones at ventanamicro.com  Tue Jul 19 02:00:48 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Tue, 19 Jul 2022 11:00:48 +0200
Subject: [PATCH v2 00/14] OpenSBI Kconfig Support
In-Reply-To: <CAK9=C2VfU1ekpr5CqJd4n2-1NCVBcKrM7d3W6iGbMTX=kggPHA@mail.gmail.com>
References: <20220718145611.2211638-1-apatel@ventanamicro.com>
 <20220718184807.dac3r5bvsublyj2n@kamzik>
 <CAK9=C2VfU1ekpr5CqJd4n2-1NCVBcKrM7d3W6iGbMTX=kggPHA@mail.gmail.com>
Message-ID: <20220719090048.unokmxb3iyko57nl@kamzik>

On Tue, Jul 19, 2022 at 09:25:35AM +0530, Anup Patel wrote:
> +Heinrich
> On Tue, Jul 19, 2022 at 12:18 AM Andrew Jones <ajones at ventanamicro.com> wrote:
> >
> > On Mon, Jul 18, 2022 at 08:25:57PM +0530, Anup Patel wrote:
> > > In future, we will see more drivers in OpenSBI for system reset, serial port,
> > > power management, etc platform specific devices. We need platform specific
> > > compile time configuration to allow users control the size of OpenSBI generic
> > > firmware binaries by only enabling desired drivers. In other words, users
> > > should be allowed to restrict the set of RISC-V platforms on which OpenSBI
> > > generic firmwares built by them can run.
> > >
> > > To address above, we add Kconfig support for OpenSBI platforms using the
> > > kconfig-frontends project which is a standalone kconfig parser.
> > >
> > > The kconfig-frontends is available as a package/receipe on various distros
> > > (such as Debian, UBuntu, OpenSUSE, etc) and Yocto project.
> >
> > Hi Anup,
> >
> > It doesn't look like Fedora provides that package. Also, is the source
> > for the packages of those other distros maintained? If I found the
> > correct source, then it's [1], which hasn't had any attention in five
> > years, even though there have been hundreds of commits to Linux's
> > scripts/kconfig. I wonder if we shouldn't consider adopting
> > scripts/minikconf.py from the QEMU sources and sticking to the kconfig
> > language subset that parser supports?
> >
> > [1] http://ymorin.is-a-geek.org/projects/kconfig-frontends
> 
> Actually, it seems quite a few distros (particularly debian, ubuntu,
> and yocto) have already included the kconfig-frontends package so
> I wonder why Fedora did not include a package for it ?

As others on this thread pointed out, it's probably due to lack of
interest in maintaining it.

> 
> The QEMU scripts/minikconf.py is pretty compact and would be
> our next best alternative but it doesn't have menuconfig support.

IMHO, we should try to avoid requiring external dependencies. Something
like menuconfig will require something external, so we should also have
a text-only alternative built in.

Thanks,
drew

> 
> Regards,
> Anup
> 
> >
> > Thanks,
> > drew
> >
> > >
> > > To try OpenSBI Kconfig support with generic platform, here are few
> > > example commands:
> > > make PLATFORM=generic             <-- builds OpenSBI using "defconfig"
> > > make PLATFORM=generic menuconfig  <-- launches graphical interface for changes
> > >
> > > These patches can also be found in the kconfig_v2 branch at:
> > > https://github.com/avpatel/opensbi.git
> > >
> > > Changes since v1:
> > >  - Fix SPDX license specifier in new Kconfig files
> > >
> > > Anup Patel (14):
> > >   Makefile: Add initial kconfig support for each platform
> > >   Makefile: Compile lib/utils sources separately for each platform
> > >   lib: utils/serial: Use kconfig for enabling/disabling drivers
> > >   lib: utils/reset: Use kconfig for enabling/disabling drivers
> > >   lib: utils/sys: Use kconfig for enabling/disabling drivers
> > >   lib: utils/timer: Use kconfig for enabling/disabling drivers
> > >   lib: utils/ipi: Use kconfig for enabling/disabling drivers
> > >   lib: utils/irqchip: Use kconfig for enabling/disabling drivers
> > >   lib: utils/i2c: Use kconfig for enabling/disabling drivers
> > >   lib: utils/gpio: Use kconfig for enabling/disabling drivers
> > >   lib: utils/fdt: Use kconfig for enabling/disabling
> > >   platform: generic: Use kconfig for enabling/disabling overrides
> > >   platform: Remove redundant config.mk from all platforms
> > >   docs: Update documentation for kconfig support
> > >
> > >  Kconfig                                   |  23 +++++
> > >  Makefile                                  | 102 ++++++++++++++++------
> > >  README.md                                 |  25 +++++-
> > >  docs/firmware/fw_dynamic.md               |   2 +-
> > >  docs/firmware/fw_jump.md                  |   4 +-
> > >  docs/firmware/fw_payload.md               |   4 +-
> > >  docs/library_usage.md                     |   6 +-
> > >  docs/platform/platform.md                 |   4 +-
> > >  docs/platform_guide.md                    |   9 +-
> > >  firmware/Kconfig                          |   1 +
> > >  include/sbi_utils/fdt/fdt_domain.h        |   9 ++
> > >  include/sbi_utils/fdt/fdt_pmu.h           |  10 +++
> > >  include/sbi_utils/ipi/fdt_ipi.h           |   9 ++
> > >  include/sbi_utils/irqchip/fdt_irqchip.h   |  10 +++
> > >  include/sbi_utils/irqchip/imsic.h         |  10 +++
> > >  include/sbi_utils/reset/fdt_reset.h       |  12 +++
> > >  include/sbi_utils/serial/fdt_serial.h     |   8 ++
> > >  include/sbi_utils/timer/fdt_timer.h       |   9 ++
> > >  lib/utils/Kconfig                         |  25 ++++++
> > >  lib/utils/fdt/Kconfig                     |  18 ++++
> > >  lib/utils/fdt/objects.mk                  |   8 +-
> > >  lib/utils/gpio/Kconfig                    |  23 +++++
> > >  lib/utils/gpio/objects.mk                 |  10 +--
> > >  lib/utils/i2c/Kconfig                     |  23 +++++
> > >  lib/utils/i2c/objects.mk                  |  10 +--
> > >  lib/utils/ipi/Kconfig                     |  23 +++++
> > >  lib/utils/ipi/objects.mk                  |  10 +--
> > >  lib/utils/irqchip/Kconfig                 |  41 +++++++++
> > >  lib/utils/irqchip/objects.mk              |  22 ++---
> > >  lib/utils/libfdt/Kconfig                  |   5 ++
> > >  lib/utils/libfdt/objects.mk               |   4 +-
> > >  lib/utils/reset/Kconfig                   |  37 ++++++++
> > >  lib/utils/reset/objects.mk                |  28 +++---
> > >  lib/utils/serial/Kconfig                  |  73 ++++++++++++++++
> > >  lib/utils/serial/objects.mk               |  44 +++++-----
> > >  lib/utils/sys/Kconfig                     |  13 +++
> > >  lib/utils/sys/objects.mk                  |   4 +-
> > >  lib/utils/timer/Kconfig                   |  23 +++++
> > >  lib/utils/timer/objects.mk                |  10 +--
> > >  platform/andes/ae350/Kconfig              |   8 ++
> > >  platform/andes/ae350/config.mk            |  36 --------
> > >  platform/andes/ae350/configs/defconfig    |   0
> > >  platform/andes/ae350/objects.mk           |  29 ++++++
> > >  platform/fpga/ariane/Kconfig              |  10 +++
> > >  platform/fpga/ariane/config.mk            |  36 --------
> > >  platform/fpga/ariane/configs/defconfig    |   0
> > >  platform/fpga/ariane/objects.mk           |  35 ++++++++
> > >  platform/fpga/openpiton/Kconfig           |  10 +++
> > >  platform/fpga/openpiton/config.mk         |  35 --------
> > >  platform/fpga/openpiton/configs/defconfig |   0
> > >  platform/fpga/openpiton/objects.mk        |  35 ++++++++
> > >  platform/generic/Kconfig                  |  26 ++++++
> > >  platform/generic/allwinner/objects.mk     |   4 +-
> > >  platform/generic/config.mk                |  40 ---------
> > >  platform/generic/configs/defconfig        |  29 ++++++
> > >  platform/generic/objects.mk               |  33 +++++++
> > >  platform/generic/sifive/objects.mk        |   8 +-
> > >  platform/kendryte/k210/Kconfig            |  10 +++
> > >  platform/kendryte/k210/config.mk          |  19 ----
> > >  platform/kendryte/k210/configs/defconfig  |   0
> > >  platform/kendryte/k210/objects.mk         |  12 +++
> > >  platform/nuclei/ux600/Kconfig             |  10 +++
> > >  platform/nuclei/ux600/config.mk           |  30 -------
> > >  platform/nuclei/ux600/configs/defconfig   |   0
> > >  platform/nuclei/ux600/objects.mk          |  22 +++++
> > >  platform/nuclei/ux600/platform.c          |   1 -
> > >  platform/template/Kconfig                 |   9 ++
> > >  platform/template/config.mk               |  77 ----------------
> > >  platform/template/configs/defconfig       |   0
> > >  platform/template/objects.mk              |  72 +++++++++++++++
> > >  platform/template/platform.c              |   2 +-
> > >  scripts/create-binary-archive.sh          |   2 +-
> > >  72 files changed, 948 insertions(+), 403 deletions(-)
> > >  create mode 100644 Kconfig
> > >  create mode 100644 firmware/Kconfig
> > >  create mode 100644 lib/utils/Kconfig
> > >  create mode 100644 lib/utils/fdt/Kconfig
> > >  create mode 100644 lib/utils/gpio/Kconfig
> > >  create mode 100644 lib/utils/i2c/Kconfig
> > >  create mode 100644 lib/utils/ipi/Kconfig
> > >  create mode 100644 lib/utils/irqchip/Kconfig
> > >  create mode 100644 lib/utils/libfdt/Kconfig
> > >  create mode 100644 lib/utils/reset/Kconfig
> > >  create mode 100644 lib/utils/serial/Kconfig
> > >  create mode 100644 lib/utils/sys/Kconfig
> > >  create mode 100644 lib/utils/timer/Kconfig
> > >  create mode 100644 platform/andes/ae350/Kconfig
> > >  delete mode 100644 platform/andes/ae350/config.mk
> > >  create mode 100644 platform/andes/ae350/configs/defconfig
> > >  create mode 100644 platform/fpga/ariane/Kconfig
> > >  delete mode 100644 platform/fpga/ariane/config.mk
> > >  create mode 100644 platform/fpga/ariane/configs/defconfig
> > >  create mode 100644 platform/fpga/openpiton/Kconfig
> > >  delete mode 100644 platform/fpga/openpiton/config.mk
> > >  create mode 100644 platform/fpga/openpiton/configs/defconfig
> > >  create mode 100644 platform/generic/Kconfig
> > >  delete mode 100644 platform/generic/config.mk
> > >  create mode 100644 platform/generic/configs/defconfig
> > >  create mode 100644 platform/kendryte/k210/Kconfig
> > >  delete mode 100644 platform/kendryte/k210/config.mk
> > >  create mode 100644 platform/kendryte/k210/configs/defconfig
> > >  create mode 100644 platform/nuclei/ux600/Kconfig
> > >  delete mode 100644 platform/nuclei/ux600/config.mk
> > >  create mode 100644 platform/nuclei/ux600/configs/defconfig
> > >  create mode 100644 platform/template/Kconfig
> > >  delete mode 100644 platform/template/config.mk
> > >  create mode 100644 platform/template/configs/defconfig
> > >
> > > --
> > > 2.34.1
> > >
> > >
> > > --
> > > opensbi mailing list
> > > opensbi at lists.infradead.org
> > > http://lists.infradead.org/mailman/listinfo/opensbi


From apatel at ventanamicro.com  Tue Jul 19 02:05:06 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Tue, 19 Jul 2022 14:35:06 +0530
Subject: [PATCH v2 00/14] OpenSBI Kconfig Support
In-Reply-To: <mvmtu7dft1m.fsf@suse.de>
References: <20220718145611.2211638-1-apatel@ventanamicro.com>
 <20220718184807.dac3r5bvsublyj2n@kamzik> <CAEn-LToptF6LHHZ3nUzCe6KYXBT6FHk0uyVrNfubjC8MppSfhA@mail.gmail.com>
 <CAK9=C2UE088wC3TxUFhrRUNMYQpfzvaCZAt4AGMjBcjnvktqWw@mail.gmail.com>
 <CAK9=C2U7s1FKYw+FqBbZz5mTP=Gz9mRJ-CLr4m3+iqg9PWiQ8A@mail.gmail.com>
 <44b673ad-6458-4d01-fc1d-5cb0a35946dc@canonical.com> <mvmtu7dft1m.fsf@suse.de>
Message-ID: <CAK9=C2XeNpHKyRNidmpDL0G3tdMA4QZG+_wX-uW4KXTWXpmKyA@mail.gmail.com>

On Tue, Jul 19, 2022 at 1:11 PM Andreas Schwab <schwab at suse.de> wrote:
>
> On Jul 19 2022, Heinrich Schuchardt wrote:
>
> > Ubuntu provides OpenSBI as a package. So it is package maintainers not
> > users that are concerned. kconfig-frontends currently is in the Universe
> > repository (community supported). This would have to change since OpenSBI
> > is in the main repository (Canonical supported). Putting software based on
> > a a dead project into main is not preferred.
>
> kconfig-frontends is not available on openSUSE Factory either.

Fair enough. We should use something else.

Kconfiglib seems to be a good option except the issues pointed by Heinrich:
1) Python pip is not recommended security wise
2) The LICENSE of Kconfiglib needs to be compatible with BSD distros

The Kconfiglib LICENSE is definitely not GPL and seems compatible
with BSD-2-clause and MIT licenses.
(Refer, https://github.com/ulfalizer/Kconfiglib/blob/master/LICENSE.txt)

To address security concerns, we can do one of the following:
A) Include Kconfiglib source under scripts/ directory.
B) Recommend that OpenSBI users, install Kconfiglib as distro
package instead of python-pip3 installer (e.g. python3-kconfiglib
package available on Ubuntu)

IMO, the option A mentioned above has a maintenance burden and
if possible we should avoid it.

Regards,
Anup




>
> --
> Andreas Schwab, SUSE Labs, schwab at suse.de
> GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE  1748 E4D4 88E3 0EEA B9D7
> "And now for something completely different."


From heinrich.schuchardt at canonical.com  Tue Jul 19 02:27:20 2022
From: heinrich.schuchardt at canonical.com (Heinrich Schuchardt)
Date: Tue, 19 Jul 2022 11:27:20 +0200
Subject: [PATCH v2 00/14] OpenSBI Kconfig Support
In-Reply-To: <CAK9=C2XeNpHKyRNidmpDL0G3tdMA4QZG+_wX-uW4KXTWXpmKyA@mail.gmail.com>
References: <20220718145611.2211638-1-apatel@ventanamicro.com>
 <20220718184807.dac3r5bvsublyj2n@kamzik>
 <CAEn-LToptF6LHHZ3nUzCe6KYXBT6FHk0uyVrNfubjC8MppSfhA@mail.gmail.com>
 <CAK9=C2UE088wC3TxUFhrRUNMYQpfzvaCZAt4AGMjBcjnvktqWw@mail.gmail.com>
 <CAK9=C2U7s1FKYw+FqBbZz5mTP=Gz9mRJ-CLr4m3+iqg9PWiQ8A@mail.gmail.com>
 <44b673ad-6458-4d01-fc1d-5cb0a35946dc@canonical.com>
 <mvmtu7dft1m.fsf@suse.de>
 <CAK9=C2XeNpHKyRNidmpDL0G3tdMA4QZG+_wX-uW4KXTWXpmKyA@mail.gmail.com>
Message-ID: <10919e46-894c-469c-35f3-1c4fe7389db8@canonical.com>

On 7/19/22 11:05, Anup Patel wrote:
> On Tue, Jul 19, 2022 at 1:11 PM Andreas Schwab <schwab at suse.de> wrote:
>>
>> On Jul 19 2022, Heinrich Schuchardt wrote:
>>
>>> Ubuntu provides OpenSBI as a package. So it is package maintainers not
>>> users that are concerned. kconfig-frontends currently is in the Universe
>>> repository (community supported). This would have to change since OpenSBI
>>> is in the main repository (Canonical supported). Putting software based on
>>> a a dead project into main is not preferred.
>>
>> kconfig-frontends is not available on openSUSE Factory either.
> 
> Fair enough. We should use something else.
> 
> Kconfiglib seems to be a good option except the issues pointed by Heinrich:
> 1) Python pip is not recommended security wise
> 2) The LICENSE of Kconfiglib needs to be compatible with BSD distros

Bugs in kconfiglib are not fixed in a timely manner. See
https://github.com/ulfalizer/Kconfiglib/issues/105

The last commit was in Jan 2020. So another dead project.

Best regards

Heinrich

> 
> The Kconfiglib LICENSE is definitely not GPL and seems compatible
> with BSD-2-clause and MIT licenses.
> (Refer, https://github.com/ulfalizer/Kconfiglib/blob/master/LICENSE.txt)
> 
> To address security concerns, we can do one of the following:
> A) Include Kconfiglib source under scripts/ directory.
> B) Recommend that OpenSBI users, install Kconfiglib as distro
> package instead of python-pip3 installer (e.g. python3-kconfiglib
> package available on Ubuntu)
> 
> IMO, the option A mentioned above has a maintenance burden and
> if possible we should avoid it.
> 
> Regards,
> Anup
> 
> 
> 
> 
>>
>> --
>> Andreas Schwab, SUSE Labs, schwab at suse.de
>> GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE  1748 E4D4 88E3 0EEA B9D7
>> "And now for something completely different."



From ajones at ventanamicro.com  Tue Jul 19 03:30:21 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Tue, 19 Jul 2022 12:30:21 +0200
Subject: [PATCH 2/4] lib: utils/serial: Initialize platform_uart_data to
 zero
In-Reply-To: <CD32E693-6D4E-4F48-AE14-4F47A08AD491@jrtc27.com>
References: <20220718172028.2006166-1-ajones@ventanamicro.com>
 <20220718172028.2006166-3-ajones@ventanamicro.com>
 <1D1800C8-676E-43E0-9F22-29DC5862E994@jrtc27.com>
 <20220718183533.7pdyuyfpae6xjfxc@kamzik>
 <CD32E693-6D4E-4F48-AE14-4F47A08AD491@jrtc27.com>
Message-ID: <20220719103021.xvvubp6sxvz6bdj5@kamzik>

On Mon, Jul 18, 2022 at 07:39:23PM +0100, Jessica Clarke wrote:
> On 18 Jul 2022, at 19:35, Andrew Jones <ajones at ventanamicro.com> wrote:
> > 
> > On Mon, Jul 18, 2022 at 06:42:40PM +0100, Jessica Clarke wrote:
> >> On 18 Jul 2022, at 18:20, Andrew Jones <ajones at ventanamicro.com> wrote:
> >>> 
> >>> While it doesn't look like there are any current cases of using
> >>> uninitialized data, let's zero all the UART data members to be
> >>> safe. Zero may not actually be better than a random number in
> >>> some cases, so all structure members should still be validated
> >>> before use, but at least zero is usually easier to debug than
> >>> some random stack garbage...
> >> 
> >> If fdt_parse_foo_uart_node leaves the platform_uart_data uninitialised
> >> (at least, parts that matter) then that seems like a bug to me. This
> >> just masks such bugs from any kind of sanitiser...
> > 
> > Hi Jess,
> > 
> > That's an interesting approach. Which tool(s) will point that out?
> > I just tried compiling with both gcc[1] and clang[2], noting that we
> > compile with -Wall, but neither complained when I purposely commented
> > out a used UART field.
> > 
> > [1] https://github.com/riscv/riscv-gnu-toolchain
> > [2] clang-14.0.0-1.fc36.x86_64
> 
> Like other sanitisers it needs some runtime support, but MSan, i.e.
> -fsanitize=memory.

Thanks, Jess. I'm a complete sanitizer noob and appreciate the pointer,
which has inspired me to do a bit of reading. I really like the idea of
getting comprehensive, automated tests in place allowing us to configure
debug builds with several sanitizers, but I see a few challenges

1) We need to link to the sanitizer runtime libraries. This may not be
   so easy for opensbi to do, depending on what the runtime libraries
   depend on. The libraries will likely expect fprintf to work, at a
   minimum, for example.

2) We need a way to get sanitizer output. Once we have UART(s) initialized
   we can use them (sanitizers appear to typically output to stderr), but
   until then we need to write the logs to memory where they can be
   dumped. (Maybe opensbi can provide an early, debug console device that
   does this.)

3) Not all sanitizers are supported by all architectures. Indeed MSan is
   not yet supported for RISCV (maybe it will be soon [1])

4) We need automated tests giving us near full coverage in order for the
   sanitizers to do their thing. (Those would be good to have even without
   sanitizers, but they're not yet there.)

With those challenges in mind, I don't believe we should assume we'll
be able to run a complete set of sanitizers on opensbi very soon. We
should probably consider other options for ensuring robustness and
debugability of the code.

Regarding this patch, I'm fine dropping it if the consensus is that
it's not better to zero memory which may be used uninitialized. And,
if that's the case, maybe we should document that policy somewhere.

[1] https://reviews.llvm.org/D97897

Thanks,
drew

> 
> Jess
> 
> > Thanks,
> > drew
> > 
> >> 
> >> Jess
> >> 
> >>> Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
> >>> ---
> >>> lib/utils/serial/fdt_serial_gaisler.c       | 2 +-
> >>> lib/utils/serial/fdt_serial_shakti.c        | 2 +-
> >>> lib/utils/serial/fdt_serial_sifive.c        | 2 +-
> >>> lib/utils/serial/fdt_serial_uart8250.c      | 2 +-
> >>> lib/utils/serial/fdt_serial_xlnx_uartlite.c | 2 +-
> >>> platform/fpga/openpiton/platform.c          | 2 +-
> >>> 6 files changed, 6 insertions(+), 6 deletions(-)
> >>> 
> >>> diff --git a/lib/utils/serial/fdt_serial_gaisler.c b/lib/utils/serial/fdt_serial_gaisler.c
> >>> index 9a9f9a8b7962..74988e34bcd8 100644
> >>> --- a/lib/utils/serial/fdt_serial_gaisler.c
> >>> +++ b/lib/utils/serial/fdt_serial_gaisler.c
> >>> @@ -15,7 +15,7 @@ static int serial_gaisler_init(void *fdt, int nodeoff,
> >>> 			       const struct fdt_match *match)
> >>> {
> >>> 	int rc;
> >>> -	struct platform_uart_data uart;
> >>> +	struct platform_uart_data uart = { 0 };
> >>> 
> >>> 	rc = fdt_parse_gaisler_uart_node(fdt, nodeoff, &uart);
> >>> 	if (rc)
> >>> diff --git a/lib/utils/serial/fdt_serial_shakti.c b/lib/utils/serial/fdt_serial_shakti.c
> >>> index 4f91419ef53c..0e056303dbb5 100644
> >>> --- a/lib/utils/serial/fdt_serial_shakti.c
> >>> +++ b/lib/utils/serial/fdt_serial_shakti.c
> >>> @@ -13,7 +13,7 @@ static int serial_shakti_init(void *fdt, int nodeoff,
> >>> 				const struct fdt_match *match)
> >>> {
> >>> 	int rc;
> >>> -	struct platform_uart_data uart;
> >>> +	struct platform_uart_data uart = { 0 };
> >>> 
> >>> 	rc = fdt_parse_shakti_uart_node(fdt, nodeoff, &uart);
> >>> 	if (rc)
> >>> diff --git a/lib/utils/serial/fdt_serial_sifive.c b/lib/utils/serial/fdt_serial_sifive.c
> >>> index f4c833c1573d..3ca10913eee3 100644
> >>> --- a/lib/utils/serial/fdt_serial_sifive.c
> >>> +++ b/lib/utils/serial/fdt_serial_sifive.c
> >>> @@ -15,7 +15,7 @@ static int serial_sifive_init(void *fdt, int nodeoff,
> >>> 				const struct fdt_match *match)
> >>> {
> >>> 	int rc;
> >>> -	struct platform_uart_data uart;
> >>> +	struct platform_uart_data uart = { 0 };
> >>> 
> >>> 	rc = fdt_parse_sifive_uart_node(fdt, nodeoff, &uart);
> >>> 	if (rc)
> >>> diff --git a/lib/utils/serial/fdt_serial_uart8250.c b/lib/utils/serial/fdt_serial_uart8250.c
> >>> index 544b7416de42..0b95f2d9e44e 100644
> >>> --- a/lib/utils/serial/fdt_serial_uart8250.c
> >>> +++ b/lib/utils/serial/fdt_serial_uart8250.c
> >>> @@ -15,7 +15,7 @@ static int serial_uart8250_init(void *fdt, int nodeoff,
> >>> 				const struct fdt_match *match)
> >>> {
> >>> 	int rc;
> >>> -	struct platform_uart_data uart;
> >>> +	struct platform_uart_data uart = { 0 };
> >>> 
> >>> 	rc = fdt_parse_uart8250_node(fdt, nodeoff, &uart);
> >>> 	if (rc)
> >>> diff --git a/lib/utils/serial/fdt_serial_xlnx_uartlite.c b/lib/utils/serial/fdt_serial_xlnx_uartlite.c
> >>> index 466e16e82d8c..9f04aea3673b 100644
> >>> --- a/lib/utils/serial/fdt_serial_xlnx_uartlite.c
> >>> +++ b/lib/utils/serial/fdt_serial_xlnx_uartlite.c
> >>> @@ -15,7 +15,7 @@ static int serial_xlnx_uartlite_init(void *fdt, int nodeoff,
> >>> 				const struct fdt_match *match)
> >>> {
> >>> 	int rc;
> >>> -	struct platform_uart_data uart;
> >>> +	struct platform_uart_data uart = { 0 };
> >>> 
> >>> 	rc = fdt_parse_xlnx_uartlite_node(fdt, nodeoff, &uart);
> >>> 	if (rc)
> >>> diff --git a/platform/fpga/openpiton/platform.c b/platform/fpga/openpiton/platform.c
> >>> index 7ca21236bfef..5ff7d200aba9 100644
> >>> --- a/platform/fpga/openpiton/platform.c
> >>> +++ b/platform/fpga/openpiton/platform.c
> >>> @@ -69,7 +69,7 @@ static struct aclint_mtimer_data mtimer = {
> >>> static int openpiton_early_init(bool cold_boot)
> >>> {
> >>> 	void *fdt;
> >>> -	struct platform_uart_data uart_data;
> >>> +	struct platform_uart_data uart_data = { 0 };
> >>> 	struct plic_data plic_data;
> >>> 	unsigned long aclint_freq;
> >>> 	uint64_t clint_addr;
> >>> -- 
> >>> 2.36.1
> >>> 
> >>> 
> >>> -- 
> >>> opensbi mailing list
> >>> opensbi at lists.infradead.org
> >>> http://lists.infradead.org/mailman/listinfo/opensbi
> >> 
> 


From apatel at ventanamicro.com  Tue Jul 19 05:59:07 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Tue, 19 Jul 2022 18:29:07 +0530
Subject: [PATCH v2 00/14] OpenSBI Kconfig Support
In-Reply-To: <10919e46-894c-469c-35f3-1c4fe7389db8@canonical.com>
References: <20220718145611.2211638-1-apatel@ventanamicro.com>
 <20220718184807.dac3r5bvsublyj2n@kamzik> <CAEn-LToptF6LHHZ3nUzCe6KYXBT6FHk0uyVrNfubjC8MppSfhA@mail.gmail.com>
 <CAK9=C2UE088wC3TxUFhrRUNMYQpfzvaCZAt4AGMjBcjnvktqWw@mail.gmail.com>
 <CAK9=C2U7s1FKYw+FqBbZz5mTP=Gz9mRJ-CLr4m3+iqg9PWiQ8A@mail.gmail.com>
 <44b673ad-6458-4d01-fc1d-5cb0a35946dc@canonical.com> <mvmtu7dft1m.fsf@suse.de>
 <CAK9=C2XeNpHKyRNidmpDL0G3tdMA4QZG+_wX-uW4KXTWXpmKyA@mail.gmail.com> <10919e46-894c-469c-35f3-1c4fe7389db8@canonical.com>
Message-ID: <CAK9=C2UWhVEbcfAHpbB7qRGVcjxGC5sTXQ14CqOg+Jv3N0A+tw@mail.gmail.com>

On Tue, Jul 19, 2022 at 2:57 PM Heinrich Schuchardt
<heinrich.schuchardt at canonical.com> wrote:
>
> On 7/19/22 11:05, Anup Patel wrote:
> > On Tue, Jul 19, 2022 at 1:11 PM Andreas Schwab <schwab at suse.de> wrote:
> >>
> >> On Jul 19 2022, Heinrich Schuchardt wrote:
> >>
> >>> Ubuntu provides OpenSBI as a package. So it is package maintainers not
> >>> users that are concerned. kconfig-frontends currently is in the Universe
> >>> repository (community supported). This would have to change since OpenSBI
> >>> is in the main repository (Canonical supported). Putting software based on
> >>> a a dead project into main is not preferred.
> >>
> >> kconfig-frontends is not available on openSUSE Factory either.
> >
> > Fair enough. We should use something else.
> >
> > Kconfiglib seems to be a good option except the issues pointed by Heinrich:
> > 1) Python pip is not recommended security wise
> > 2) The LICENSE of Kconfiglib needs to be compatible with BSD distros
>
> Bugs in kconfiglib are not fixed in a timely manner. See
> https://github.com/ulfalizer/Kconfiglib/issues/105
>
> The last commit was in Jan 2020. So another dead project.

Let me send a v3 with Kconfiglib tools included in OpenSBI sources
so that we are not dependent on the Kconfiglib project to actively fix
issues for us.

Regards,
Anup

>
> Best regards
>
> Heinrich
>
> >
> > The Kconfiglib LICENSE is definitely not GPL and seems compatible
> > with BSD-2-clause and MIT licenses.
> > (Refer, https://github.com/ulfalizer/Kconfiglib/blob/master/LICENSE.txt)
> >
> > To address security concerns, we can do one of the following:
> > A) Include Kconfiglib source under scripts/ directory.
> > B) Recommend that OpenSBI users, install Kconfiglib as distro
> > package instead of python-pip3 installer (e.g. python3-kconfiglib
> > package available on Ubuntu)
> >
> > IMO, the option A mentioned above has a maintenance burden and
> > if possible we should avoid it.
> >
> > Regards,
> > Anup
> >
> >
> >
> >
> >>
> >> --
> >> Andreas Schwab, SUSE Labs, schwab at suse.de
> >> GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE  1748 E4D4 88E3 0EEA B9D7
> >> "And now for something completely different."
>


From apatel at ventanamicro.com  Tue Jul 19 06:18:22 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Tue, 19 Jul 2022 18:48:22 +0530
Subject: [PATCH v3 00/15] OpenSBI Kconfig Support
Message-ID: <20220719131837.2389630-1-apatel@ventanamicro.com>

In future, we will see more drivers in OpenSBI for system reset, serial port,
power management, etc platform specific devices. We need platform specific
compile time configuration to allow users control the size of OpenSBI generic
firmware binaries by only enabling desired drivers. In other words, users
should be allowed to restrict the set of RISC-V platforms on which OpenSBI
generic firmwares built by them can run.

To address above, we add Kconfig support for OpenSBI platforms using the
Kconfiglib project which is a standalone kconfig parser.

To try OpenSBI Kconfig support with generic platform, here are few
example commands:
make PLATFORM=generic             <-- builds OpenSBI using "defconfig"
make PLATFORM=generic menuconfig  <-- launches graphical interface for changes

These patches can also be found in the kconfig_v3 branch at:
https://github.com/avpatel/opensbi.git

Changes since v2:
 - Use $(FOO) instead of $FOO to access FOO environment variable in
   Kconfig files.
 - Added a new PATCH1 to include Kconfiglib v14.1.0 sources under
   the scripts directory of OpenSBI.

Changes since v1:
 - Fix SPDX license specifier in new Kconfig files

Anup Patel (15):
  scripts: Add Kconfiglib v14.1.0 under scripts directory
  Makefile: Add initial kconfig support for each platform
  Makefile: Compile lib/utils sources separately for each platform
  lib: utils/serial: Use kconfig for enabling/disabling drivers
  lib: utils/reset: Use kconfig for enabling/disabling drivers
  lib: utils/sys: Use kconfig for enabling/disabling drivers
  lib: utils/timer: Use kconfig for enabling/disabling drivers
  lib: utils/ipi: Use kconfig for enabling/disabling drivers
  lib: utils/irqchip: Use kconfig for enabling/disabling drivers
  lib: utils/i2c: Use kconfig for enabling/disabling drivers
  lib: utils/gpio: Use kconfig for enabling/disabling drivers
  lib: utils/fdt: Use kconfig for enabling/disabling
  platform: generic: Use kconfig for enabling/disabling overrides
  platform: Remove redundant config.mk from all platforms
  docs: Update documentation for kconfig support

 Kconfig                                       |   23 +
 Makefile                                      |  104 +-
 README.md                                     |   25 +-
 docs/firmware/fw_dynamic.md                   |    2 +-
 docs/firmware/fw_jump.md                      |    4 +-
 docs/firmware/fw_payload.md                   |    4 +-
 docs/library_usage.md                         |    6 +-
 docs/platform/platform.md                     |    4 +-
 docs/platform_guide.md                        |    9 +-
 firmware/Kconfig                              |    1 +
 include/sbi_utils/fdt/fdt_domain.h            |    9 +
 include/sbi_utils/fdt/fdt_pmu.h               |   10 +
 include/sbi_utils/ipi/fdt_ipi.h               |    9 +
 include/sbi_utils/irqchip/fdt_irqchip.h       |   10 +
 include/sbi_utils/irqchip/imsic.h             |   10 +
 include/sbi_utils/reset/fdt_reset.h           |   12 +
 include/sbi_utils/serial/fdt_serial.h         |    8 +
 include/sbi_utils/timer/fdt_timer.h           |    9 +
 lib/utils/Kconfig                             |   25 +
 lib/utils/fdt/Kconfig                         |   18 +
 lib/utils/fdt/objects.mk                      |    8 +-
 lib/utils/gpio/Kconfig                        |   23 +
 lib/utils/gpio/objects.mk                     |   10 +-
 lib/utils/i2c/Kconfig                         |   23 +
 lib/utils/i2c/objects.mk                      |   10 +-
 lib/utils/ipi/Kconfig                         |   23 +
 lib/utils/ipi/objects.mk                      |   10 +-
 lib/utils/irqchip/Kconfig                     |   41 +
 lib/utils/irqchip/objects.mk                  |   22 +-
 lib/utils/libfdt/Kconfig                      |    5 +
 lib/utils/libfdt/objects.mk                   |    4 +-
 lib/utils/reset/Kconfig                       |   37 +
 lib/utils/reset/objects.mk                    |   28 +-
 lib/utils/serial/Kconfig                      |   73 +
 lib/utils/serial/objects.mk                   |   44 +-
 lib/utils/sys/Kconfig                         |   13 +
 lib/utils/sys/objects.mk                      |    4 +-
 lib/utils/timer/Kconfig                       |   23 +
 lib/utils/timer/objects.mk                    |   10 +-
 platform/andes/ae350/Kconfig                  |    8 +
 platform/andes/ae350/config.mk                |   36 -
 platform/andes/ae350/configs/defconfig        |    0
 platform/andes/ae350/objects.mk               |   29 +
 platform/fpga/ariane/Kconfig                  |   10 +
 platform/fpga/ariane/config.mk                |   36 -
 platform/fpga/ariane/configs/defconfig        |    0
 platform/fpga/ariane/objects.mk               |   35 +
 platform/fpga/openpiton/Kconfig               |   10 +
 platform/fpga/openpiton/config.mk             |   35 -
 platform/fpga/openpiton/configs/defconfig     |    0
 platform/fpga/openpiton/objects.mk            |   35 +
 platform/generic/Kconfig                      |   26 +
 platform/generic/allwinner/objects.mk         |    4 +-
 platform/generic/config.mk                    |   40 -
 platform/generic/configs/defconfig            |   29 +
 platform/generic/objects.mk                   |   33 +
 platform/generic/sifive/objects.mk            |    8 +-
 platform/kendryte/k210/Kconfig                |   10 +
 platform/kendryte/k210/config.mk              |   19 -
 platform/kendryte/k210/configs/defconfig      |    0
 platform/kendryte/k210/objects.mk             |   12 +
 platform/nuclei/ux600/Kconfig                 |   10 +
 platform/nuclei/ux600/config.mk               |   30 -
 platform/nuclei/ux600/configs/defconfig       |    0
 platform/nuclei/ux600/objects.mk              |   22 +
 platform/nuclei/ux600/platform.c              |    1 -
 platform/template/Kconfig                     |    9 +
 platform/template/config.mk                   |   77 -
 platform/template/configs/defconfig           |    0
 platform/template/objects.mk                  |   72 +
 platform/template/platform.c                  |    2 +-
 scripts/Kconfiglib/LICENSE.txt                |    5 +
 scripts/Kconfiglib/MANIFEST.in                |    2 +
 scripts/Kconfiglib/README.rst                 |  841 ++
 scripts/Kconfiglib/alldefconfig.py            |   27 +
 scripts/Kconfiglib/allmodconfig.py            |   46 +
 scripts/Kconfiglib/allnoconfig.py             |   45 +
 scripts/Kconfiglib/allyesconfig.py            |   56 +
 scripts/Kconfiglib/defconfig.py               |   43 +
 scripts/Kconfiglib/examples/Kmenuconfig       |  102 +
 .../Kconfiglib/examples/allnoconfig_walk.py   |   66 +
 .../examples/defconfig_oldconfig.py           |   39 +
 scripts/Kconfiglib/examples/dumpvars.py       |   15 +
 scripts/Kconfiglib/examples/eval_expr.py      |   24 +
 scripts/Kconfiglib/examples/find_symbol.py    |  112 +
 scripts/Kconfiglib/examples/help_grep.py      |   64 +
 scripts/Kconfiglib/examples/kconfiglib.py     |    1 +
 scripts/Kconfiglib/examples/list_undefined.py |  156 +
 .../Kconfiglib/examples/menuconfig_example.py |  341 +
 scripts/Kconfiglib/examples/merge_config.py   |  121 +
 .../Kconfiglib/examples/print_config_tree.py  |  199 +
 scripts/Kconfiglib/examples/print_sym_info.py |   54 +
 scripts/Kconfiglib/examples/print_tree.py     |   75 +
 scripts/Kconfiglib/genconfig.py               |  154 +
 scripts/Kconfiglib/guiconfig.py               | 2324 ++++++
 scripts/Kconfiglib/kconfiglib.py              | 7160 +++++++++++++++++
 scripts/Kconfiglib/listnewconfig.py           |   76 +
 scripts/Kconfiglib/makefile.patch             |   48 +
 scripts/Kconfiglib/menuconfig.py              | 3278 ++++++++
 scripts/Kconfiglib/oldconfig.py               |  246 +
 scripts/Kconfiglib/olddefconfig.py            |   28 +
 scripts/Kconfiglib/savedefconfig.py           |   49 +
 scripts/Kconfiglib/setconfig.py               |   92 +
 scripts/Kconfiglib/setup.cfg                  |    7 +
 scripts/Kconfiglib/setup.py                   |   96 +
 scripts/Kconfiglib/tests/Kappend              |   12 +
 scripts/Kconfiglib/tests/Kassignable          |  230 +
 scripts/Kconfiglib/tests/Kchoice              |  198 +
 scripts/Kconfiglib/tests/Kdefconfig_existent  |    8 +
 .../tests/Kdefconfig_existent_but_n           |   10 +
 .../Kconfiglib/tests/Kdefconfig_nonexistent   |    5 +
 scripts/Kconfiglib/tests/Kdefconfig_srctree   |    5 +
 scripts/Kconfiglib/tests/Kdepcopy             |  173 +
 scripts/Kconfiglib/tests/Kdeploop0            |    3 +
 scripts/Kconfiglib/tests/Kdeploop1            |    3 +
 scripts/Kconfiglib/tests/Kdeploop10           |   48 +
 scripts/Kconfiglib/tests/Kdeploop2            |    3 +
 scripts/Kconfiglib/tests/Kdeploop3            |    3 +
 scripts/Kconfiglib/tests/Kdeploop4            |    7 +
 scripts/Kconfiglib/tests/Kdeploop5            |    7 +
 scripts/Kconfiglib/tests/Kdeploop6            |    6 +
 scripts/Kconfiglib/tests/Kdeploop7            |   11 +
 scripts/Kconfiglib/tests/Kdeploop8            |    8 +
 scripts/Kconfiglib/tests/Kdeploop9            |    7 +
 scripts/Kconfiglib/tests/Kdirdep              |   30 +
 scripts/Kconfiglib/tests/Kescape              |    3 +
 scripts/Kconfiglib/tests/Keval                |   34 +
 scripts/Kconfiglib/tests/Kexpr_items          |   11 +
 scripts/Kconfiglib/tests/Kheader              |    5 +
 scripts/Kconfiglib/tests/Khelp                |   50 +
 scripts/Kconfiglib/tests/Kifremoval           |   99 +
 scripts/Kconfiglib/tests/Kimply               |  145 +
 scripts/Kconfiglib/tests/Kinclude_path        |   12 +
 .../Kconfiglib/tests/Kinclude_path_sourced_1  |   12 +
 .../Kconfiglib/tests/Kinclude_path_sourced_2  |   11 +
 scripts/Kconfiglib/tests/Kitemlists           |   36 +
 scripts/Kconfiglib/tests/Klocation            |   78 +
 scripts/Kconfiglib/tests/Klocation_sourced    |   26 +
 scripts/Kconfiglib/tests/Kmainmenu            |    5 +
 scripts/Kconfiglib/tests/Kmenuconfig          |   37 +
 scripts/Kconfiglib/tests/Kmisc                |   82 +
 scripts/Kconfiglib/tests/Kmissingrsource      |    1 +
 scripts/Kconfiglib/tests/Kmissingsource       |    1 +
 scripts/Kconfiglib/tests/Korder               |   35 +
 scripts/Kconfiglib/tests/Kpreprocess          |  151 +
 scripts/Kconfiglib/tests/Krange               |  133 +
 scripts/Kconfiglib/tests/Krecursive1          |    1 +
 scripts/Kconfiglib/tests/Krecursive2          |    1 +
 scripts/Kconfiglib/tests/Kreferenced          |   63 +
 scripts/Kconfiglib/tests/Krelation            |   36 +
 scripts/Kconfiglib/tests/Krepr                |   64 +
 scripts/Kconfiglib/tests/Kstr                 |  293 +
 scripts/Kconfiglib/tests/Kundef               |   23 +
 scripts/Kconfiglib/tests/Kuserfunctions       |   14 +
 scripts/Kconfiglib/tests/Kvisibility          |  342 +
 scripts/Kconfiglib/tests/config_indented      |    3 +
 scripts/Kconfiglib/tests/config_set_bool      |    1 +
 scripts/Kconfiglib/tests/config_set_string    |    1 +
 scripts/Kconfiglib/tests/defconfig_1          |    0
 scripts/Kconfiglib/tests/defconfig_2          |    0
 scripts/Kconfiglib/tests/empty                |    0
 scripts/Kconfiglib/tests/kconfigfunctions.py  |   22 +
 scripts/Kconfiglib/tests/reltest              |   51 +
 .../Kconfiglib/tests/sub/Kconfig_symlink_2    |    1 +
 .../Kconfiglib/tests/sub/Kconfig_symlink_3    |    2 +
 .../Kconfiglib/tests/sub/Klocation_grsourced1 |    1 +
 .../Kconfiglib/tests/sub/Klocation_grsourced2 |    1 +
 .../Kconfiglib/tests/sub/Klocation_gsourced1  |    1 +
 .../Kconfiglib/tests/sub/Klocation_gsourced2  |    1 +
 .../Kconfiglib/tests/sub/Klocation_rsourced   |    2 +
 scripts/Kconfiglib/tests/sub/defconfig_in_sub |    0
 .../tests/sub/sub/Kconfig_symlink_1           |    2 +
 scripts/Kconfiglib/tests/symlink              |    1 +
 scripts/Kconfiglib/testsuite.py               | 3203 ++++++++
 scripts/create-binary-archive.sh              |    2 +-
 175 files changed, 22816 insertions(+), 403 deletions(-)
 create mode 100644 Kconfig
 create mode 100644 firmware/Kconfig
 create mode 100644 lib/utils/Kconfig
 create mode 100644 lib/utils/fdt/Kconfig
 create mode 100644 lib/utils/gpio/Kconfig
 create mode 100644 lib/utils/i2c/Kconfig
 create mode 100644 lib/utils/ipi/Kconfig
 create mode 100644 lib/utils/irqchip/Kconfig
 create mode 100644 lib/utils/libfdt/Kconfig
 create mode 100644 lib/utils/reset/Kconfig
 create mode 100644 lib/utils/serial/Kconfig
 create mode 100644 lib/utils/sys/Kconfig
 create mode 100644 lib/utils/timer/Kconfig
 create mode 100644 platform/andes/ae350/Kconfig
 delete mode 100644 platform/andes/ae350/config.mk
 create mode 100644 platform/andes/ae350/configs/defconfig
 create mode 100644 platform/fpga/ariane/Kconfig
 delete mode 100644 platform/fpga/ariane/config.mk
 create mode 100644 platform/fpga/ariane/configs/defconfig
 create mode 100644 platform/fpga/openpiton/Kconfig
 delete mode 100644 platform/fpga/openpiton/config.mk
 create mode 100644 platform/fpga/openpiton/configs/defconfig
 create mode 100644 platform/generic/Kconfig
 delete mode 100644 platform/generic/config.mk
 create mode 100644 platform/generic/configs/defconfig
 create mode 100644 platform/kendryte/k210/Kconfig
 delete mode 100644 platform/kendryte/k210/config.mk
 create mode 100644 platform/kendryte/k210/configs/defconfig
 create mode 100644 platform/nuclei/ux600/Kconfig
 delete mode 100644 platform/nuclei/ux600/config.mk
 create mode 100644 platform/nuclei/ux600/configs/defconfig
 create mode 100644 platform/template/Kconfig
 delete mode 100644 platform/template/config.mk
 create mode 100644 platform/template/configs/defconfig
 create mode 100644 scripts/Kconfiglib/LICENSE.txt
 create mode 100644 scripts/Kconfiglib/MANIFEST.in
 create mode 100644 scripts/Kconfiglib/README.rst
 create mode 100755 scripts/Kconfiglib/alldefconfig.py
 create mode 100755 scripts/Kconfiglib/allmodconfig.py
 create mode 100755 scripts/Kconfiglib/allnoconfig.py
 create mode 100755 scripts/Kconfiglib/allyesconfig.py
 create mode 100755 scripts/Kconfiglib/defconfig.py
 create mode 100644 scripts/Kconfiglib/examples/Kmenuconfig
 create mode 100644 scripts/Kconfiglib/examples/allnoconfig_walk.py
 create mode 100644 scripts/Kconfiglib/examples/defconfig_oldconfig.py
 create mode 100644 scripts/Kconfiglib/examples/dumpvars.py
 create mode 100644 scripts/Kconfiglib/examples/eval_expr.py
 create mode 100644 scripts/Kconfiglib/examples/find_symbol.py
 create mode 100644 scripts/Kconfiglib/examples/help_grep.py
 create mode 120000 scripts/Kconfiglib/examples/kconfiglib.py
 create mode 100644 scripts/Kconfiglib/examples/list_undefined.py
 create mode 100755 scripts/Kconfiglib/examples/menuconfig_example.py
 create mode 100755 scripts/Kconfiglib/examples/merge_config.py
 create mode 100644 scripts/Kconfiglib/examples/print_config_tree.py
 create mode 100644 scripts/Kconfiglib/examples/print_sym_info.py
 create mode 100644 scripts/Kconfiglib/examples/print_tree.py
 create mode 100755 scripts/Kconfiglib/genconfig.py
 create mode 100755 scripts/Kconfiglib/guiconfig.py
 create mode 100644 scripts/Kconfiglib/kconfiglib.py
 create mode 100755 scripts/Kconfiglib/listnewconfig.py
 create mode 100644 scripts/Kconfiglib/makefile.patch
 create mode 100755 scripts/Kconfiglib/menuconfig.py
 create mode 100755 scripts/Kconfiglib/oldconfig.py
 create mode 100755 scripts/Kconfiglib/olddefconfig.py
 create mode 100755 scripts/Kconfiglib/savedefconfig.py
 create mode 100755 scripts/Kconfiglib/setconfig.py
 create mode 100644 scripts/Kconfiglib/setup.cfg
 create mode 100644 scripts/Kconfiglib/setup.py
 create mode 100644 scripts/Kconfiglib/tests/Kappend
 create mode 100644 scripts/Kconfiglib/tests/Kassignable
 create mode 100644 scripts/Kconfiglib/tests/Kchoice
 create mode 100644 scripts/Kconfiglib/tests/Kdefconfig_existent
 create mode 100644 scripts/Kconfiglib/tests/Kdefconfig_existent_but_n
 create mode 100644 scripts/Kconfiglib/tests/Kdefconfig_nonexistent
 create mode 100644 scripts/Kconfiglib/tests/Kdefconfig_srctree
 create mode 100644 scripts/Kconfiglib/tests/Kdepcopy
 create mode 100644 scripts/Kconfiglib/tests/Kdeploop0
 create mode 100644 scripts/Kconfiglib/tests/Kdeploop1
 create mode 100644 scripts/Kconfiglib/tests/Kdeploop10
 create mode 100644 scripts/Kconfiglib/tests/Kdeploop2
 create mode 100644 scripts/Kconfiglib/tests/Kdeploop3
 create mode 100644 scripts/Kconfiglib/tests/Kdeploop4
 create mode 100644 scripts/Kconfiglib/tests/Kdeploop5
 create mode 100644 scripts/Kconfiglib/tests/Kdeploop6
 create mode 100644 scripts/Kconfiglib/tests/Kdeploop7
 create mode 100644 scripts/Kconfiglib/tests/Kdeploop8
 create mode 100644 scripts/Kconfiglib/tests/Kdeploop9
 create mode 100644 scripts/Kconfiglib/tests/Kdirdep
 create mode 100644 scripts/Kconfiglib/tests/Kescape
 create mode 100644 scripts/Kconfiglib/tests/Keval
 create mode 100644 scripts/Kconfiglib/tests/Kexpr_items
 create mode 100644 scripts/Kconfiglib/tests/Kheader
 create mode 100644 scripts/Kconfiglib/tests/Khelp
 create mode 100644 scripts/Kconfiglib/tests/Kifremoval
 create mode 100644 scripts/Kconfiglib/tests/Kimply
 create mode 100644 scripts/Kconfiglib/tests/Kinclude_path
 create mode 100644 scripts/Kconfiglib/tests/Kinclude_path_sourced_1
 create mode 100644 scripts/Kconfiglib/tests/Kinclude_path_sourced_2
 create mode 100644 scripts/Kconfiglib/tests/Kitemlists
 create mode 100644 scripts/Kconfiglib/tests/Klocation
 create mode 100644 scripts/Kconfiglib/tests/Klocation_sourced
 create mode 100644 scripts/Kconfiglib/tests/Kmainmenu
 create mode 100644 scripts/Kconfiglib/tests/Kmenuconfig
 create mode 100644 scripts/Kconfiglib/tests/Kmisc
 create mode 100644 scripts/Kconfiglib/tests/Kmissingrsource
 create mode 100644 scripts/Kconfiglib/tests/Kmissingsource
 create mode 100644 scripts/Kconfiglib/tests/Korder
 create mode 100644 scripts/Kconfiglib/tests/Kpreprocess
 create mode 100644 scripts/Kconfiglib/tests/Krange
 create mode 100644 scripts/Kconfiglib/tests/Krecursive1
 create mode 100644 scripts/Kconfiglib/tests/Krecursive2
 create mode 100644 scripts/Kconfiglib/tests/Kreferenced
 create mode 100644 scripts/Kconfiglib/tests/Krelation
 create mode 100644 scripts/Kconfiglib/tests/Krepr
 create mode 100644 scripts/Kconfiglib/tests/Kstr
 create mode 100644 scripts/Kconfiglib/tests/Kundef
 create mode 100644 scripts/Kconfiglib/tests/Kuserfunctions
 create mode 100644 scripts/Kconfiglib/tests/Kvisibility
 create mode 100644 scripts/Kconfiglib/tests/config_indented
 create mode 100644 scripts/Kconfiglib/tests/config_set_bool
 create mode 100644 scripts/Kconfiglib/tests/config_set_string
 create mode 100644 scripts/Kconfiglib/tests/defconfig_1
 create mode 100644 scripts/Kconfiglib/tests/defconfig_2
 create mode 100644 scripts/Kconfiglib/tests/empty
 create mode 100644 scripts/Kconfiglib/tests/kconfigfunctions.py
 create mode 100755 scripts/Kconfiglib/tests/reltest
 create mode 100644 scripts/Kconfiglib/tests/sub/Kconfig_symlink_2
 create mode 100644 scripts/Kconfiglib/tests/sub/Kconfig_symlink_3
 create mode 100644 scripts/Kconfiglib/tests/sub/Klocation_grsourced1
 create mode 100644 scripts/Kconfiglib/tests/sub/Klocation_grsourced2
 create mode 100644 scripts/Kconfiglib/tests/sub/Klocation_gsourced1
 create mode 100644 scripts/Kconfiglib/tests/sub/Klocation_gsourced2
 create mode 100644 scripts/Kconfiglib/tests/sub/Klocation_rsourced
 create mode 100644 scripts/Kconfiglib/tests/sub/defconfig_in_sub
 create mode 100644 scripts/Kconfiglib/tests/sub/sub/Kconfig_symlink_1
 create mode 120000 scripts/Kconfiglib/tests/symlink
 create mode 100644 scripts/Kconfiglib/testsuite.py

-- 
2.34.1



From apatel at ventanamicro.com  Tue Jul 19 06:18:25 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Tue, 19 Jul 2022 18:48:25 +0530
Subject: [PATCH v3 03/15] Makefile: Compile lib/utils sources separately for each platform
In-Reply-To: <20220719131837.2389630-1-apatel@ventanamicro.com>
References: <20220719131837.2389630-1-apatel@ventanamicro.com>
Message-ID: <20220719131837.2389630-4-apatel@ventanamicro.com>

Currently, if same build directory is used to compile two different
platforms then lib/utils objects are shared for these platforms.

We will be having platform specific configs to enable/disable drivers
in lib/utils and select compile time options for lib/utils sources.
This means lib/utils sources will now be compiled in a platform
specific way.

To tackle above, we update top-level Makefile as follows:
1) Don't create libsbiutils.a anymore because this can't be shared
   between platforms.
2) Compile lib/utils sources separately for each platform.
3) Add comments showing which make rules are for lib/sbi, lib/utils,
   firmware, and platform sources.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 Makefile                         | 48 ++++++++++++++++----------------
 docs/library_usage.md            |  6 +---
 scripts/create-binary-archive.sh |  2 +-
 3 files changed, 26 insertions(+), 30 deletions(-)

diff --git a/Makefile b/Makefile
index c13495c..8c9e8df 100644
--- a/Makefile
+++ b/Makefile
@@ -242,8 +242,8 @@ include $(firmware-object-mks)
 
 # Setup list of objects
 libsbi-objs-path-y=$(foreach obj,$(libsbi-objs-y),$(build_dir)/lib/sbi/$(obj))
-libsbiutils-objs-path-y=$(foreach obj,$(libsbiutils-objs-y),$(build_dir)/lib/utils/$(obj))
 ifdef PLATFORM
+libsbiutils-objs-path-y=$(foreach obj,$(libsbiutils-objs-y),$(platform_build_dir)/lib/utils/$(obj))
 platform-objs-path-y=$(foreach obj,$(platform-objs-y),$(platform_build_dir)/$(obj))
 firmware-bins-path-y=$(foreach bin,$(firmware-bins-y),$(platform_build_dir)/firmware/$(bin))
 endif
@@ -460,7 +460,6 @@ compile_gen_dep = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
 	     echo "$(1:.dep=$(2)): $(3)" >> $(1)
 
 targets-y  = $(build_dir)/lib/libsbi.a
-targets-y  += $(build_dir)/lib/libsbiutils.a
 ifdef PLATFORM
 targets-y += $(platform_build_dir)/lib/libplatsbi.a
 endif
@@ -473,12 +472,10 @@ all: $(targets-y)
 # Preserve all intermediate files
 .SECONDARY:
 
+# Rules for lib/sbi sources
 $(build_dir)/lib/libsbi.a: $(libsbi-objs-path-y)
 	$(call compile_ar,$@,$^)
 
-$(build_dir)/lib/libsbiutils.a: $(libsbi-objs-path-y) $(libsbiutils-objs-path-y)
-	$(call compile_ar,$@,$^)
-
 $(platform_build_dir)/lib/libplatsbi.a: $(libsbi-objs-path-y) $(libsbiutils-objs-path-y) $(platform-objs-path-y)
 	$(call compile_ar,$@,$^)
 
@@ -502,22 +499,14 @@ $(build_dir)/%.dep: $(src_dir)/%.S
 $(build_dir)/%.o: $(src_dir)/%.S
 	$(call compile_as,$@,$<)
 
-$(build_dir)/%.dep: $(src_dir)/%.carray
+# Rules for platform sources
+$(platform_build_dir)/%.dep: $(platform_src_dir)/%.carray
 	$(call compile_gen_dep,$@,.c,$< $(KCONFIG_CONFIG))
 	$(call compile_gen_dep,$@,.o,$(@:.dep=.c))
 
-$(build_dir)/%.c: $(src_dir)/%.carray
+$(platform_build_dir)/%.c: $(platform_src_dir)/%.carray
 	$(call compile_carray,$@,$<)
 
-$(platform_build_dir)/%.bin: $(platform_build_dir)/%.elf
-	$(call compile_objcopy,$@,$<)
-
-$(platform_build_dir)/%.elf: $(platform_build_dir)/%.o $(platform_build_dir)/%.elf.ld $(platform_build_dir)/lib/libplatsbi.a
-	$(call compile_elf,$@,$@.ld,$< $(platform_build_dir)/lib/libplatsbi.a)
-
-$(platform_build_dir)/%.ld: $(src_dir)/%.ldS
-	$(call compile_cpp,$@,$<)
-
 $(platform_build_dir)/%.dep: $(platform_src_dir)/%.c
 	$(call compile_cc_dep,$@,$<)
 
@@ -541,6 +530,23 @@ $(platform_build_dir)/%.c: $(platform_build_dir)/%.dtb
 $(platform_build_dir)/%.dtb: $(platform_src_dir)/%.dts
 	$(call compile_dts,$@,$<)
 
+# Rules for lib/utils and firmware sources
+$(platform_build_dir)/%.bin: $(platform_build_dir)/%.elf
+	$(call compile_objcopy,$@,$<)
+
+$(platform_build_dir)/%.elf: $(platform_build_dir)/%.o $(platform_build_dir)/%.elf.ld $(platform_build_dir)/lib/libplatsbi.a
+	$(call compile_elf,$@,$@.ld,$< $(platform_build_dir)/lib/libplatsbi.a)
+
+$(platform_build_dir)/%.ld: $(src_dir)/%.ldS
+	$(call compile_cpp,$@,$<)
+
+$(platform_build_dir)/%.dep: $(src_dir)/%.carray
+	$(call compile_gen_dep,$@,.c,$< $(KCONFIG_CONFIG))
+	$(call compile_gen_dep,$@,.o,$(@:.dep=.c))
+
+$(platform_build_dir)/%.c: $(src_dir)/%.carray
+	$(call compile_carray,$@,$<)
+
 $(platform_build_dir)/%.dep: $(src_dir)/%.c
 	$(call compile_cc_dep,$@,$<)
 
@@ -594,7 +600,6 @@ endif
 endif
 
 install_targets-y  = install_libsbi
-install_targets-y  += install_libsbiutils
 ifdef PLATFORM
 install_targets-y += install_libplatsbi
 install_targets-y += install_firmwares
@@ -609,17 +614,12 @@ install_libsbi: $(build_dir)/lib/libsbi.a
 	$(call inst_header_dir,$(install_root_dir)/$(install_include_path),$(include_dir)/sbi)
 	$(call inst_file,$(install_root_dir)/$(install_lib_path)/libsbi.a,$(build_dir)/lib/libsbi.a)
 
-.PHONY: install_libsbiutils
-install_libsbiutils: $(build_dir)/lib/libsbiutils.a
-	$(call inst_header_dir,$(install_root_dir)/$(install_include_path),$(include_dir)/sbi_utils)
-	$(call inst_file,$(install_root_dir)/$(install_lib_path)/libsbiutils.a,$(build_dir)/lib/libsbiutils.a)
-
 .PHONY: install_libplatsbi
-install_libplatsbi: $(platform_build_dir)/lib/libplatsbi.a $(build_dir)/lib/libsbi.a $(build_dir)/lib/libsbiutils.a
+install_libplatsbi: $(platform_build_dir)/lib/libplatsbi.a $(build_dir)/lib/libsbi.a
 	$(call inst_file,$(install_root_dir)/$(install_lib_path)/opensbi/$(platform_subdir)/lib/libplatsbi.a,$(platform_build_dir)/lib/libplatsbi.a)
 
 .PHONY: install_firmwares
-install_firmwares: $(platform_build_dir)/lib/libplatsbi.a $(build_dir)/lib/libsbi.a $(build_dir)/lib/libsbiutils.a $(firmware-bins-path-y)
+install_firmwares: $(platform_build_dir)/lib/libplatsbi.a $(build_dir)/lib/libsbi.a $(firmware-bins-path-y)
 	$(call inst_file_list,$(install_root_dir),$(build_dir),$(install_firmware_path)/$(platform_subdir)/firmware,$(firmware-elfs-path-y))
 	$(call inst_file_list,$(install_root_dir),$(build_dir),$(install_firmware_path)/$(platform_subdir)/firmware,$(firmware-bins-path-y))
 
diff --git a/docs/library_usage.md b/docs/library_usage.md
index ff99801..d5d2ba9 100644
--- a/docs/library_usage.md
+++ b/docs/library_usage.md
@@ -8,11 +8,7 @@ OpenSBI provides two types of static libraries:
    hooks for the execution of this interface must be provided by the firmware or
    bootloader linking with this library. This library is installed as
    *<install_directory>/lib/libsbi.a*
-2. *libsbiutils.a* - A static library that will contain all common code required
-   by any platform supported in OpenSBI. It will be built by default and included
-   in libplatsbi.a. This library is installed as
-   *<install_directory>/lib/libsbiutils.a*.
-3. *libplatsbi.a* - An example platform-specific static library integrating
+2. *libplatsbi.a* - An example platform-specific static library integrating
    *libsbi.a* with platform-specific hooks. This library is available only for
    the platforms supported by OpenSBI. This library is installed as
    *<install_directory>/platform/<platform_subdir>/lib/libplatsbi.a*
diff --git a/scripts/create-binary-archive.sh b/scripts/create-binary-archive.sh
index 43c5452..261a45a 100755
--- a/scripts/create-binary-archive.sh
+++ b/scripts/create-binary-archive.sh
@@ -118,7 +118,7 @@ build_opensbi() {
 	# Build and install generic library
 	echo "Build and install generic library XLEN=${BUILD_RISCV_XLEN}"
 	echo ""
-	make -C "${BUILD_OPENSBI_SOURCE_PATH}" O="${BUILD_OUTPUT_PATH}/${BUILD_NAME}" I="${BUILD_OUTPUT_PATH}/${BUILD_ARCHIVE_NAME}" PLATFORM_RISCV_XLEN="${BUILD_RISCV_XLEN}" install_libsbi install_libsbiutils -j "${BUILD_NUM_THREADS}"
+	make -C "${BUILD_OPENSBI_SOURCE_PATH}" O="${BUILD_OUTPUT_PATH}/${BUILD_NAME}" I="${BUILD_OUTPUT_PATH}/${BUILD_ARCHIVE_NAME}" PLATFORM_RISCV_XLEN="${BUILD_RISCV_XLEN}" install_libsbi -j "${BUILD_NUM_THREADS}"
 	echo ""
 
 	# Build and install relevant platforms
-- 
2.34.1



From apatel at ventanamicro.com  Tue Jul 19 06:18:24 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Tue, 19 Jul 2022 18:48:24 +0530
Subject: [PATCH v3 02/15] Makefile: Add initial kconfig support for each platform
In-Reply-To: <20220719131837.2389630-1-apatel@ventanamicro.com>
References: <20220719131837.2389630-1-apatel@ventanamicro.com>
Message-ID: <20220719131837.2389630-3-apatel@ventanamicro.com>

We extend the top-level makefile to allow kconfig based configuration
for each platform where each platform has it's own set of configs with
"defconfig" being the default config.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 Kconfig                                   | 23 ++++++++++
 Makefile                                  | 56 +++++++++++++++++++++--
 firmware/Kconfig                          |  1 +
 lib/utils/Kconfig                         |  1 +
 platform/andes/ae350/Kconfig              |  5 ++
 platform/andes/ae350/configs/defconfig    |  0
 platform/fpga/ariane/Kconfig              |  5 ++
 platform/fpga/ariane/configs/defconfig    |  0
 platform/fpga/openpiton/Kconfig           |  5 ++
 platform/fpga/openpiton/configs/defconfig |  0
 platform/generic/Kconfig                  |  5 ++
 platform/generic/configs/defconfig        |  0
 platform/kendryte/k210/Kconfig            |  5 ++
 platform/kendryte/k210/configs/defconfig  |  0
 platform/nuclei/ux600/Kconfig             |  5 ++
 platform/nuclei/ux600/configs/defconfig   |  0
 platform/template/Kconfig                 |  5 ++
 platform/template/configs/defconfig       |  0
 18 files changed, 113 insertions(+), 3 deletions(-)
 create mode 100644 Kconfig
 create mode 100644 firmware/Kconfig
 create mode 100644 lib/utils/Kconfig
 create mode 100644 platform/andes/ae350/Kconfig
 create mode 100644 platform/andes/ae350/configs/defconfig
 create mode 100644 platform/fpga/ariane/Kconfig
 create mode 100644 platform/fpga/ariane/configs/defconfig
 create mode 100644 platform/fpga/openpiton/Kconfig
 create mode 100644 platform/fpga/openpiton/configs/defconfig
 create mode 100644 platform/generic/Kconfig
 create mode 100644 platform/generic/configs/defconfig
 create mode 100644 platform/kendryte/k210/Kconfig
 create mode 100644 platform/kendryte/k210/configs/defconfig
 create mode 100644 platform/nuclei/ux600/Kconfig
 create mode 100644 platform/nuclei/ux600/configs/defconfig
 create mode 100644 platform/template/Kconfig
 create mode 100644 platform/template/configs/defconfig

diff --git a/Kconfig b/Kconfig
new file mode 100644
index 0000000..b3213a9
--- /dev/null
+++ b/Kconfig
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+mainmenu "OpenSBI $(OPENSBI_PLATFORM) Configuration"
+
+config OPENSBI_SRC_DIR
+	string
+	option env="OPENSBI_SRC_DIR"
+
+config OPENSBI_PLATFORM
+	string
+	option env="OPENSBI_PLATFORM"
+
+config OPENSBI_PLATFORM_SRC_DIR
+	string
+	option env="OPENSBI_PLATFORM_SRC_DIR"
+
+menu "Platform Options"
+source "$(OPENSBI_PLATFORM_SRC_DIR)/Kconfig"
+endmenu
+
+source "$(OPENSBI_SRC_DIR)/lib/utils/Kconfig"
+
+source "$(OPENSBI_SRC_DIR)/firmware/Kconfig"
diff --git a/Makefile b/Makefile
index f619ef7..c13495c 100644
--- a/Makefile
+++ b/Makefile
@@ -52,6 +52,9 @@ ifdef PLATFORM_DIR
 else
  platform_parent_dir=$(src_dir)/platform
 endif
+ifndef PLATFORM_DEFCONFIG
+PLATFORM_DEFCONFIG=defconfig
+endif
 
 # Check if verbosity is ON for build process
 CMD_PREFIX_DEFAULT := @
@@ -70,6 +73,20 @@ export libsbi_dir=$(CURDIR)/lib/sbi
 export libsbiutils_dir=$(CURDIR)/lib/utils
 export firmware_dir=$(CURDIR)/firmware
 
+# Setup variables for kconfig
+ifdef PLATFORM
+export PYTHONDONTWRITEBYTECODE=1.
+export KCONFIG_DIR=$(platform_build_dir)/kconfig
+export KCONFIG_AUTOLIST=$(KCONFIG_DIR)/auto.list
+export KCONFIG_AUTOHEADER=$(KCONFIG_DIR)/autoconf.h
+export KCONFIG_AUTOCMD=$(KCONFIG_DIR)/auto.conf.cmd
+export KCONFIG_CONFIG=$(KCONFIG_DIR)/.config
+# Additional exports for include paths in Kconfig files
+export OPENSBI_SRC_DIR=$(src_dir)
+export OPENSBI_PLATFORM=$(PLATFORM)
+export OPENSBI_PLATFORM_SRC_DIR=$(platform_src_dir)
+endif
+
 # Find library version
 OPENSBI_VERSION_MAJOR=`grep "define OPENSBI_VERSION_MAJOR" $(include_dir)/sbi/sbi_version.h | sed 's/.*MAJOR.*\([0-9][0-9]*\)/\1/'`
 OPENSBI_VERSION_MINOR=`grep "define OPENSBI_VERSION_MINOR" $(include_dir)/sbi/sbi_version.h | sed 's/.*MINOR.*\([0-9][0-9]*\)/\1/'`
@@ -183,8 +200,35 @@ libsbi-object-mks=$(shell if [ -d $(libsbi_dir) ]; then find $(libsbi_dir) -inam
 libsbiutils-object-mks=$(shell if [ -d $(libsbiutils_dir) ]; then find $(libsbiutils_dir) -iname "objects.mk" | sort -r; fi)
 firmware-object-mks=$(shell if [ -d $(firmware_dir) ]; then find $(firmware_dir) -iname "objects.mk" | sort -r; fi)
 
-# Include platform specifig config.mk
+# The "make all" rule should always be first rule
+.PHONY: all
+all:
+
+# Include platform specific .config and config.mk
 ifdef PLATFORM
+.PHONY: menuconfig
+menuconfig: $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
+	$(CMD_PREFIX)mkdir -p $(KCONFIG_DIR)
+	$(CMD_PREFIX)$(src_dir)/scripts/Kconfiglib/menuconfig.py $(src_dir)/Kconfig
+
+.PHONY: savedefconfig
+savedefconfig: $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
+	$(CMD_PREFIX)mkdir -p $(KCONFIG_DIR)
+	$(CMD_PREFIX)$(src_dir)/scripts/Kconfiglib/savedefconfig.py --kconfig $(src_dir)/Kconfig --out $(KCONFIG_DIR)/defconfig
+
+$(KCONFIG_CONFIG): $(platform_src_dir)/configs/$(PLATFORM_DEFCONFIG) $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
+	$(CMD_PREFIX)mkdir -p $(KCONFIG_DIR)
+	$(CMD_PREFIX)$(src_dir)/scripts/Kconfiglib/defconfig.py --kconfig $(src_dir)/Kconfig $(platform_src_dir)/configs/$(PLATFORM_DEFCONFIG)
+	$(CMD_PREFIX)$(src_dir)/scripts/Kconfiglib/genconfig.py --header-path $(KCONFIG_AUTOHEADER) --sync-deps $(KCONFIG_DIR) --file-list $(KCONFIG_AUTOLIST) $(src_dir)/Kconfig
+
+$(KCONFIG_AUTOCMD): $(KCONFIG_CONFIG)
+	$(CMD_PREFIX)mkdir -p $(KCONFIG_DIR)
+	$(CMD_PREFIX)echo -n "$(KCONFIG_CONFIG): " > $(KCONFIG_AUTOCMD)
+	$(CMD_PREFIX)cat $(KCONFIG_AUTOLIST) | tr '\n' ' ' >> $(KCONFIG_AUTOCMD)
+
+include $(KCONFIG_CONFIG)
+include $(KCONFIG_AUTOCMD)
+
 include $(platform_src_dir)/config.mk
 endif
 
@@ -280,6 +324,9 @@ ifeq ($(BUILD_INFO),y)
 GENFLAGS	+=	-DOPENSBI_BUILD_TIME_STAMP="\"$(OPENSBI_BUILD_TIME_STAMP)\""
 GENFLAGS	+=	-DOPENSBI_BUILD_COMPILER_VERSION="\"$(OPENSBI_BUILD_COMPILER_VERSION)\""
 endif
+ifdef PLATFORM
+GENFLAGS	+=	-include $(KCONFIG_AUTOHEADER)
+endif
 GENFLAGS	+=	$(libsbiutils-genflags-y)
 GENFLAGS	+=	$(platform-genflags-y)
 GENFLAGS	+=	$(firmware-genflags-y)
@@ -419,7 +466,7 @@ targets-y += $(platform_build_dir)/lib/libplatsbi.a
 endif
 targets-y += $(firmware-bins-path-y)
 
-# Default rule "make" should always be first rule
+# The default "make all" rule
 .PHONY: all
 all: $(targets-y)
 
@@ -456,7 +503,7 @@ $(build_dir)/%.o: $(src_dir)/%.S
 	$(call compile_as,$@,$<)
 
 $(build_dir)/%.dep: $(src_dir)/%.carray
-	$(call compile_gen_dep,$@,.c,$<)
+	$(call compile_gen_dep,$@,.c,$< $(KCONFIG_CONFIG))
 	$(call compile_gen_dep,$@,.o,$(@:.dep=.c))
 
 $(build_dir)/%.c: $(src_dir)/%.carray
@@ -523,6 +570,9 @@ docs: $(build_dir)/docs/latex/refman.pdf
 # They should not be included for any "xxxconfig" or "xxxclean" rule
 all-deps-1 = $(if $(findstring config,$(MAKECMDGOALS)),,$(deps-y))
 all-deps-2 = $(if $(findstring clean,$(MAKECMDGOALS)),,$(all-deps-1))
+ifdef PLATFORM
+$(all-deps-2): | $(KCONFIG_CONFIG)
+endif
 -include $(all-deps-2)
 
 # Include external dependency of firmwares after default Makefile rules
diff --git a/firmware/Kconfig b/firmware/Kconfig
new file mode 100644
index 0000000..d6e0506
--- /dev/null
+++ b/firmware/Kconfig
@@ -0,0 +1 @@
+# SPDX-License-Identifier: BSD-2-Clause
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
new file mode 100644
index 0000000..d6e0506
--- /dev/null
+++ b/lib/utils/Kconfig
@@ -0,0 +1 @@
+# SPDX-License-Identifier: BSD-2-Clause
diff --git a/platform/andes/ae350/Kconfig b/platform/andes/ae350/Kconfig
new file mode 100644
index 0000000..4b73c76
--- /dev/null
+++ b/platform/andes/ae350/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+config PLATFORM_ANDES_AE350
+	bool
+	default y
diff --git a/platform/andes/ae350/configs/defconfig b/platform/andes/ae350/configs/defconfig
new file mode 100644
index 0000000..e69de29
diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
new file mode 100644
index 0000000..70d2004
--- /dev/null
+++ b/platform/fpga/ariane/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+config PLATFORM_ARIANE_FPGA
+	bool
+	default y
diff --git a/platform/fpga/ariane/configs/defconfig b/platform/fpga/ariane/configs/defconfig
new file mode 100644
index 0000000..e69de29
diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
new file mode 100644
index 0000000..81e4b76
--- /dev/null
+++ b/platform/fpga/openpiton/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+config PLATFORM_OPENPITON_FPGA
+	bool
+	default y
diff --git a/platform/fpga/openpiton/configs/defconfig b/platform/fpga/openpiton/configs/defconfig
new file mode 100644
index 0000000..e69de29
diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
new file mode 100644
index 0000000..3eab282
--- /dev/null
+++ b/platform/generic/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+config PLATFORM_GENERIC
+	bool
+	default y
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
new file mode 100644
index 0000000..e69de29
diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
new file mode 100644
index 0000000..889f16b
--- /dev/null
+++ b/platform/kendryte/k210/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+config PLATFORM_KENDRYTE_K210
+	bool
+	default y
diff --git a/platform/kendryte/k210/configs/defconfig b/platform/kendryte/k210/configs/defconfig
new file mode 100644
index 0000000..e69de29
diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
new file mode 100644
index 0000000..c368ef4
--- /dev/null
+++ b/platform/nuclei/ux600/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+config PLATFORM_NUCLEI_UX600
+	bool
+	default y
diff --git a/platform/nuclei/ux600/configs/defconfig b/platform/nuclei/ux600/configs/defconfig
new file mode 100644
index 0000000..e69de29
diff --git a/platform/template/Kconfig b/platform/template/Kconfig
new file mode 100644
index 0000000..b9c4350
--- /dev/null
+++ b/platform/template/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+config PLATFORM_TEMPLATE
+	bool
+	default y
diff --git a/platform/template/configs/defconfig b/platform/template/configs/defconfig
new file mode 100644
index 0000000..e69de29
-- 
2.34.1



From apatel at ventanamicro.com  Tue Jul 19 06:18:26 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Tue, 19 Jul 2022 18:48:26 +0530
Subject: [PATCH v3 04/15] lib: utils/serial: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220719131837.2389630-1-apatel@ventanamicro.com>
References: <20220719131837.2389630-1-apatel@ventanamicro.com>
Message-ID: <20220719131837.2389630-5-apatel@ventanamicro.com>

We update serial drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate serial
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 include/sbi_utils/serial/fdt_serial.h |  8 +++
 lib/utils/Kconfig                     |  6 +++
 lib/utils/serial/Kconfig              | 71 +++++++++++++++++++++++++++
 lib/utils/serial/objects.mk           | 44 ++++++++---------
 platform/andes/ae350/Kconfig          |  1 +
 platform/fpga/ariane/Kconfig          |  1 +
 platform/fpga/openpiton/Kconfig       |  1 +
 platform/generic/configs/defconfig    |  8 +++
 platform/kendryte/k210/Kconfig        |  1 +
 platform/nuclei/ux600/Kconfig         |  1 +
 platform/template/Kconfig             |  1 +
 11 files changed, 121 insertions(+), 22 deletions(-)
 create mode 100644 lib/utils/serial/Kconfig

diff --git a/include/sbi_utils/serial/fdt_serial.h b/include/sbi_utils/serial/fdt_serial.h
index 6451c23..daa2e4f 100644
--- a/include/sbi_utils/serial/fdt_serial.h
+++ b/include/sbi_utils/serial/fdt_serial.h
@@ -12,6 +12,8 @@
 
 #include <sbi/sbi_types.h>
 
+#ifdef CONFIG_FDT_SERIAL
+
 struct fdt_serial {
 	const struct fdt_match *match_table;
 	int (*init)(void *fdt, int nodeoff, const struct fdt_match *match);
@@ -19,4 +21,10 @@ struct fdt_serial {
 
 int fdt_serial_init(void);
 
+#else
+
+static inline int fdt_serial_init(void) { return 0; }
+
+#endif
+
 #endif
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index d6e0506..4524a3f 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -1 +1,7 @@
 # SPDX-License-Identifier: BSD-2-Clause
+
+menu "Utils and Drivers Support"
+
+source "$(OPENSBI_SRC_DIR)/lib/utils/serial/Kconfig"
+
+endmenu
diff --git a/lib/utils/serial/Kconfig b/lib/utils/serial/Kconfig
new file mode 100644
index 0000000..e114f26
--- /dev/null
+++ b/lib/utils/serial/Kconfig
@@ -0,0 +1,71 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "Serial Device Support"
+
+config FDT_SERIAL
+	bool "FDT based serial drivers"
+	default n
+
+if FDT_SERIAL
+
+config FDT_SERIAL_GAISLER
+	bool "Gaisler UART FDT driver"
+	select SERIAL_GAISLER
+	default n
+
+config FDT_SERIAL_HTIF
+	bool "Host transfer interface (HTIF) UART FDT driver"
+	default n
+
+config FDT_SERIAL_SHAKTI
+	bool "Shakti UART FDT driver"
+	select SERIAL_SHAKTI
+	default n
+
+config FDT_SERIAL_SIFIVE
+	bool "SiFive UART FDT driver"
+	select SERIAL_SIFIVE
+	default n
+
+config FDT_SERIAL_LITEX
+	bool "LiteX UART FDT driver"
+	select SERIAL_LITEX
+	default n
+
+config FDT_SERIAL_UART8250
+	bool "8250 UART FDT driver"
+	select SERIAL_UART8250
+	default n
+
+config FDT_SERIAL_XILINX_UARTLITE
+	bool "Xilinx UART Lite FDT driver"
+	select SERIAL_XILINX_UARTLITE
+	default n
+
+endif
+
+config SERIAL_GAISLER
+	bool "Gaisler UART support"
+	default n
+
+config SERIAL_SHAKTI
+	bool "Shakti UART support"
+	default n
+
+config SERIAL_SIFIVE
+	bool "SiFive UART support"
+	default n
+
+config SERIAL_LITEX
+	bool "LiteX UART support"
+	default n
+
+config SERIAL_UART8250
+	bool "8250 UART support"
+	default n
+
+config SERIAL_XILINX_UARTLITE
+	bool "Xilinx UART Lite support"
+	default n
+
+endmenu
diff --git a/lib/utils/serial/objects.mk b/lib/utils/serial/objects.mk
index d26a74e..fa9f5a3 100644
--- a/lib/utils/serial/objects.mk
+++ b/lib/utils/serial/objects.mk
@@ -7,33 +7,33 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += serial/fdt_serial.o
-libsbiutils-objs-y += serial/fdt_serial_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_SERIAL) += serial/fdt_serial.o
+libsbiutils-objs-$(CONFIG_FDT_SERIAL) += serial/fdt_serial_drivers.o
 
-carray-fdt_serial_drivers-y += fdt_serial_gaisler
-libsbiutils-objs-y += serial/fdt_serial_gaisler.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_GAISLER) += fdt_serial_gaisler
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_GAISLER) += serial/fdt_serial_gaisler.o
 
-carray-fdt_serial_drivers-y += fdt_serial_htif
-libsbiutils-objs-y += serial/fdt_serial_htif.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_HTIF) += fdt_serial_htif
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_HTIF) += serial/fdt_serial_htif.o
 
-carray-fdt_serial_drivers-y += fdt_serial_shakti
-libsbiutils-objs-y += serial/fdt_serial_shakti.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_SHAKTI) += fdt_serial_shakti
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_SHAKTI) += serial/fdt_serial_shakti.o
 
-carray-fdt_serial_drivers-y += fdt_serial_sifive
-libsbiutils-objs-y += serial/fdt_serial_sifive.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_SIFIVE) += fdt_serial_sifive
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_SIFIVE) += serial/fdt_serial_sifive.o
 
-carray-fdt_serial_drivers-y += fdt_serial_litex
-libsbiutils-objs-y += serial/fdt_serial_litex.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_LITEX) += fdt_serial_litex
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_LITEX) += serial/fdt_serial_litex.o
 
-carray-fdt_serial_drivers-y += fdt_serial_uart8250
-libsbiutils-objs-y += serial/fdt_serial_uart8250.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_UART8250) += fdt_serial_uart8250
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_UART8250) += serial/fdt_serial_uart8250.o
 
-carray-fdt_serial_drivers-y += fdt_serial_xlnx_uartlite
-libsbiutils-objs-y += serial/fdt_serial_xlnx_uartlite.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_XILINX_UARTLITE) += fdt_serial_xlnx_uartlite
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_XILINX_UARTLITE) += serial/fdt_serial_xlnx_uartlite.o
 
-libsbiutils-objs-y += serial/gaisler-uart.o
-libsbiutils-objs-y += serial/shakti-uart.o
-libsbiutils-objs-y += serial/sifive-uart.o
-libsbiutils-objs-y += serial/litex-uart.o
-libsbiutils-objs-y += serial/uart8250.o
-libsbiutils-objs-y += serial/xlnx-uartlite.o
+libsbiutils-objs-$(CONFIG_SERIAL_GAISLER) += serial/gaisler-uart.o
+libsbiutils-objs-$(CONFIG_SERIAL_SHAKTI) += serial/shakti-uart.o
+libsbiutils-objs-$(CONFIG_SERIAL_SIFIVE) += serial/sifive-uart.o
+libsbiutils-objs-$(CONFIG_SERIAL_LITEX) += serial/litex-uart.o
+libsbiutils-objs-$(CONFIG_SERIAL_UART8250) += serial/uart8250.o
+libsbiutils-objs-$(CONFIG_SERIAL_XILINX_UARTLITE) += serial/xlnx-uartlite.o
diff --git a/platform/andes/ae350/Kconfig b/platform/andes/ae350/Kconfig
index 4b73c76..162d19c 100644
--- a/platform/andes/ae350/Kconfig
+++ b/platform/andes/ae350/Kconfig
@@ -2,4 +2,5 @@
 
 config PLATFORM_ANDES_AE350
 	bool
+	select SERIAL_UART8250
 	default y
diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
index 70d2004..3ce0ec1 100644
--- a/platform/fpga/ariane/Kconfig
+++ b/platform/fpga/ariane/Kconfig
@@ -2,4 +2,5 @@
 
 config PLATFORM_ARIANE_FPGA
 	bool
+	select SERIAL_UART8250
 	default y
diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
index 81e4b76..4dd752d 100644
--- a/platform/fpga/openpiton/Kconfig
+++ b/platform/fpga/openpiton/Kconfig
@@ -2,4 +2,5 @@
 
 config PLATFORM_OPENPITON_FPGA
 	bool
+	select SERIAL_UART8250
 	default y
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index e69de29..ce70c59 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -0,0 +1,8 @@
+CONFIG_FDT_SERIAL=y
+CONFIG_FDT_SERIAL_GAISLER=y
+CONFIG_FDT_SERIAL_HTIF=y
+CONFIG_FDT_SERIAL_SHAKTI=y
+CONFIG_FDT_SERIAL_SIFIVE=y
+CONFIG_FDT_SERIAL_LITEX=y
+CONFIG_FDT_SERIAL_UART8250=y
+CONFIG_FDT_SERIAL_XILINX_UARTLITE=y
diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
index 889f16b..adc7613 100644
--- a/platform/kendryte/k210/Kconfig
+++ b/platform/kendryte/k210/Kconfig
@@ -2,4 +2,5 @@
 
 config PLATFORM_KENDRYTE_K210
 	bool
+	select SERIAL_SIFIVE
 	default y
diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
index c368ef4..69fe8f8 100644
--- a/platform/nuclei/ux600/Kconfig
+++ b/platform/nuclei/ux600/Kconfig
@@ -2,4 +2,5 @@
 
 config PLATFORM_NUCLEI_UX600
 	bool
+	select SERIAL_SIFIVE
 	default y
diff --git a/platform/template/Kconfig b/platform/template/Kconfig
index b9c4350..5a5472b 100644
--- a/platform/template/Kconfig
+++ b/platform/template/Kconfig
@@ -2,4 +2,5 @@
 
 config PLATFORM_TEMPLATE
 	bool
+	select SERIAL_UART8250
 	default y
-- 
2.34.1



From apatel at ventanamicro.com  Tue Jul 19 06:18:27 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Tue, 19 Jul 2022 18:48:27 +0530
Subject: [PATCH v3 05/15] lib: utils/reset: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220719131837.2389630-1-apatel@ventanamicro.com>
References: <20220719131837.2389630-1-apatel@ventanamicro.com>
Message-ID: <20220719131837.2389630-6-apatel@ventanamicro.com>

We update reset drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate reset
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 include/sbi_utils/reset/fdt_reset.h | 12 +++++++++++
 lib/utils/Kconfig                   |  2 ++
 lib/utils/reset/Kconfig             | 33 +++++++++++++++++++++++++++++
 lib/utils/reset/objects.mk          | 28 ++++++++++++------------
 platform/generic/configs/defconfig  |  6 ++++++
 5 files changed, 67 insertions(+), 14 deletions(-)
 create mode 100644 lib/utils/reset/Kconfig

diff --git a/include/sbi_utils/reset/fdt_reset.h b/include/sbi_utils/reset/fdt_reset.h
index e7f7350..ea8063b 100644
--- a/include/sbi_utils/reset/fdt_reset.h
+++ b/include/sbi_utils/reset/fdt_reset.h
@@ -17,6 +17,8 @@ struct fdt_reset {
 	int (*init)(void *fdt, int nodeoff, const struct fdt_match *match);
 };
 
+#ifdef CONFIG_FDT_RESET
+
 /**
  * fdt_reset_driver_init() - initialize reset driver based on the device-tree
  */
@@ -29,4 +31,14 @@ int fdt_reset_driver_init(void *fdt, struct fdt_reset *drv);
  */
 void fdt_reset_init(void);
 
+#else
+
+static inline int fdt_reset_driver_init(void *fdt, struct fdt_reset *drv)
+{
+	return 0;
+}
+static inline void fdt_reset_init(void) { }
+
+#endif
+
 #endif
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 4524a3f..f061b7a 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -2,6 +2,8 @@
 
 menu "Utils and Drivers Support"
 
+source "$(OPENSBI_SRC_DIR)/lib/utils/reset/Kconfig"
+
 source "$(OPENSBI_SRC_DIR)/lib/utils/serial/Kconfig"
 
 endmenu
diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig
new file mode 100644
index 0000000..845a1ae
--- /dev/null
+++ b/lib/utils/reset/Kconfig
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "System Reset Support"
+
+config FDT_RESET
+	bool "FDT based reset drivers"
+	default n
+
+if FDT_RESET
+
+config FDT_RESET_GPIO
+	bool "GPIO FDT reset driver"
+	default n
+
+config FDT_RESET_HTIF
+	bool "Host transfer interface (HTIF) FDT reset driver"
+	default n
+
+config FDT_RESET_SIFIVE_TEST
+	bool "SiFive Test FDT reset driver"
+	default n
+
+config FDT_RESET_SUNXI_WDT
+	bool "Sunxi WDT FDT reset driver"
+	default n
+
+config FDT_RESET_THEAD
+	bool "T-HEAD FDT reset driver"
+	default n
+
+endif
+
+endmenu
diff --git a/lib/utils/reset/objects.mk b/lib/utils/reset/objects.mk
index 8cddcdf..8a50dd0 100644
--- a/lib/utils/reset/objects.mk
+++ b/lib/utils/reset/objects.mk
@@ -7,22 +7,22 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += reset/fdt_reset.o
-libsbiutils-objs-y += reset/fdt_reset_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_RESET) += reset/fdt_reset.o
+libsbiutils-objs-$(CONFIG_FDT_RESET) += reset/fdt_reset_drivers.o
 
-carray-fdt_reset_drivers-y += fdt_poweroff_gpio
-carray-fdt_reset_drivers-y += fdt_reset_gpio
-libsbiutils-objs-y += reset/fdt_reset_gpio.o
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_GPIO) += fdt_poweroff_gpio
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_GPIO) += fdt_reset_gpio
+libsbiutils-objs-$(CONFIG_FDT_RESET_GPIO) += reset/fdt_reset_gpio.o
 
-carray-fdt_reset_drivers-y += fdt_reset_htif
-libsbiutils-objs-y += reset/fdt_reset_htif.o
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_HTIF) += fdt_reset_htif
+libsbiutils-objs-$(CONFIG_FDT_RESET_HTIF) += reset/fdt_reset_htif.o
 
-carray-fdt_reset_drivers-y += fdt_reset_sifive_test
-libsbiutils-objs-y += reset/fdt_reset_sifive_test.o
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_SIFIVE_TEST) += fdt_reset_sifive_test
+libsbiutils-objs-$(CONFIG_FDT_RESET_SIFIVE_TEST) += reset/fdt_reset_sifive_test.o
 
-carray-fdt_reset_drivers-y += fdt_reset_sunxi_wdt
-libsbiutils-objs-y += reset/fdt_reset_sunxi_wdt.o
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_SUNXI_WDT) += fdt_reset_sunxi_wdt
+libsbiutils-objs-$(CONFIG_FDT_RESET_SUNXI_WDT) += reset/fdt_reset_sunxi_wdt.o
 
-carray-fdt_reset_drivers-y += fdt_reset_thead
-libsbiutils-objs-y += reset/fdt_reset_thead.o
-libsbiutils-objs-y += reset/fdt_reset_thead_asm.o
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_THEAD) += fdt_reset_thead
+libsbiutils-objs-$(CONFIG_FDT_RESET_THEAD) += reset/fdt_reset_thead.o
+libsbiutils-objs-$(CONFIG_FDT_RESET_THEAD) += reset/fdt_reset_thead_asm.o
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index ce70c59..9778665 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -1,3 +1,9 @@
+CONFIG_FDT_RESET=y
+CONFIG_FDT_RESET_GPIO=y
+CONFIG_FDT_RESET_HTIF=y
+CONFIG_FDT_RESET_SIFIVE_TEST=y
+CONFIG_FDT_RESET_SUNXI_WDT=y
+CONFIG_FDT_RESET_THEAD=y
 CONFIG_FDT_SERIAL=y
 CONFIG_FDT_SERIAL_GAISLER=y
 CONFIG_FDT_SERIAL_HTIF=y
-- 
2.34.1



From apatel at ventanamicro.com  Tue Jul 19 06:18:28 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Tue, 19 Jul 2022 18:48:28 +0530
Subject: [PATCH v3 06/15] lib: utils/sys: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220719131837.2389630-1-apatel@ventanamicro.com>
References: <20220719131837.2389630-1-apatel@ventanamicro.com>
Message-ID: <20220719131837.2389630-7-apatel@ventanamicro.com>

We update system drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate system
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 lib/utils/Kconfig        |  2 ++
 lib/utils/reset/Kconfig  |  2 ++
 lib/utils/serial/Kconfig |  1 +
 lib/utils/sys/Kconfig    | 13 +++++++++++++
 lib/utils/sys/objects.mk |  4 ++--
 5 files changed, 20 insertions(+), 2 deletions(-)
 create mode 100644 lib/utils/sys/Kconfig

diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index f061b7a..492f4e9 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -6,4 +6,6 @@ source "$(OPENSBI_SRC_DIR)/lib/utils/reset/Kconfig"
 
 source "$(OPENSBI_SRC_DIR)/lib/utils/serial/Kconfig"
 
+source "$(OPENSBI_SRC_DIR)/lib/utils/sys/Kconfig"
+
 endmenu
diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig
index 845a1ae..e7a71e3 100644
--- a/lib/utils/reset/Kconfig
+++ b/lib/utils/reset/Kconfig
@@ -14,10 +14,12 @@ config FDT_RESET_GPIO
 
 config FDT_RESET_HTIF
 	bool "Host transfer interface (HTIF) FDT reset driver"
+	select SYS_HTIF
 	default n
 
 config FDT_RESET_SIFIVE_TEST
 	bool "SiFive Test FDT reset driver"
+	select SYS_SIFIVE_TEST
 	default n
 
 config FDT_RESET_SUNXI_WDT
diff --git a/lib/utils/serial/Kconfig b/lib/utils/serial/Kconfig
index e114f26..bf37e34 100644
--- a/lib/utils/serial/Kconfig
+++ b/lib/utils/serial/Kconfig
@@ -15,6 +15,7 @@ config FDT_SERIAL_GAISLER
 
 config FDT_SERIAL_HTIF
 	bool "Host transfer interface (HTIF) UART FDT driver"
+	select SYS_HTIF
 	default n
 
 config FDT_SERIAL_SHAKTI
diff --git a/lib/utils/sys/Kconfig b/lib/utils/sys/Kconfig
new file mode 100644
index 0000000..ee85b1a
--- /dev/null
+++ b/lib/utils/sys/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "System Device Support"
+
+config SYS_HTIF
+	bool "Host transfere interface (HTIF) support"
+	default n
+
+config SYS_SIFIVE_TEST
+	bool "SiFive test support"
+	default n
+
+endmenu
diff --git a/lib/utils/sys/objects.mk b/lib/utils/sys/objects.mk
index 06be322..9f67aee 100644
--- a/lib/utils/sys/objects.mk
+++ b/lib/utils/sys/objects.mk
@@ -7,5 +7,5 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += sys/htif.o
-libsbiutils-objs-y += sys/sifive_test.o
+libsbiutils-objs-$(CONFIG_SYS_HTIF) += sys/htif.o
+libsbiutils-objs-$(CONFIG_SYS_SIFIVE_TEST) += sys/sifive_test.o
-- 
2.34.1



From apatel at ventanamicro.com  Tue Jul 19 06:18:29 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Tue, 19 Jul 2022 18:48:29 +0530
Subject: [PATCH v3 07/15] lib: utils/timer: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220719131837.2389630-1-apatel@ventanamicro.com>
References: <20220719131837.2389630-1-apatel@ventanamicro.com>
Message-ID: <20220719131837.2389630-8-apatel@ventanamicro.com>

We update timer drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate timer
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 include/sbi_utils/timer/fdt_timer.h |  9 +++++++++
 lib/utils/Kconfig                   |  2 ++
 lib/utils/timer/Kconfig             | 22 ++++++++++++++++++++++
 lib/utils/timer/objects.mk          | 10 +++++-----
 platform/fpga/ariane/Kconfig        |  1 +
 platform/fpga/openpiton/Kconfig     |  1 +
 platform/generic/configs/defconfig  |  2 ++
 platform/kendryte/k210/Kconfig      |  1 +
 platform/nuclei/ux600/Kconfig       |  1 +
 platform/template/Kconfig           |  1 +
 10 files changed, 45 insertions(+), 5 deletions(-)
 create mode 100644 lib/utils/timer/Kconfig

diff --git a/include/sbi_utils/timer/fdt_timer.h b/include/sbi_utils/timer/fdt_timer.h
index 36202a4..cf105fe 100644
--- a/include/sbi_utils/timer/fdt_timer.h
+++ b/include/sbi_utils/timer/fdt_timer.h
@@ -12,6 +12,8 @@
 
 #include <sbi/sbi_types.h>
 
+#ifdef CONFIG_FDT_TIMER
+
 struct fdt_timer {
 	const struct fdt_match *match_table;
 	int (*cold_init)(void *fdt, int nodeoff, const struct fdt_match *match);
@@ -23,4 +25,11 @@ void fdt_timer_exit(void);
 
 int fdt_timer_init(bool cold_boot);
 
+#else
+
+static inline void fdt_timer_exit(void) { }
+static inline int fdt_timer_init(bool cold_boot) { return 0; }
+
+#endif
+
 #endif
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 492f4e9..6561d0b 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -8,4 +8,6 @@ source "$(OPENSBI_SRC_DIR)/lib/utils/serial/Kconfig"
 
 source "$(OPENSBI_SRC_DIR)/lib/utils/sys/Kconfig"
 
+source "$(OPENSBI_SRC_DIR)/lib/utils/timer/Kconfig"
+
 endmenu
diff --git a/lib/utils/timer/Kconfig b/lib/utils/timer/Kconfig
new file mode 100644
index 0000000..0a1c727
--- /dev/null
+++ b/lib/utils/timer/Kconfig
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "Timer Device Support"
+
+config FDT_TIMER
+	bool "FDT based timer drivers"
+	default n
+
+if FDT_TIMER
+
+config FDT_TIMER_MTIMER
+	bool "ACLINT MTIMER FDT driver"
+	select TIMER_MTIMER
+	default n
+
+endif
+
+config TIMER_MTIMER
+	bool "ACLINT MTIMER support"
+	default n
+
+endmenu
diff --git a/lib/utils/timer/objects.mk b/lib/utils/timer/objects.mk
index bc4073d..7f5f3ce 100644
--- a/lib/utils/timer/objects.mk
+++ b/lib/utils/timer/objects.mk
@@ -7,10 +7,10 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += timer/aclint_mtimer.o
+libsbiutils-objs-$(CONFIG_TIMER_MTIMER) += timer/aclint_mtimer.o
 
-libsbiutils-objs-y += timer/fdt_timer.o
-libsbiutils-objs-y += timer/fdt_timer_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_TIMER) += timer/fdt_timer.o
+libsbiutils-objs-$(CONFIG_FDT_TIMER) += timer/fdt_timer_drivers.o
 
-carray-fdt_timer_drivers-y += fdt_timer_mtimer
-libsbiutils-objs-y += timer/fdt_timer_mtimer.o
+carray-fdt_timer_drivers-$(CONFIG_FDT_TIMER_MTIMER) += fdt_timer_mtimer
+libsbiutils-objs-$(CONFIG_FDT_TIMER_MTIMER) += timer/fdt_timer_mtimer.o
diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
index 3ce0ec1..a7fd758 100644
--- a/platform/fpga/ariane/Kconfig
+++ b/platform/fpga/ariane/Kconfig
@@ -3,4 +3,5 @@
 config PLATFORM_ARIANE_FPGA
 	bool
 	select SERIAL_UART8250
+	select TIMER_MTIMER
 	default y
diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
index 4dd752d..a7f09e4 100644
--- a/platform/fpga/openpiton/Kconfig
+++ b/platform/fpga/openpiton/Kconfig
@@ -3,4 +3,5 @@
 config PLATFORM_OPENPITON_FPGA
 	bool
 	select SERIAL_UART8250
+	select TIMER_MTIMER
 	default y
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index 9778665..9b96a4f 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -12,3 +12,5 @@ CONFIG_FDT_SERIAL_SIFIVE=y
 CONFIG_FDT_SERIAL_LITEX=y
 CONFIG_FDT_SERIAL_UART8250=y
 CONFIG_FDT_SERIAL_XILINX_UARTLITE=y
+CONFIG_FDT_TIMER=y
+CONFIG_FDT_TIMER_MTIMER=y
diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
index adc7613..90db649 100644
--- a/platform/kendryte/k210/Kconfig
+++ b/platform/kendryte/k210/Kconfig
@@ -3,4 +3,5 @@
 config PLATFORM_KENDRYTE_K210
 	bool
 	select SERIAL_SIFIVE
+	select TIMER_MTIMER
 	default y
diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
index 69fe8f8..dec2654 100644
--- a/platform/nuclei/ux600/Kconfig
+++ b/platform/nuclei/ux600/Kconfig
@@ -3,4 +3,5 @@
 config PLATFORM_NUCLEI_UX600
 	bool
 	select SERIAL_SIFIVE
+	select TIMER_MTIMER
 	default y
diff --git a/platform/template/Kconfig b/platform/template/Kconfig
index 5a5472b..ce463ea 100644
--- a/platform/template/Kconfig
+++ b/platform/template/Kconfig
@@ -3,4 +3,5 @@
 config PLATFORM_TEMPLATE
 	bool
 	select SERIAL_UART8250
+	select TIMER_MTIMER
 	default y
-- 
2.34.1



From apatel at ventanamicro.com  Tue Jul 19 06:18:30 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Tue, 19 Jul 2022 18:48:30 +0530
Subject: [PATCH v3 08/15] lib: utils/ipi: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220719131837.2389630-1-apatel@ventanamicro.com>
References: <20220719131837.2389630-1-apatel@ventanamicro.com>
Message-ID: <20220719131837.2389630-9-apatel@ventanamicro.com>

We update ipi drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate ipi
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 include/sbi_utils/ipi/fdt_ipi.h    |  9 +++++++++
 lib/utils/Kconfig                  |  2 ++
 lib/utils/ipi/Kconfig              | 22 ++++++++++++++++++++++
 lib/utils/ipi/objects.mk           | 10 +++++-----
 platform/fpga/ariane/Kconfig       |  1 +
 platform/fpga/openpiton/Kconfig    |  1 +
 platform/generic/configs/defconfig |  2 ++
 platform/kendryte/k210/Kconfig     |  1 +
 platform/nuclei/ux600/Kconfig      |  1 +
 platform/template/Kconfig          |  1 +
 10 files changed, 45 insertions(+), 5 deletions(-)
 create mode 100644 lib/utils/ipi/Kconfig

diff --git a/include/sbi_utils/ipi/fdt_ipi.h b/include/sbi_utils/ipi/fdt_ipi.h
index 9337353..1dd9062 100644
--- a/include/sbi_utils/ipi/fdt_ipi.h
+++ b/include/sbi_utils/ipi/fdt_ipi.h
@@ -12,6 +12,8 @@
 
 #include <sbi/sbi_types.h>
 
+#ifdef CONFIG_FDT_IPI
+
 struct fdt_ipi {
 	const struct fdt_match *match_table;
 	int (*cold_init)(void *fdt, int nodeoff, const struct fdt_match *match);
@@ -23,4 +25,11 @@ void fdt_ipi_exit(void);
 
 int fdt_ipi_init(bool cold_boot);
 
+#else
+
+static inline void fdt_ipi_exit(void) { }
+static inline int fdt_ipi_init(bool cold_boot) { return 0; }
+
+#endif
+
 #endif
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 6561d0b..50d9770 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -2,6 +2,8 @@
 
 menu "Utils and Drivers Support"
 
+source "$(OPENSBI_SRC_DIR)/lib/utils/ipi/Kconfig"
+
 source "$(OPENSBI_SRC_DIR)/lib/utils/reset/Kconfig"
 
 source "$(OPENSBI_SRC_DIR)/lib/utils/serial/Kconfig"
diff --git a/lib/utils/ipi/Kconfig b/lib/utils/ipi/Kconfig
new file mode 100644
index 0000000..c0634d3
--- /dev/null
+++ b/lib/utils/ipi/Kconfig
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "IPI Device Support"
+
+config FDT_IPI
+	bool "FDT based ipi drivers"
+	default n
+
+if FDT_IPI
+
+config FDT_IPI_MSWI
+	bool "ACLINT MSWI FDT driver"
+	select IPI_MSWI
+	default n
+
+endif
+
+config IPI_MSWI
+	bool "ACLINT MSWI support"
+	default n
+
+endmenu
diff --git a/lib/utils/ipi/objects.mk b/lib/utils/ipi/objects.mk
index 0b0bc2d..0600cac 100644
--- a/lib/utils/ipi/objects.mk
+++ b/lib/utils/ipi/objects.mk
@@ -7,10 +7,10 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += ipi/aclint_mswi.o
+libsbiutils-objs-$(CONFIG_IPI_MSWI) += ipi/aclint_mswi.o
 
-libsbiutils-objs-y += ipi/fdt_ipi.o
-libsbiutils-objs-y += ipi/fdt_ipi_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_IPI) += ipi/fdt_ipi.o
+libsbiutils-objs-$(CONFIG_FDT_IPI) += ipi/fdt_ipi_drivers.o
 
-carray-fdt_ipi_drivers-y += fdt_ipi_mswi
-libsbiutils-objs-y += ipi/fdt_ipi_mswi.o
+carray-fdt_ipi_drivers-$(CONFIG_FDT_IPI_MSWI) += fdt_ipi_mswi
+libsbiutils-objs-$(CONFIG_FDT_IPI_MSWI) += ipi/fdt_ipi_mswi.o
diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
index a7fd758..0bc1597 100644
--- a/platform/fpga/ariane/Kconfig
+++ b/platform/fpga/ariane/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_ARIANE_FPGA
 	bool
+	select IPI_MSWI
 	select SERIAL_UART8250
 	select TIMER_MTIMER
 	default y
diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
index a7f09e4..0e4e3ba 100644
--- a/platform/fpga/openpiton/Kconfig
+++ b/platform/fpga/openpiton/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_OPENPITON_FPGA
 	bool
+	select IPI_MSWI
 	select SERIAL_UART8250
 	select TIMER_MTIMER
 	default y
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index 9b96a4f..e218c32 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -1,3 +1,5 @@
+CONFIG_FDT_IPI=y
+CONFIG_FDT_IPI_MSWI=y
 CONFIG_FDT_RESET=y
 CONFIG_FDT_RESET_GPIO=y
 CONFIG_FDT_RESET_HTIF=y
diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
index 90db649..685a5cd 100644
--- a/platform/kendryte/k210/Kconfig
+++ b/platform/kendryte/k210/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_KENDRYTE_K210
 	bool
+	select IPI_MSWI
 	select SERIAL_SIFIVE
 	select TIMER_MTIMER
 	default y
diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
index dec2654..cd995a3 100644
--- a/platform/nuclei/ux600/Kconfig
+++ b/platform/nuclei/ux600/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_NUCLEI_UX600
 	bool
+	select IPI_MSWI
 	select SERIAL_SIFIVE
 	select TIMER_MTIMER
 	default y
diff --git a/platform/template/Kconfig b/platform/template/Kconfig
index ce463ea..672b6e7 100644
--- a/platform/template/Kconfig
+++ b/platform/template/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_TEMPLATE
 	bool
+	select IPI_MSWI
 	select SERIAL_UART8250
 	select TIMER_MTIMER
 	default y
-- 
2.34.1



From apatel at ventanamicro.com  Tue Jul 19 06:18:31 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Tue, 19 Jul 2022 18:48:31 +0530
Subject: [PATCH v3 09/15] lib: utils/irqchip: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220719131837.2389630-1-apatel@ventanamicro.com>
References: <20220719131837.2389630-1-apatel@ventanamicro.com>
Message-ID: <20220719131837.2389630-10-apatel@ventanamicro.com>

We update irqchip drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate irqchip
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 include/sbi_utils/irqchip/fdt_irqchip.h | 10 +++++++
 include/sbi_utils/irqchip/imsic.h       | 10 +++++++
 lib/utils/Kconfig                       |  2 ++
 lib/utils/irqchip/Kconfig               | 40 +++++++++++++++++++++++++
 lib/utils/irqchip/objects.mk            | 22 +++++++-------
 platform/andes/ae350/Kconfig            |  1 +
 platform/fpga/ariane/Kconfig            |  1 +
 platform/fpga/openpiton/Kconfig         |  1 +
 platform/generic/configs/defconfig      |  4 +++
 platform/kendryte/k210/Kconfig          |  1 +
 platform/nuclei/ux600/Kconfig           |  1 +
 platform/template/Kconfig               |  1 +
 12 files changed, 83 insertions(+), 11 deletions(-)
 create mode 100644 lib/utils/irqchip/Kconfig

diff --git a/include/sbi_utils/irqchip/fdt_irqchip.h b/include/sbi_utils/irqchip/fdt_irqchip.h
index 13ef6f7..63e9fd9 100644
--- a/include/sbi_utils/irqchip/fdt_irqchip.h
+++ b/include/sbi_utils/irqchip/fdt_irqchip.h
@@ -12,6 +12,8 @@
 
 #include <sbi/sbi_types.h>
 
+#ifdef CONFIG_FDT_IRQCHIP
+
 struct fdt_irqchip {
 	const struct fdt_match *match_table;
 	int (*cold_init)(void *fdt, int nodeoff, const struct fdt_match *match);
@@ -23,4 +25,12 @@ void fdt_irqchip_exit(void);
 
 int fdt_irqchip_init(bool cold_boot);
 
+#else
+
+static inline void fdt_irqchip_exit(void) { }
+
+static inline int fdt_irqchip_init(bool cold_boot) { return 0; }
+
+#endif
+
 #endif
diff --git a/include/sbi_utils/irqchip/imsic.h b/include/sbi_utils/irqchip/imsic.h
index cffcb5a..e295771 100644
--- a/include/sbi_utils/irqchip/imsic.h
+++ b/include/sbi_utils/irqchip/imsic.h
@@ -33,6 +33,8 @@ struct imsic_data {
 	struct imsic_regs regs[IMSIC_MAX_REGS];
 };
 
+#ifdef CONFIG_IRQCHIP_IMSIC
+
 int imsic_map_hartid_to_data(u32 hartid, struct imsic_data *imsic, int file);
 
 struct imsic_data *imsic_get_data(u32 hartid);
@@ -47,4 +49,12 @@ int imsic_data_check(struct imsic_data *imsic);
 
 int imsic_cold_irqchip_init(struct imsic_data *imsic);
 
+#else
+
+static inline void imsic_local_irqchip_init(void) { }
+
+static inline int imsic_data_check(struct imsic_data *imsic) { return 0; }
+
+#endif
+
 #endif
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 50d9770..3f315fd 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -4,6 +4,8 @@ menu "Utils and Drivers Support"
 
 source "$(OPENSBI_SRC_DIR)/lib/utils/ipi/Kconfig"
 
+source "$(OPENSBI_SRC_DIR)/lib/utils/irqchip/Kconfig"
+
 source "$(OPENSBI_SRC_DIR)/lib/utils/reset/Kconfig"
 
 source "$(OPENSBI_SRC_DIR)/lib/utils/serial/Kconfig"
diff --git a/lib/utils/irqchip/Kconfig b/lib/utils/irqchip/Kconfig
new file mode 100644
index 0000000..6a7e297
--- /dev/null
+++ b/lib/utils/irqchip/Kconfig
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "Interrupt Controller Support"
+
+config FDT_IRQCHIP
+	bool "FDT based interrupt controller drivers"
+	default n
+
+if FDT_IRQCHIP
+
+config FDT_IRQCHIP_APLIC
+	bool "Advanced Platform Level Interrupt Controller (APLIC) FDT driver"
+	select IRQCHIP_APLIC
+	default n
+
+config FDT_IRQCHIP_IMSIC
+	bool "Incoming Message Signalled Interrupt Controller (IMSIC) FDT driver"
+	select IRQCHIP_IMSIC
+	default n
+
+config FDT_IRQCHIP_PLIC
+	bool "Platform Level Interrupt Controller (PLIC) FDT driver"
+	select IRQCHIP_PLIC
+	default n
+
+endif
+
+config IRQCHIP_APLIC
+	bool "Advanced Platform Level Interrupt Controller (APLIC) support"
+	default n
+
+config IRQCHIP_IMSIC
+	bool "Incoming Message Signalled Interrupt Controller (IMSIC) support"
+	default n
+
+config IRQCHIP_PLIC
+	bool "Platform Level Interrupt Controller (PLIC) support"
+	default n
+
+endmenu
diff --git a/lib/utils/irqchip/objects.mk b/lib/utils/irqchip/objects.mk
index 7775bc4..e458891 100644
--- a/lib/utils/irqchip/objects.mk
+++ b/lib/utils/irqchip/objects.mk
@@ -7,18 +7,18 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += irqchip/fdt_irqchip.o
-libsbiutils-objs-y += irqchip/fdt_irqchip_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_IRQCHIP) += irqchip/fdt_irqchip.o
+libsbiutils-objs-$(CONFIG_FDT_IRQCHIP) += irqchip/fdt_irqchip_drivers.o
 
-carray-fdt_irqchip_drivers-y += fdt_irqchip_aplic
-libsbiutils-objs-y += irqchip/fdt_irqchip_aplic.o
+carray-fdt_irqchip_drivers-$(CONFIG_FDT_IRQCHIP_APLIC) += fdt_irqchip_aplic
+libsbiutils-objs-$(CONFIG_FDT_IRQCHIP_APLIC) += irqchip/fdt_irqchip_aplic.o
 
-carray-fdt_irqchip_drivers-y += fdt_irqchip_imsic
-libsbiutils-objs-y += irqchip/fdt_irqchip_imsic.o
+carray-fdt_irqchip_drivers-$(CONFIG_FDT_IRQCHIP_IMSIC) += fdt_irqchip_imsic
+libsbiutils-objs-$(CONFIG_FDT_IRQCHIP_IMSIC) += irqchip/fdt_irqchip_imsic.o
 
-carray-fdt_irqchip_drivers-y += fdt_irqchip_plic
-libsbiutils-objs-y += irqchip/fdt_irqchip_plic.o
+carray-fdt_irqchip_drivers-$(CONFIG_FDT_IRQCHIP_PLIC) += fdt_irqchip_plic
+libsbiutils-objs-$(CONFIG_FDT_IRQCHIP_PLIC) += irqchip/fdt_irqchip_plic.o
 
-libsbiutils-objs-y += irqchip/aplic.o
-libsbiutils-objs-y += irqchip/imsic.o
-libsbiutils-objs-y += irqchip/plic.o
+libsbiutils-objs-$(CONFIG_IRQCHIP_APLIC) += irqchip/aplic.o
+libsbiutils-objs-$(CONFIG_IRQCHIP_IMSIC) += irqchip/imsic.o
+libsbiutils-objs-$(CONFIG_IRQCHIP_PLIC) += irqchip/plic.o
diff --git a/platform/andes/ae350/Kconfig b/platform/andes/ae350/Kconfig
index 162d19c..87deab0 100644
--- a/platform/andes/ae350/Kconfig
+++ b/platform/andes/ae350/Kconfig
@@ -2,5 +2,6 @@
 
 config PLATFORM_ANDES_AE350
 	bool
+	select IRQCHIP_PLIC
 	select SERIAL_UART8250
 	default y
diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
index 0bc1597..5a720d9 100644
--- a/platform/fpga/ariane/Kconfig
+++ b/platform/fpga/ariane/Kconfig
@@ -3,6 +3,7 @@
 config PLATFORM_ARIANE_FPGA
 	bool
 	select IPI_MSWI
+	select IRQCHIP_PLIC
 	select SERIAL_UART8250
 	select TIMER_MTIMER
 	default y
diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
index 0e4e3ba..973906e 100644
--- a/platform/fpga/openpiton/Kconfig
+++ b/platform/fpga/openpiton/Kconfig
@@ -3,6 +3,7 @@
 config PLATFORM_OPENPITON_FPGA
 	bool
 	select IPI_MSWI
+	select IRQCHIP_PLIC
 	select SERIAL_UART8250
 	select TIMER_MTIMER
 	default y
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index e218c32..ad66a63 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -1,5 +1,9 @@
 CONFIG_FDT_IPI=y
 CONFIG_FDT_IPI_MSWI=y
+CONFIG_FDT_IRQCHIP=y
+CONFIG_FDT_IRQCHIP_APLIC=y
+CONFIG_FDT_IRQCHIP_IMSIC=y
+CONFIG_FDT_IRQCHIP_PLIC=y
 CONFIG_FDT_RESET=y
 CONFIG_FDT_RESET_GPIO=y
 CONFIG_FDT_RESET_HTIF=y
diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
index 685a5cd..2a1daaa 100644
--- a/platform/kendryte/k210/Kconfig
+++ b/platform/kendryte/k210/Kconfig
@@ -3,6 +3,7 @@
 config PLATFORM_KENDRYTE_K210
 	bool
 	select IPI_MSWI
+	select IRQCHIP_PLIC
 	select SERIAL_SIFIVE
 	select TIMER_MTIMER
 	default y
diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
index cd995a3..464802e 100644
--- a/platform/nuclei/ux600/Kconfig
+++ b/platform/nuclei/ux600/Kconfig
@@ -3,6 +3,7 @@
 config PLATFORM_NUCLEI_UX600
 	bool
 	select IPI_MSWI
+	select IRQCHIP_PLIC
 	select SERIAL_SIFIVE
 	select TIMER_MTIMER
 	default y
diff --git a/platform/template/Kconfig b/platform/template/Kconfig
index 672b6e7..8338425 100644
--- a/platform/template/Kconfig
+++ b/platform/template/Kconfig
@@ -3,6 +3,7 @@
 config PLATFORM_TEMPLATE
 	bool
 	select IPI_MSWI
+	select IRQCHIP_PLIC
 	select SERIAL_UART8250
 	select TIMER_MTIMER
 	default y
-- 
2.34.1



From apatel at ventanamicro.com  Tue Jul 19 06:18:32 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Tue, 19 Jul 2022 18:48:32 +0530
Subject: [PATCH v3 10/15] lib: utils/i2c: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220719131837.2389630-1-apatel@ventanamicro.com>
References: <20220719131837.2389630-1-apatel@ventanamicro.com>
Message-ID: <20220719131837.2389630-11-apatel@ventanamicro.com>

We update i2c drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate i2c
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 lib/utils/Kconfig                  |  2 ++
 lib/utils/i2c/Kconfig              | 22 ++++++++++++++++++++++
 lib/utils/i2c/objects.mk           | 10 +++++-----
 platform/generic/configs/defconfig |  2 ++
 4 files changed, 31 insertions(+), 5 deletions(-)
 create mode 100644 lib/utils/i2c/Kconfig

diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 3f315fd..39452d5 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -2,6 +2,8 @@
 
 menu "Utils and Drivers Support"
 
+source "$(OPENSBI_SRC_DIR)/lib/utils/i2c/Kconfig"
+
 source "$(OPENSBI_SRC_DIR)/lib/utils/ipi/Kconfig"
 
 source "$(OPENSBI_SRC_DIR)/lib/utils/irqchip/Kconfig"
diff --git a/lib/utils/i2c/Kconfig b/lib/utils/i2c/Kconfig
new file mode 100644
index 0000000..16c4453
--- /dev/null
+++ b/lib/utils/i2c/Kconfig
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "I2C Support"
+
+config FDT_I2C
+	bool "FDT based I2C drivers"
+	select I2C
+	default n
+
+if FDT_I2C
+
+config FDT_I2C_SIFIVE
+	bool "SiFive I2C FDT driver"
+	default n
+
+endif
+
+config I2C
+	bool "I2C support"
+	default n
+
+endmenu
diff --git a/lib/utils/i2c/objects.mk b/lib/utils/i2c/objects.mk
index bb0d6d5..a0fbbb5 100644
--- a/lib/utils/i2c/objects.mk
+++ b/lib/utils/i2c/objects.mk
@@ -7,10 +7,10 @@
 #   Nikita Shubin <n.shubin at yadro.com>
 #
 
-libsbiutils-objs-y += i2c/i2c.o
+libsbiutils-objs-$(CONFIG_I2C) += i2c/i2c.o
 
-libsbiutils-objs-y += i2c/fdt_i2c.o
-libsbiutils-objs-y += i2c/fdt_i2c_adapter_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_I2C) += i2c/fdt_i2c.o
+libsbiutils-objs-$(CONFIG_FDT_I2C) += i2c/fdt_i2c_adapter_drivers.o
 
-carray-fdt_i2c_adapter_drivers-y += fdt_i2c_adapter_sifive
-libsbiutils-objs-y += i2c/fdt_i2c_sifive.o
+carray-fdt_i2c_adapter_drivers-$(CONFIG_FDT_I2C_SIFIVE) += fdt_i2c_adapter_sifive
+libsbiutils-objs-$(CONFIG_FDT_I2C_SIFIVE) += i2c/fdt_i2c_sifive.o
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index ad66a63..c9c221e 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -1,3 +1,5 @@
+CONFIG_FDT_I2C=y
+CONFIG_FDT_I2C_SIFIVE=y
 CONFIG_FDT_IPI=y
 CONFIG_FDT_IPI_MSWI=y
 CONFIG_FDT_IRQCHIP=y
-- 
2.34.1



From apatel at ventanamicro.com  Tue Jul 19 06:18:33 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Tue, 19 Jul 2022 18:48:33 +0530
Subject: [PATCH v3 11/15] lib: utils/gpio: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220719131837.2389630-1-apatel@ventanamicro.com>
References: <20220719131837.2389630-1-apatel@ventanamicro.com>
Message-ID: <20220719131837.2389630-12-apatel@ventanamicro.com>

We update gpio drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate gpio
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 lib/utils/Kconfig                  |  2 ++
 lib/utils/gpio/Kconfig             | 22 ++++++++++++++++++++++
 lib/utils/gpio/objects.mk          | 10 +++++-----
 lib/utils/reset/Kconfig            |  1 +
 platform/generic/configs/defconfig |  2 ++
 5 files changed, 32 insertions(+), 5 deletions(-)
 create mode 100644 lib/utils/gpio/Kconfig

diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 39452d5..673b02b 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -2,6 +2,8 @@
 
 menu "Utils and Drivers Support"
 
+source "$(OPENSBI_SRC_DIR)/lib/utils/gpio/Kconfig"
+
 source "$(OPENSBI_SRC_DIR)/lib/utils/i2c/Kconfig"
 
 source "$(OPENSBI_SRC_DIR)/lib/utils/ipi/Kconfig"
diff --git a/lib/utils/gpio/Kconfig b/lib/utils/gpio/Kconfig
new file mode 100644
index 0000000..daf7f91
--- /dev/null
+++ b/lib/utils/gpio/Kconfig
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "GPIO Support"
+
+config FDT_GPIO
+	bool "FDT based GPIO drivers"
+	select GPIO
+	default n
+
+if FDT_GPIO
+
+config FDT_GPIO_SIFIVE
+	bool "SiFive GPIO FDT driver"
+	default n
+
+endif
+
+config GPIO
+	bool "GPIO support"
+	default n
+
+endmenu
diff --git a/lib/utils/gpio/objects.mk b/lib/utils/gpio/objects.mk
index a5e131b..eedd699 100644
--- a/lib/utils/gpio/objects.mk
+++ b/lib/utils/gpio/objects.mk
@@ -7,10 +7,10 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += gpio/fdt_gpio.o
-libsbiutils-objs-y += gpio/fdt_gpio_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_GPIO) += gpio/fdt_gpio.o
+libsbiutils-objs-$(CONFIG_FDT_GPIO) += gpio/fdt_gpio_drivers.o
 
-carray-fdt_gpio_drivers-y += fdt_gpio_sifive
-libsbiutils-objs-y += gpio/fdt_gpio_sifive.o
+carray-fdt_gpio_drivers-$(CONFIG_FDT_GPIO_SIFIVE) += fdt_gpio_sifive
+libsbiutils-objs-$(CONFIG_FDT_GPIO_SIFIVE) += gpio/fdt_gpio_sifive.o
 
-libsbiutils-objs-y += gpio/gpio.o
+libsbiutils-objs-$(CONFIG_GPIO) += gpio/gpio.o
diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig
index e7a71e3..71996cb 100644
--- a/lib/utils/reset/Kconfig
+++ b/lib/utils/reset/Kconfig
@@ -10,6 +10,7 @@ if FDT_RESET
 
 config FDT_RESET_GPIO
 	bool "GPIO FDT reset driver"
+	depends on FDT_GPIO
 	default n
 
 config FDT_RESET_HTIF
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index c9c221e..d56cef2 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -1,3 +1,5 @@
+CONFIG_FDT_GPIO=y
+CONFIG_FDT_GPIO_SIFIVE=y
 CONFIG_FDT_I2C=y
 CONFIG_FDT_I2C_SIFIVE=y
 CONFIG_FDT_IPI=y
-- 
2.34.1



From apatel at ventanamicro.com  Tue Jul 19 06:18:34 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Tue, 19 Jul 2022 18:48:34 +0530
Subject: [PATCH v3 12/15] lib: utils/fdt: Use kconfig for enabling/disabling
In-Reply-To: <20220719131837.2389630-1-apatel@ventanamicro.com>
References: <20220719131837.2389630-1-apatel@ventanamicro.com>
Message-ID: <20220719131837.2389630-13-apatel@ventanamicro.com>

We update FDT support makefile to use kconfig for enabling/disabling.
To avoid compilation errors, we also enable FDT for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 include/sbi_utils/fdt/fdt_domain.h             |  9 +++++++++
 include/sbi_utils/fdt/fdt_pmu.h                | 10 ++++++++++
 lib/utils/Kconfig                              |  4 ++++
 lib/utils/fdt/Kconfig                          | 18 ++++++++++++++++++
 lib/utils/fdt/objects.mk                       |  8 ++++----
 lib/utils/gpio/Kconfig                         |  1 +
 lib/utils/i2c/Kconfig                          |  1 +
 lib/utils/ipi/Kconfig                          |  1 +
 lib/utils/irqchip/Kconfig                      |  1 +
 {platform/generic => lib/utils/libfdt}/Kconfig |  4 ++--
 lib/utils/libfdt/objects.mk                    |  4 ++--
 lib/utils/reset/Kconfig                        |  1 +
 lib/utils/serial/Kconfig                       |  1 +
 lib/utils/timer/Kconfig                        |  1 +
 platform/andes/ae350/Kconfig                   |  1 +
 platform/fpga/ariane/Kconfig                   |  1 +
 platform/fpga/openpiton/Kconfig                |  1 +
 platform/generic/Kconfig                       |  3 +++
 platform/kendryte/k210/Kconfig                 |  1 +
 platform/nuclei/ux600/Kconfig                  |  1 +
 platform/nuclei/ux600/platform.c               |  1 -
 21 files changed, 64 insertions(+), 9 deletions(-)
 create mode 100644 lib/utils/fdt/Kconfig
 copy {platform/generic => lib/utils/libfdt}/Kconfig (57%)

diff --git a/include/sbi_utils/fdt/fdt_domain.h b/include/sbi_utils/fdt/fdt_domain.h
index 5448eb4..ee09d1b 100644
--- a/include/sbi_utils/fdt/fdt_domain.h
+++ b/include/sbi_utils/fdt/fdt_domain.h
@@ -13,6 +13,8 @@
 
 #include <sbi/sbi_types.h>
 
+#ifdef CONFIG_FDT_DOMAIN
+
 struct sbi_domain;
 
 /**
@@ -70,4 +72,11 @@ void fdt_domain_fixup(void *fdt);
  */
 int fdt_domains_populate(void *fdt);
 
+#else
+
+static inline void fdt_domain_fixup(void *fdt) { }
+static inline int fdt_domains_populate(void *fdt) { return 0; }
+
+#endif
+
 #endif /* __FDT_DOMAIN_H__ */
diff --git a/include/sbi_utils/fdt/fdt_pmu.h b/include/sbi_utils/fdt/fdt_pmu.h
index 2fa01ed..c65cad7 100644
--- a/include/sbi_utils/fdt/fdt_pmu.h
+++ b/include/sbi_utils/fdt/fdt_pmu.h
@@ -13,6 +13,8 @@
 
 #include <sbi/sbi_types.h>
 
+#ifdef CONFIG_FDT_PMU
+
 /**
  * Fix up the PMU node in the device tree
  *
@@ -43,4 +45,12 @@ int fdt_pmu_setup(void *fdt);
  */
 uint64_t fdt_pmu_get_select_value(uint32_t event_idx);
 
+#else
+
+static inline void fdt_pmu_fixup(void *fdt) { }
+static inline int fdt_pmu_setup(void *fdt) { return 0; }
+static inline uint64_t fdt_pmu_get_select_value(uint32_t event_idx) { return 0; }
+
+#endif
+
 #endif
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 673b02b..5a71e75 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -2,6 +2,8 @@
 
 menu "Utils and Drivers Support"
 
+source "$(OPENSBI_SRC_DIR)/lib/utils/fdt/Kconfig"
+
 source "$(OPENSBI_SRC_DIR)/lib/utils/gpio/Kconfig"
 
 source "$(OPENSBI_SRC_DIR)/lib/utils/i2c/Kconfig"
@@ -10,6 +12,8 @@ source "$(OPENSBI_SRC_DIR)/lib/utils/ipi/Kconfig"
 
 source "$(OPENSBI_SRC_DIR)/lib/utils/irqchip/Kconfig"
 
+source "$(OPENSBI_SRC_DIR)/lib/utils/libfdt/Kconfig"
+
 source "$(OPENSBI_SRC_DIR)/lib/utils/reset/Kconfig"
 
 source "$(OPENSBI_SRC_DIR)/lib/utils/serial/Kconfig"
diff --git a/lib/utils/fdt/Kconfig b/lib/utils/fdt/Kconfig
new file mode 100644
index 0000000..23b003b
--- /dev/null
+++ b/lib/utils/fdt/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menuconfig FDT
+	bool "Flattened Device Tree (FDT) support"
+	select LIBFDT
+	default n
+
+if FDT
+
+config FDT_DOMAIN
+	bool "FDT domain support"
+	default n
+
+config FDT_PMU
+	bool "FDT performance monitoring unit (PMU) support"
+	default n
+
+endif
diff --git a/lib/utils/fdt/objects.mk b/lib/utils/fdt/objects.mk
index 03800f9..5cede81 100644
--- a/lib/utils/fdt/objects.mk
+++ b/lib/utils/fdt/objects.mk
@@ -4,7 +4,7 @@
 # Copyright (C) 2020 Bin Meng <bmeng.cn at gmail.com>
 #
 
-libsbiutils-objs-y += fdt/fdt_domain.o
-libsbiutils-objs-y += fdt/fdt_pmu.o
-libsbiutils-objs-y += fdt/fdt_helper.o
-libsbiutils-objs-y += fdt/fdt_fixup.o
+libsbiutils-objs-$(CONFIG_FDT_DOMAIN) += fdt/fdt_domain.o
+libsbiutils-objs-$(CONFIG_FDT_PMU) += fdt/fdt_pmu.o
+libsbiutils-objs-$(CONFIG_FDT) += fdt/fdt_helper.o
+libsbiutils-objs-$(CONFIG_FDT) += fdt/fdt_fixup.o
diff --git a/lib/utils/gpio/Kconfig b/lib/utils/gpio/Kconfig
index daf7f91..38a9d75 100644
--- a/lib/utils/gpio/Kconfig
+++ b/lib/utils/gpio/Kconfig
@@ -4,6 +4,7 @@ menu "GPIO Support"
 
 config FDT_GPIO
 	bool "FDT based GPIO drivers"
+	depends on FDT
 	select GPIO
 	default n
 
diff --git a/lib/utils/i2c/Kconfig b/lib/utils/i2c/Kconfig
index 16c4453..46a3454 100644
--- a/lib/utils/i2c/Kconfig
+++ b/lib/utils/i2c/Kconfig
@@ -4,6 +4,7 @@ menu "I2C Support"
 
 config FDT_I2C
 	bool "FDT based I2C drivers"
+	depends on FDT
 	select I2C
 	default n
 
diff --git a/lib/utils/ipi/Kconfig b/lib/utils/ipi/Kconfig
index c0634d3..efc8960 100644
--- a/lib/utils/ipi/Kconfig
+++ b/lib/utils/ipi/Kconfig
@@ -4,6 +4,7 @@ menu "IPI Device Support"
 
 config FDT_IPI
 	bool "FDT based ipi drivers"
+	depends on FDT
 	default n
 
 if FDT_IPI
diff --git a/lib/utils/irqchip/Kconfig b/lib/utils/irqchip/Kconfig
index 6a7e297..7a71388 100644
--- a/lib/utils/irqchip/Kconfig
+++ b/lib/utils/irqchip/Kconfig
@@ -4,6 +4,7 @@ menu "Interrupt Controller Support"
 
 config FDT_IRQCHIP
 	bool "FDT based interrupt controller drivers"
+	depends on FDT
 	default n
 
 if FDT_IRQCHIP
diff --git a/platform/generic/Kconfig b/lib/utils/libfdt/Kconfig
similarity index 57%
copy from platform/generic/Kconfig
copy to lib/utils/libfdt/Kconfig
index 3eab282..d1cecf8 100644
--- a/platform/generic/Kconfig
+++ b/lib/utils/libfdt/Kconfig
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: BSD-2-Clause
 
-config PLATFORM_GENERIC
+config LIBFDT
 	bool
-	default y
+	default n
diff --git a/lib/utils/libfdt/objects.mk b/lib/utils/libfdt/objects.mk
index 8c060df..a5b4001 100644
--- a/lib/utils/libfdt/objects.mk
+++ b/lib/utils/libfdt/objects.mk
@@ -12,5 +12,5 @@ libfdt_files = fdt.o fdt_addresses.o fdt_check.o fdt_empty_tree.o fdt_ro.o fdt_r
 $(foreach file, $(libfdt_files), \
         $(eval CFLAGS_$(file) = -I$(src)/../../utils/libfdt))
 
-libsbiutils-objs-y += $(addprefix libfdt/,$(libfdt_files))
-libsbiutils-genflags-y  += -I$(libsbiutils_dir)/libfdt/
+libsbiutils-objs-$(CONFIG_LIBFDT) += $(addprefix libfdt/,$(libfdt_files))
+libsbiutils-genflags-$(CONFIG_LIBFDT)  += -I$(libsbiutils_dir)/libfdt/
diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig
index 71996cb..2e83ff6 100644
--- a/lib/utils/reset/Kconfig
+++ b/lib/utils/reset/Kconfig
@@ -4,6 +4,7 @@ menu "System Reset Support"
 
 config FDT_RESET
 	bool "FDT based reset drivers"
+	depends on FDT
 	default n
 
 if FDT_RESET
diff --git a/lib/utils/serial/Kconfig b/lib/utils/serial/Kconfig
index bf37e34..152060d 100644
--- a/lib/utils/serial/Kconfig
+++ b/lib/utils/serial/Kconfig
@@ -4,6 +4,7 @@ menu "Serial Device Support"
 
 config FDT_SERIAL
 	bool "FDT based serial drivers"
+	depends on FDT
 	default n
 
 if FDT_SERIAL
diff --git a/lib/utils/timer/Kconfig b/lib/utils/timer/Kconfig
index 0a1c727..23c48c5 100644
--- a/lib/utils/timer/Kconfig
+++ b/lib/utils/timer/Kconfig
@@ -4,6 +4,7 @@ menu "Timer Device Support"
 
 config FDT_TIMER
 	bool "FDT based timer drivers"
+	depends on FDT
 	default n
 
 if FDT_TIMER
diff --git a/platform/andes/ae350/Kconfig b/platform/andes/ae350/Kconfig
index 87deab0..d57f19f 100644
--- a/platform/andes/ae350/Kconfig
+++ b/platform/andes/ae350/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_ANDES_AE350
 	bool
+	select FDT
 	select IRQCHIP_PLIC
 	select SERIAL_UART8250
 	default y
diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
index 5a720d9..e1f118b 100644
--- a/platform/fpga/ariane/Kconfig
+++ b/platform/fpga/ariane/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_ARIANE_FPGA
 	bool
+	select FDT
 	select IPI_MSWI
 	select IRQCHIP_PLIC
 	select SERIAL_UART8250
diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
index 973906e..bc9c86e 100644
--- a/platform/fpga/openpiton/Kconfig
+++ b/platform/fpga/openpiton/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_OPENPITON_FPGA
 	bool
+	select FDT
 	select IPI_MSWI
 	select IRQCHIP_PLIC
 	select SERIAL_UART8250
diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
index 3eab282..c1a77b3 100644
--- a/platform/generic/Kconfig
+++ b/platform/generic/Kconfig
@@ -2,4 +2,7 @@
 
 config PLATFORM_GENERIC
 	bool
+	select FDT
+	select FDT_DOMAIN
+	select FDT_PMU
 	default y
diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
index 2a1daaa..5bf5973 100644
--- a/platform/kendryte/k210/Kconfig
+++ b/platform/kendryte/k210/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_KENDRYTE_K210
 	bool
+	select FDT
 	select IPI_MSWI
 	select IRQCHIP_PLIC
 	select SERIAL_SIFIVE
diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
index 464802e..84d74e8 100644
--- a/platform/nuclei/ux600/Kconfig
+++ b/platform/nuclei/ux600/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_NUCLEI_UX600
 	bool
+	select FDT
 	select IPI_MSWI
 	select IRQCHIP_PLIC
 	select SERIAL_SIFIVE
diff --git a/platform/nuclei/ux600/platform.c b/platform/nuclei/ux600/platform.c
index 5498a1f..2b02734 100644
--- a/platform/nuclei/ux600/platform.c
+++ b/platform/nuclei/ux600/platform.c
@@ -8,7 +8,6 @@
  *   hqfang <578567190 at qq.com>
  */
 
-#include <libfdt.h>
 #include <sbi/riscv_asm.h>
 #include <sbi/riscv_io.h>
 #include <sbi/riscv_encoding.h>
-- 
2.34.1



From apatel at ventanamicro.com  Tue Jul 19 06:18:35 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Tue, 19 Jul 2022 18:48:35 +0530
Subject: [PATCH v3 13/15] platform: generic: Use kconfig for enabling/disabling overrides
In-Reply-To: <20220719131837.2389630-1-apatel@ventanamicro.com>
References: <20220719131837.2389630-1-apatel@ventanamicro.com>
Message-ID: <20220719131837.2389630-14-apatel@ventanamicro.com>

We update generic platform to use kconfig for enabling/disabling
platform overrides. We also enable all platform overrides in generic
platform defconfig.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 platform/generic/Kconfig              | 18 ++++++++++++++++++
 platform/generic/allwinner/objects.mk |  4 ++--
 platform/generic/configs/defconfig    |  3 +++
 platform/generic/sifive/objects.mk    |  8 ++++----
 4 files changed, 27 insertions(+), 6 deletions(-)

diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
index c1a77b3..c76bd12 100644
--- a/platform/generic/Kconfig
+++ b/platform/generic/Kconfig
@@ -6,3 +6,21 @@ config PLATFORM_GENERIC
 	select FDT_DOMAIN
 	select FDT_PMU
 	default y
+
+if PLATFORM_GENERIC
+
+config PLATFORM_ALLWINNER_D1
+	bool "Allwinner D1 support"
+	depends on FDT_IRQCHIP_PLIC
+	default n
+
+config PLATFORM_SIFIVE_FU540
+	bool "SiFive FU540 support"
+	default n
+
+config PLATFORM_SIFIVE_FU740
+	bool "SiFive FU740 support"
+	depends on FDT_RESET && FDT_I2C
+	default n
+
+endif
diff --git a/platform/generic/allwinner/objects.mk b/platform/generic/allwinner/objects.mk
index 9e36ab6..447cdf1 100644
--- a/platform/generic/allwinner/objects.mk
+++ b/platform/generic/allwinner/objects.mk
@@ -2,5 +2,5 @@
 # SPDX-License-Identifier: BSD-2-Clause
 #
 
-carray-platform_override_modules-y += sun20i_d1
-platform-objs-y += allwinner/sun20i-d1.o
+carray-platform_override_modules-$(CONFIG_PLATFORM_ALLWINNER_D1) += sun20i_d1
+platform-objs-$(CONFIG_PLATFORM_ALLWINNER_D1) += allwinner/sun20i-d1.o
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index d56cef2..2a75394 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -1,3 +1,6 @@
+CONFIG_PLATFORM_ALLWINNER_D1=y
+CONFIG_PLATFORM_SIFIVE_FU540=y
+CONFIG_PLATFORM_SIFIVE_FU740=y
 CONFIG_FDT_GPIO=y
 CONFIG_FDT_GPIO_SIFIVE=y
 CONFIG_FDT_I2C=y
diff --git a/platform/generic/sifive/objects.mk b/platform/generic/sifive/objects.mk
index c17e2df..d75e444 100644
--- a/platform/generic/sifive/objects.mk
+++ b/platform/generic/sifive/objects.mk
@@ -2,8 +2,8 @@
 # SPDX-License-Identifier: BSD-2-Clause
 #
 
-carray-platform_override_modules-y += sifive_fu540
-platform-objs-y += sifive/fu540.o
+carray-platform_override_modules-$(CONFIG_PLATFORM_SIFIVE_FU540) += sifive_fu540
+platform-objs-$(CONFIG_PLATFORM_SIFIVE_FU540) += sifive/fu540.o
 
-carray-platform_override_modules-y += sifive_fu740
-platform-objs-y += sifive/fu740.o
+carray-platform_override_modules-$(CONFIG_PLATFORM_SIFIVE_FU740) += sifive_fu740
+platform-objs-$(CONFIG_PLATFORM_SIFIVE_FU740) += sifive/fu740.o
-- 
2.34.1



From apatel at ventanamicro.com  Tue Jul 19 06:18:36 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Tue, 19 Jul 2022 18:48:36 +0530
Subject: [PATCH v3 14/15] platform: Remove redundant config.mk from all platforms
In-Reply-To: <20220719131837.2389630-1-apatel@ventanamicro.com>
References: <20220719131837.2389630-1-apatel@ventanamicro.com>
Message-ID: <20220719131837.2389630-15-apatel@ventanamicro.com>

The options defined in config.mk can be specified in objects.mk of each
platform so let us remove config.mk from all platforms.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 Makefile                           |  4 +-
 platform/andes/ae350/config.mk     | 36 --------------
 platform/andes/ae350/objects.mk    | 29 +++++++++++
 platform/fpga/ariane/config.mk     | 36 --------------
 platform/fpga/ariane/objects.mk    | 35 ++++++++++++++
 platform/fpga/openpiton/config.mk  | 35 --------------
 platform/fpga/openpiton/objects.mk | 35 ++++++++++++++
 platform/generic/config.mk         | 40 ----------------
 platform/generic/objects.mk        | 33 +++++++++++++
 platform/kendryte/k210/config.mk   | 19 --------
 platform/kendryte/k210/objects.mk  | 12 +++++
 platform/nuclei/ux600/config.mk    | 30 ------------
 platform/nuclei/ux600/objects.mk   | 22 +++++++++
 platform/template/config.mk        | 77 ------------------------------
 platform/template/objects.mk       | 72 ++++++++++++++++++++++++++++
 platform/template/platform.c       |  2 +-
 16 files changed, 240 insertions(+), 277 deletions(-)
 delete mode 100644 platform/andes/ae350/config.mk
 delete mode 100644 platform/fpga/ariane/config.mk
 delete mode 100644 platform/fpga/openpiton/config.mk
 delete mode 100644 platform/generic/config.mk
 delete mode 100644 platform/kendryte/k210/config.mk
 delete mode 100644 platform/nuclei/ux600/config.mk
 delete mode 100644 platform/template/config.mk

diff --git a/Makefile b/Makefile
index 8c9e8df..ac01fb0 100644
--- a/Makefile
+++ b/Makefile
@@ -204,7 +204,7 @@ firmware-object-mks=$(shell if [ -d $(firmware_dir) ]; then find $(firmware_dir)
 .PHONY: all
 all:
 
-# Include platform specific .config and config.mk
+# Include platform specific .config
 ifdef PLATFORM
 .PHONY: menuconfig
 menuconfig: $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
@@ -228,8 +228,6 @@ $(KCONFIG_AUTOCMD): $(KCONFIG_CONFIG)
 
 include $(KCONFIG_CONFIG)
 include $(KCONFIG_AUTOCMD)
-
-include $(platform_src_dir)/config.mk
 endif
 
 # Include all object.mk files
diff --git a/platform/andes/ae350/config.mk b/platform/andes/ae350/config.mk
deleted file mode 100644
index f555ef5..0000000
--- a/platform/andes/ae350/config.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2019 Andes Technology Corporation
-#
-# Authors:
-#   Zong Li <zong at andestech.com>
-#   Nylon Chen <nylon7 at andestech.com>
-
-# Compiler flags
-platform-cppflags-y =
-platform-cflags-y =
-platform-asflags-y =
-platform-ldflags-y =
-
-# Blobs to build
-FW_TEXT_START=0x00000000
-
-FW_DYNAMIC=y
-
-FW_JUMP=y
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-  FW_JUMP_ADDR=0x400000
-else
-  FW_JUMP_ADDR=0x200000
-endif
-FW_JUMP_FDT_ADDR=0x2000000
-
-FW_PAYLOAD=y
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-  FW_PAYLOAD_OFFSET=0x400000
-else
-  FW_PAYLOAD_OFFSET=0x200000
-endif
-
-FW_PAYLOAD_FDT_ADDR=0x2000000
diff --git a/platform/andes/ae350/objects.mk b/platform/andes/ae350/objects.mk
index 5369677..80f0737 100644
--- a/platform/andes/ae350/objects.mk
+++ b/platform/andes/ae350/objects.mk
@@ -8,4 +8,33 @@
 #   Nylon Chen <nylon7 at andestech.com>
 #
 
+# Compiler flags
+platform-cppflags-y =
+platform-cflags-y =
+platform-asflags-y =
+platform-ldflags-y =
+
+# Objects to build
 platform-objs-y += cache.o platform.o plicsw.o plmt.o
+
+# Blobs to build
+FW_TEXT_START=0x00000000
+
+FW_DYNAMIC=y
+
+FW_JUMP=y
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+  FW_JUMP_ADDR=0x400000
+else
+  FW_JUMP_ADDR=0x200000
+endif
+FW_JUMP_FDT_ADDR=0x2000000
+
+FW_PAYLOAD=y
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+  FW_PAYLOAD_OFFSET=0x400000
+else
+  FW_PAYLOAD_OFFSET=0x200000
+endif
+
+FW_PAYLOAD_FDT_ADDR=0x2000000
diff --git a/platform/fpga/ariane/config.mk b/platform/fpga/ariane/config.mk
deleted file mode 100644
index 3556461..0000000
--- a/platform/fpga/ariane/config.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (C) 2019 FORTH-ICS/CARV
-#		Panagiotis Peristerakis <perister at ics.forth.gr>
-#
-
-#for more infos, check out /platform/template/config.mk
-
-PLATFORM_RISCV_XLEN = 64
-
-# Blobs to build
-FW_TEXT_START=0x80000000
-FW_JUMP=n
-
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
- # This needs to be 4MB aligned for 32-bit support
- FW_JUMP_ADDR=0x80400000
- else
- # This needs to be 2MB aligned for 64-bit support
- FW_JUMP_ADDR=0x80200000
- endif
-FW_JUMP_FDT_ADDR=0x82200000
-
-# Firmware with payload configuration.
-FW_PAYLOAD=y
-
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-# This needs to be 4MB aligned for 32-bit support
-  FW_PAYLOAD_OFFSET=0x400000
-else
-# This needs to be 2MB aligned for 64-bit support
-  FW_PAYLOAD_OFFSET=0x200000
-endif
-FW_PAYLOAD_FDT_ADDR=0x82200000
-FW_PAYLOAD_ALIGN=0x1000
diff --git a/platform/fpga/ariane/objects.mk b/platform/fpga/ariane/objects.mk
index 814e6da..83581ac 100644
--- a/platform/fpga/ariane/objects.mk
+++ b/platform/fpga/ariane/objects.mk
@@ -5,4 +5,39 @@
 #		Panagiotis Peristerakis <perister at ics.forth.gr>
 #
 
+# Compiler flags
+platform-cppflags-y =
+platform-cflags-y =
+platform-asflags-y =
+platform-ldflags-y =
+
+# Object to build
 platform-objs-y += platform.o
+
+PLATFORM_RISCV_XLEN = 64
+
+# Blobs to build
+FW_TEXT_START=0x80000000
+FW_JUMP=n
+
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+ # This needs to be 4MB aligned for 32-bit support
+ FW_JUMP_ADDR=0x80400000
+ else
+ # This needs to be 2MB aligned for 64-bit support
+ FW_JUMP_ADDR=0x80200000
+ endif
+FW_JUMP_FDT_ADDR=0x82200000
+
+# Firmware with payload configuration.
+FW_PAYLOAD=y
+
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+# This needs to be 4MB aligned for 32-bit support
+  FW_PAYLOAD_OFFSET=0x400000
+else
+# This needs to be 2MB aligned for 64-bit support
+  FW_PAYLOAD_OFFSET=0x200000
+endif
+FW_PAYLOAD_FDT_ADDR=0x82200000
+FW_PAYLOAD_ALIGN=0x1000
diff --git a/platform/fpga/openpiton/config.mk b/platform/fpga/openpiton/config.mk
deleted file mode 100644
index a969b25..0000000
--- a/platform/fpga/openpiton/config.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2020 Western Digital Corporation or its affiliates.
-#
-
-#for more infos, check out /platform/template/config.mk
-
-PLATFORM_RISCV_XLEN = 64
-
-# Blobs to build
-FW_TEXT_START=0x80000000
-FW_JUMP=n
-
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
- # This needs to be 4MB aligned for 32-bit support
- FW_JUMP_ADDR=0x80400000
- else
- # This needs to be 2MB aligned for 64-bit support
- FW_JUMP_ADDR=0x80200000
- endif
-FW_JUMP_FDT_ADDR=0x82200000
-
-# Firmware with payload configuration.
-FW_PAYLOAD=y
-
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-# This needs to be 4MB aligned for 32-bit support
-  FW_PAYLOAD_OFFSET=0x400000
-else
-# This needs to be 2MB aligned for 64-bit support
-  FW_PAYLOAD_OFFSET=0x200000
-endif
-FW_PAYLOAD_FDT_ADDR=0x82200000
-FW_PAYLOAD_ALIGN=0x1000
diff --git a/platform/fpga/openpiton/objects.mk b/platform/fpga/openpiton/objects.mk
index 30a3c4f..c8c345a 100644
--- a/platform/fpga/openpiton/objects.mk
+++ b/platform/fpga/openpiton/objects.mk
@@ -4,4 +4,39 @@
 # Copyright (c) 2020 Western Digital Corporation or its affiliates.
 #
 
+# Compiler flags
+platform-cppflags-y =
+platform-cflags-y =
+platform-asflags-y =
+platform-ldflags-y =
+
+# Objects to build
 platform-objs-y += platform.o
+
+PLATFORM_RISCV_XLEN = 64
+
+# Blobs to build
+FW_TEXT_START=0x80000000
+FW_JUMP=n
+
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+ # This needs to be 4MB aligned for 32-bit support
+ FW_JUMP_ADDR=0x80400000
+ else
+ # This needs to be 2MB aligned for 64-bit support
+ FW_JUMP_ADDR=0x80200000
+ endif
+FW_JUMP_FDT_ADDR=0x82200000
+
+# Firmware with payload configuration.
+FW_PAYLOAD=y
+
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+# This needs to be 4MB aligned for 32-bit support
+  FW_PAYLOAD_OFFSET=0x400000
+else
+# This needs to be 2MB aligned for 64-bit support
+  FW_PAYLOAD_OFFSET=0x200000
+endif
+FW_PAYLOAD_FDT_ADDR=0x82200000
+FW_PAYLOAD_ALIGN=0x1000
diff --git a/platform/generic/config.mk b/platform/generic/config.mk
deleted file mode 100644
index 8151974..0000000
--- a/platform/generic/config.mk
+++ /dev/null
@@ -1,40 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2020 Western Digital Corporation or its affiliates.
-#
-# Authors:
-#   Anup Patel <anup.patel at wdc.com>
-#
-
-# Compiler flags
-platform-cppflags-y =
-platform-cflags-y =
-platform-asflags-y =
-platform-ldflags-y =
-
-# Command for platform specific "make run"
-platform-runcmd = qemu-system-riscv$(PLATFORM_RISCV_XLEN) -M virt -m 256M \
-  -nographic -bios $(build_dir)/platform/generic/firmware/fw_payload.elf
-
-# Blobs to build
-FW_TEXT_START=0x80000000
-FW_DYNAMIC=y
-FW_JUMP=y
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-  # This needs to be 4MB aligned for 32-bit system
-  FW_JUMP_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x400000)))
-else
-  # This needs to be 2MB aligned for 64-bit system
-  FW_JUMP_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x200000)))
-endif
-FW_JUMP_FDT_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x2200000)))
-FW_PAYLOAD=y
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-  # This needs to be 4MB aligned for 32-bit system
-  FW_PAYLOAD_OFFSET=0x400000
-else
-  # This needs to be 2MB aligned for 64-bit system
-  FW_PAYLOAD_OFFSET=0x200000
-endif
-FW_PAYLOAD_FDT_ADDR=$(FW_JUMP_FDT_ADDR)
diff --git a/platform/generic/objects.mk b/platform/generic/objects.mk
index 4907754..136853e 100644
--- a/platform/generic/objects.mk
+++ b/platform/generic/objects.mk
@@ -7,5 +7,38 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
+# Compiler flags
+platform-cppflags-y =
+platform-cflags-y =
+platform-asflags-y =
+platform-ldflags-y =
+
+# Command for platform specific "make run"
+platform-runcmd = qemu-system-riscv$(PLATFORM_RISCV_XLEN) -M virt -m 256M \
+  -nographic -bios $(build_dir)/platform/generic/firmware/fw_payload.elf
+
+# Objects to build
 platform-objs-y += platform.o
 platform-objs-y += platform_override_modules.o
+
+# Blobs to build
+FW_TEXT_START=0x80000000
+FW_DYNAMIC=y
+FW_JUMP=y
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+  # This needs to be 4MB aligned for 32-bit system
+  FW_JUMP_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x400000)))
+else
+  # This needs to be 2MB aligned for 64-bit system
+  FW_JUMP_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x200000)))
+endif
+FW_JUMP_FDT_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x2200000)))
+FW_PAYLOAD=y
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+  # This needs to be 4MB aligned for 32-bit system
+  FW_PAYLOAD_OFFSET=0x400000
+else
+  # This needs to be 2MB aligned for 64-bit system
+  FW_PAYLOAD_OFFSET=0x200000
+endif
+FW_PAYLOAD_FDT_ADDR=$(FW_JUMP_FDT_ADDR)
diff --git a/platform/kendryte/k210/config.mk b/platform/kendryte/k210/config.mk
deleted file mode 100644
index 8a9b814..0000000
--- a/platform/kendryte/k210/config.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2019 Western Digital Corporation or its affiliates.
-#
-# Authors:
-#   Damien Le Moal <damien.lemoal at wdc.com>
-#
-
-# Compiler flags
-platform-cppflags-y =
-platform-cflags-y =
-platform-asflags-y =
-platform-ldflags-y =
-
-# Blobs to build
-FW_TEXT_START=0x80000000
-FW_PAYLOAD=y
-FW_PAYLOAD_ALIGN=0x1000
diff --git a/platform/kendryte/k210/objects.mk b/platform/kendryte/k210/objects.mk
index b74da74..1bfb898 100644
--- a/platform/kendryte/k210/objects.mk
+++ b/platform/kendryte/k210/objects.mk
@@ -7,8 +7,20 @@
 #   Damien Le Moal <damien.lemoal at wdc.com>
 #
 
+# Compiler flags
+platform-cppflags-y =
+platform-cflags-y =
+platform-asflags-y =
+platform-ldflags-y =
+
+# Objects to build
 platform-objs-y += platform.o
 
 platform-objs-y += k210.o
 platform-varprefix-k210.o = dt_k210
 platform-padding-k210.o = 2048
+
+# Blobs to build
+FW_TEXT_START=0x80000000
+FW_PAYLOAD=y
+FW_PAYLOAD_ALIGN=0x1000
diff --git a/platform/nuclei/ux600/config.mk b/platform/nuclei/ux600/config.mk
deleted file mode 100644
index dddcc4e..0000000
--- a/platform/nuclei/ux600/config.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2020 Nuclei Corporation or its affiliates.
-#
-# Authors:
-#   lujun <lujun at nucleisys.com>
-#   hqfang <578567190 at qq.com>
-#
-
-# Compiler flags
-platform-cppflags-y =
-platform-cflags-y =
-platform-asflags-y =
-platform-ldflags-y =
-
-# Command for platform specific "make run"
-platform-runcmd = xl_spike \
-  $(build_dir)/platform/nuclei/ux600/firmware/fw_payload.elf
-
-# Blobs to build
-FW_TEXT_START=0xA0000000
-FW_DYNAMIC=y
-FW_JUMP=y
-
-FW_JUMP_ADDR=0xA0200000
-FW_JUMP_FDT_ADDR=0xA8000000
-FW_PAYLOAD=y
-FW_PAYLOAD_OFFSET=0x200000
-FW_PAYLOAD_FDT_ADDR=0xA8000000
diff --git a/platform/nuclei/ux600/objects.mk b/platform/nuclei/ux600/objects.mk
index 8c36c90..7c429e0 100644
--- a/platform/nuclei/ux600/objects.mk
+++ b/platform/nuclei/ux600/objects.mk
@@ -8,4 +8,26 @@
 #   hqfang <578567190 at qq.com>
 #
 
+# Compiler flags
+platform-cppflags-y =
+platform-cflags-y =
+platform-asflags-y =
+platform-ldflags-y =
+
+# Command for platform specific "make run"
+platform-runcmd = xl_spike \
+  $(build_dir)/platform/nuclei/ux600/firmware/fw_payload.elf
+
+# Objects to build
 platform-objs-y += platform.o
+
+# Blobs to build
+FW_TEXT_START=0xA0000000
+FW_DYNAMIC=y
+FW_JUMP=y
+
+FW_JUMP_ADDR=0xA0200000
+FW_JUMP_FDT_ADDR=0xA8000000
+FW_PAYLOAD=y
+FW_PAYLOAD_OFFSET=0x200000
+FW_PAYLOAD_FDT_ADDR=0xA8000000
diff --git a/platform/template/config.mk b/platform/template/config.mk
deleted file mode 100644
index f817342..0000000
--- a/platform/template/config.mk
+++ /dev/null
@@ -1,77 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2019 Western Digital Corporation or its affiliates.
-#
-
-# Compiler pre-processor flags
-platform-cppflags-y =
-
-# C Compiler and assembler flags.
-platform-cflags-y =
-platform-asflags-y =
-
-# Linker flags: additional libraries and object files that the platform
-# code needs can be added here
-platform-ldflags-y =
-
-#
-# Command for platform specific "make run"
-# Useful for development and debugging on plaftform simulator (such as QEMU)
-#
-# platform-runcmd = your_platform_run.sh
-
-#
-# Platform RISC-V XLEN, ABI, ISA and Code Model configuration.
-# These are optional parameters but platforms can optionaly provide it.
-# Some of these are guessed based on GCC compiler capabilities
-#
-# PLATFORM_RISCV_XLEN = 64
-# PLATFORM_RISCV_ABI = lp64
-# PLATFORM_RISCV_ISA = rv64imafdc
-# PLATFORM_RISCV_CODE_MODEL = medany
-
-# Firmware load address configuration. This is mandatory.
-FW_TEXT_START=0x80000000
-
-# Optional parameter for path to external FDT
-# FW_FDT_PATH="path to platform flattened device tree file"
-
-#
-# Dynamic firmware configuration.
-# Optional parameters are commented out. Uncomment and define these parameters
-# as needed.
-#
-FW_DYNAMIC=<y|n>
-
-#
-# Jump firmware configuration.
-# Optional parameters are commented out. Uncomment and define these parameters
-# as needed.
-#
-FW_JUMP=<y|n>
-# This needs to be 4MB aligned for 32-bit support
-# This needs to be 2MB aligned for 64-bit support
-# ifeq ($(PLATFORM_RISCV_XLEN), 32)
-# FW_JUMP_ADDR=0x80400000
-# else
-# FW_JUMP_ADDR=0x80200000
-# endif
-# FW_JUMP_FDT_ADDR=0x82200000
-
-#
-# Firmware with payload configuration.
-# Optional parameters are commented out. Uncomment and define these parameters
-# as needed.
-#
-FW_PAYLOAD=<y|n>
-# This needs to be 4MB aligned for 32-bit support
-# This needs to be 2MB aligned for 64-bit support
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-FW_PAYLOAD_OFFSET=0x400000
-else
-FW_PAYLOAD_OFFSET=0x200000
-endif
-# FW_PAYLOAD_ALIGN=0x1000
-# FW_PAYLOAD_PATH="path to next boot stage binary image file"
-# FW_PAYLOAD_FDT_ADDR=0x82200000
diff --git a/platform/template/objects.mk b/platform/template/objects.mk
index caaec2b..0025928 100644
--- a/platform/template/objects.mk
+++ b/platform/template/objects.mk
@@ -4,6 +4,33 @@
 # Copyright (c) 2019 Western Digital Corporation or its affiliates.
 #
 
+# Compiler pre-processor flags
+platform-cppflags-y =
+
+# C Compiler and assembler flags.
+platform-cflags-y =
+platform-asflags-y =
+
+# Linker flags: additional libraries and object files that the platform
+# code needs can be added here
+platform-ldflags-y =
+
+#
+# Command for platform specific "make run"
+# Useful for development and debugging on plaftform simulator (such as QEMU)
+#
+# platform-runcmd = your_platform_run.sh
+
+#
+# Platform RISC-V XLEN, ABI, ISA and Code Model configuration.
+# These are optional parameters but platforms can optionaly provide it.
+# Some of these are guessed based on GCC compiler capabilities
+#
+# PLATFORM_RISCV_XLEN = 64
+# PLATFORM_RISCV_ABI = lp64
+# PLATFORM_RISCV_ISA = rv64imafdc
+# PLATFORM_RISCV_CODE_MODEL = medany
+
 # Space separated list of object file names to be compiled for the platform
 platform-objs-y += platform.o
 
@@ -13,3 +40,48 @@ platform-objs-y += platform.o
 # source file be in the form <dt file name>.dts
 #
 # platform-objs-y += <dt file name>.o
+
+# Firmware load address configuration. This is mandatory.
+FW_TEXT_START=0x80000000
+
+# Optional parameter for path to external FDT
+# FW_FDT_PATH="path to platform flattened device tree file"
+
+#
+# Dynamic firmware configuration.
+# Optional parameters are commented out. Uncomment and define these parameters
+# as needed.
+#
+FW_DYNAMIC=<y|n>
+
+#
+# Jump firmware configuration.
+# Optional parameters are commented out. Uncomment and define these parameters
+# as needed.
+#
+FW_JUMP=<y|n>
+# This needs to be 4MB aligned for 32-bit support
+# This needs to be 2MB aligned for 64-bit support
+# ifeq ($(PLATFORM_RISCV_XLEN), 32)
+# FW_JUMP_ADDR=0x80400000
+# else
+# FW_JUMP_ADDR=0x80200000
+# endif
+# FW_JUMP_FDT_ADDR=0x82200000
+
+#
+# Firmware with payload configuration.
+# Optional parameters are commented out. Uncomment and define these parameters
+# as needed.
+#
+FW_PAYLOAD=<y|n>
+# This needs to be 4MB aligned for 32-bit support
+# This needs to be 2MB aligned for 64-bit support
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+FW_PAYLOAD_OFFSET=0x400000
+else
+FW_PAYLOAD_OFFSET=0x200000
+endif
+# FW_PAYLOAD_ALIGN=0x1000
+# FW_PAYLOAD_PATH="path to next boot stage binary image file"
+# FW_PAYLOAD_FDT_ADDR=0x82200000
diff --git a/platform/template/platform.c b/platform/template/platform.c
index 5524847..f3802da 100644
--- a/platform/template/platform.c
+++ b/platform/template/platform.c
@@ -11,7 +11,7 @@
 
 /*
  * Include these files as needed.
- * See config.mk PLATFORM_xxx configuration parameters.
+ * See objects.mk PLATFORM_xxx configuration parameters.
  */
 #include <sbi_utils/ipi/aclint_mswi.h>
 #include <sbi_utils/irqchip/plic.h>
-- 
2.34.1



From apatel at ventanamicro.com  Tue Jul 19 06:18:37 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Tue, 19 Jul 2022 18:48:37 +0530
Subject: [PATCH v3 15/15] docs: Update documentation for kconfig support
In-Reply-To: <20220719131837.2389630-1-apatel@ventanamicro.com>
References: <20220719131837.2389630-1-apatel@ventanamicro.com>
Message-ID: <20220719131837.2389630-16-apatel@ventanamicro.com>

We update all documentation files to:
1) Remove references to platform specific config.mk file since it is
   has been removed.
2) Add details about platform specific configs/defconfig and Kconfig
   files mandatory for each platform.
3) Add required packages in top-level README.md

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 README.md                   | 25 +++++++++++++++++++++++--
 docs/firmware/fw_dynamic.md |  2 +-
 docs/firmware/fw_jump.md    |  4 ++--
 docs/firmware/fw_payload.md |  4 ++--
 docs/platform/platform.md   |  4 ++--
 docs/platform_guide.md      |  9 +++++----
 6 files changed, 35 insertions(+), 13 deletions(-)

diff --git a/README.md b/README.md
index 54fabe4..67d491d 100644
--- a/README.md
+++ b/README.md
@@ -92,8 +92,8 @@ N.B. Any S-mode boot loader (i.e. U-Boot) doesn't need to support HSM extension,
 as it doesn't need to boot all the harts. The operating system should be
 capable enough to bring up all other non-booting harts using HSM extension.
 
-Required Toolchain
-------------------
+Required Toolchain and Packages
+-------------------------------
 
 OpenSBI can be compiled natively or cross-compiled on a x86 host. For
 cross-compilation, you can build your own toolchain, download a prebuilt one
@@ -115,6 +115,14 @@ triple is used (e.g. *-target riscv64-unknown-elf*).
 Please note that only a 64-bit version of the toolchain is available in
 the Bootlin toolchain repository for now.
 
+In addition to a toolchain, OpenSBI also requires following packages on
+the host:
+
+1. device-tree-compiler: The device tree compiler for compiling device
+   tree sources (DTS files).
+2. python3: The python 3.0 (or higher) language support for various
+   scripts.
+
 Building and Installing the OpenSBI Platform-Independent Library
 ----------------------------------------------------------------
 
@@ -196,6 +204,19 @@ top-level make command line. These options, such as *PLATFORM_<xyz>* or
 *docs/platform/<platform_name>.md* files and
 *docs/firmware/<firmware_name>.md* files.
 
+All OpenSBI platforms support Kconfig style build-time configuration. Users
+can change the build-time configuration of a platform using a graphical
+interface as follows:
+```
+make PLATFORM=<platform_subdir> menuconfig
+```
+
+Alternately, an OpenSBI platform can have multiple default configurations
+and users can select a custom default configuration as follows:
+```
+make PLATFORM=<platform_subdir> PLATFORM_DEFCONFIG=<platform_custom_defconfig>
+```
+
 Building 32-bit / 64-bit OpenSBI Images
 ---------------------------------------
 By default, building OpenSBI generates 32-bit or 64-bit images based on the
diff --git a/docs/firmware/fw_dynamic.md b/docs/firmware/fw_dynamic.md
index 7b9b192..e251488 100644
--- a/docs/firmware/fw_dynamic.md
+++ b/docs/firmware/fw_dynamic.md
@@ -20,7 +20,7 @@ the booting stage binary to follow OpenSBI firmware.
 A platform can enable *FW_DYNAMIC* firmware using any of the following methods.
 
 1. Specifying `FW_DYNAMIC=y` on the top level `make` command line.
-2. Specifying `FW_DYNAMIC=y` in the target platform *config.mk* configuration
+2. Specifying `FW_DYNAMIC=y` in the target platform *objects.mk* configuration
 file.
 
 The compiled *FW_DYNAMIC* firmware ELF file is named *fw_dynamic.elf*. It's
diff --git a/docs/firmware/fw_jump.md b/docs/firmware/fw_jump.md
index eea3013..35a4301 100644
--- a/docs/firmware/fw_jump.md
+++ b/docs/firmware/fw_jump.md
@@ -15,7 +15,7 @@ and the booting stage binary to follow the OpenSBI firmware.
 A platform *FW_JUMP* firmware can be enabled by any of the following methods:
 
 1. Specifying `FW_JUMP=y` on the top level `make` command line.
-2. Specifying `FW_JUMP=y` in the target platform *config.mk* configuration file.
+2. Specifying `FW_JUMP=y` in the target platform *objects.mk* configuration file.
 
 The compiled *FW_JUMP* firmware ELF file is named *fw_jump.elf*. Its expanded
 image file is *fw_jump.bin*. Both files are created in the platform-specific
@@ -26,7 +26,7 @@ build directory under the *build/platform/<platform_subdir>/firmware* directory.
 
 To operate correctly, a *FW_JUMP* firmware requires some configuration
 parameters to be defined using either the top level `make` command line or the
-target platform *config.mk* configuration file. The possible parameters are as
+target platform *objects.mk* configuration file. The possible parameters are as
 follows:
 
 * **FW_JUMP_ADDR** - Address of the entry point of the booting stage to be
diff --git a/docs/firmware/fw_payload.md b/docs/firmware/fw_payload.md
index 0947448..3bb918f 100644
--- a/docs/firmware/fw_payload.md
+++ b/docs/firmware/fw_payload.md
@@ -20,7 +20,7 @@ Enabling *FW_PAYLOAD* compilation
 The *FW_PAYLOAD* firmware can be enabled by any of the following methods:
 
 1. Specifying `FW_PAYLOAD=y` on the top level `make` command line.
-2. Specifying `FW_PAYLOAD=y` in the target platform *config.mk* configuration
+2. Specifying `FW_PAYLOAD=y` in the target platform *objects.mk* configuration
    file.
 
 The compiled *FW_PAYLOAD* firmware ELF file is named *fw_jump.elf*. Its
@@ -33,7 +33,7 @@ Configuration Options
 
 A *FW_PAYLOAD* firmware is built according to configuration parameters and
 options. These configuration parameters can be defined using either the top
-level `make` command line or the target platform *config.mk* configuration
+level `make` command line or the target platform *objects.mk* configuration
 file. The parameters currently defined are as follows:
 
 * **FW_PAYLOAD_OFFSET** - Offset from *FW_TEXT_BASE* where the payload binary
diff --git a/docs/platform/platform.md b/docs/platform/platform.md
index cb4bec6..b726e1d 100644
--- a/docs/platform/platform.md
+++ b/docs/platform/platform.md
@@ -42,8 +42,8 @@ OpenSBI currently supports the following virtual and hardware platforms:
 The code for these supported platforms can be used as example to implement
 support for other platforms. The *platform/template* directory also provides
 template files for implementing support for a new platform. The *object.mk*,
-*config.mk* and *platform.c* template files provides enough comments to
-facilitate the implementation.
+*Kconfig*, *configs/defconfig* and *platform.c* template files provides enough
+comments to facilitate the implementation.
 
 [generic.md]: generic.md
 [qemu_virt.md]: qemu_virt.md
diff --git a/docs/platform_guide.md b/docs/platform_guide.md
index b6c2c2e..8ac5aa0 100644
--- a/docs/platform_guide.md
+++ b/docs/platform_guide.md
@@ -28,11 +28,12 @@ Adding support for a new platform
 Support for a new platform named *&lt;xyz&gt;* can be added as follows:
 
 1. Create a directory named *&lt;xyz&gt;* under the *platform/* directory.
-2. Create a platform configuration file named *config.mk* under the
-   *platform/&lt;xyz&gt;/* directory. This configuration file will provide
+2. Create a platform configuration files named *Kconfig* and *configs/defconfig*
+   under the *platform/&lt;xyz&gt;/* directory. This configuration files will
+   provide the build time configuration for the sources to be compiled.
+3. Create a *platform/&lt;xyz&gt;/objects.mk* file for listing the platform
+   object files to be compiled. This file also provides platform-specific
    compiler flags, and select firmware options.
-3. Create a *platform/&lt;xyz&gt;/objects.mk* file for listing the
-   platform-specific object files to be compiled.
 4. Create a *platform/&lt;xyz&gt;/platform.c* file providing a
    *struct sbi_platform* instance.
 
-- 
2.34.1



From apatel at ventanamicro.com  Tue Jul 19 06:18:23 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Tue, 19 Jul 2022 18:48:23 +0530
Subject: [PATCH v3 01/15] scripts: Add Kconfiglib v14.1.0 under scripts directory
In-Reply-To: <20220719131837.2389630-1-apatel@ventanamicro.com>
References: <20220719131837.2389630-1-apatel@ventanamicro.com>
Message-ID: <20220719131837.2389630-2-apatel@ventanamicro.com>

We adopt Kconfiglib v14.1.0 sources under scripts directory so that
top-level OpenSBI makefile can directly use Kconfiglib scripts without
expecting users to install a particular version Kconfiglib on their
build system.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 scripts/Kconfiglib/LICENSE.txt                |    5 +
 scripts/Kconfiglib/MANIFEST.in                |    2 +
 scripts/Kconfiglib/README.rst                 |  841 ++
 scripts/Kconfiglib/alldefconfig.py            |   27 +
 scripts/Kconfiglib/allmodconfig.py            |   46 +
 scripts/Kconfiglib/allnoconfig.py             |   45 +
 scripts/Kconfiglib/allyesconfig.py            |   56 +
 scripts/Kconfiglib/defconfig.py               |   43 +
 scripts/Kconfiglib/examples/Kmenuconfig       |  102 +
 .../Kconfiglib/examples/allnoconfig_walk.py   |   66 +
 .../examples/defconfig_oldconfig.py           |   39 +
 scripts/Kconfiglib/examples/dumpvars.py       |   15 +
 scripts/Kconfiglib/examples/eval_expr.py      |   24 +
 scripts/Kconfiglib/examples/find_symbol.py    |  112 +
 scripts/Kconfiglib/examples/help_grep.py      |   64 +
 scripts/Kconfiglib/examples/kconfiglib.py     |    1 +
 scripts/Kconfiglib/examples/list_undefined.py |  156 +
 .../Kconfiglib/examples/menuconfig_example.py |  341 +
 scripts/Kconfiglib/examples/merge_config.py   |  121 +
 .../Kconfiglib/examples/print_config_tree.py  |  199 +
 scripts/Kconfiglib/examples/print_sym_info.py |   54 +
 scripts/Kconfiglib/examples/print_tree.py     |   75 +
 scripts/Kconfiglib/genconfig.py               |  154 +
 scripts/Kconfiglib/guiconfig.py               | 2324 ++++++
 scripts/Kconfiglib/kconfiglib.py              | 7160 +++++++++++++++++
 scripts/Kconfiglib/listnewconfig.py           |   76 +
 scripts/Kconfiglib/makefile.patch             |   48 +
 scripts/Kconfiglib/menuconfig.py              | 3278 ++++++++
 scripts/Kconfiglib/oldconfig.py               |  246 +
 scripts/Kconfiglib/olddefconfig.py            |   28 +
 scripts/Kconfiglib/savedefconfig.py           |   49 +
 scripts/Kconfiglib/setconfig.py               |   92 +
 scripts/Kconfiglib/setup.cfg                  |    7 +
 scripts/Kconfiglib/setup.py                   |   96 +
 scripts/Kconfiglib/tests/Kappend              |   12 +
 scripts/Kconfiglib/tests/Kassignable          |  230 +
 scripts/Kconfiglib/tests/Kchoice              |  198 +
 scripts/Kconfiglib/tests/Kdefconfig_existent  |    8 +
 .../tests/Kdefconfig_existent_but_n           |   10 +
 .../Kconfiglib/tests/Kdefconfig_nonexistent   |    5 +
 scripts/Kconfiglib/tests/Kdefconfig_srctree   |    5 +
 scripts/Kconfiglib/tests/Kdepcopy             |  173 +
 scripts/Kconfiglib/tests/Kdeploop0            |    3 +
 scripts/Kconfiglib/tests/Kdeploop1            |    3 +
 scripts/Kconfiglib/tests/Kdeploop10           |   48 +
 scripts/Kconfiglib/tests/Kdeploop2            |    3 +
 scripts/Kconfiglib/tests/Kdeploop3            |    3 +
 scripts/Kconfiglib/tests/Kdeploop4            |    7 +
 scripts/Kconfiglib/tests/Kdeploop5            |    7 +
 scripts/Kconfiglib/tests/Kdeploop6            |    6 +
 scripts/Kconfiglib/tests/Kdeploop7            |   11 +
 scripts/Kconfiglib/tests/Kdeploop8            |    8 +
 scripts/Kconfiglib/tests/Kdeploop9            |    7 +
 scripts/Kconfiglib/tests/Kdirdep              |   30 +
 scripts/Kconfiglib/tests/Kescape              |    3 +
 scripts/Kconfiglib/tests/Keval                |   34 +
 scripts/Kconfiglib/tests/Kexpr_items          |   11 +
 scripts/Kconfiglib/tests/Kheader              |    5 +
 scripts/Kconfiglib/tests/Khelp                |   50 +
 scripts/Kconfiglib/tests/Kifremoval           |   99 +
 scripts/Kconfiglib/tests/Kimply               |  145 +
 scripts/Kconfiglib/tests/Kinclude_path        |   12 +
 .../Kconfiglib/tests/Kinclude_path_sourced_1  |   12 +
 .../Kconfiglib/tests/Kinclude_path_sourced_2  |   11 +
 scripts/Kconfiglib/tests/Kitemlists           |   36 +
 scripts/Kconfiglib/tests/Klocation            |   78 +
 scripts/Kconfiglib/tests/Klocation_sourced    |   26 +
 scripts/Kconfiglib/tests/Kmainmenu            |    5 +
 scripts/Kconfiglib/tests/Kmenuconfig          |   37 +
 scripts/Kconfiglib/tests/Kmisc                |   82 +
 scripts/Kconfiglib/tests/Kmissingrsource      |    1 +
 scripts/Kconfiglib/tests/Kmissingsource       |    1 +
 scripts/Kconfiglib/tests/Korder               |   35 +
 scripts/Kconfiglib/tests/Kpreprocess          |  151 +
 scripts/Kconfiglib/tests/Krange               |  133 +
 scripts/Kconfiglib/tests/Krecursive1          |    1 +
 scripts/Kconfiglib/tests/Krecursive2          |    1 +
 scripts/Kconfiglib/tests/Kreferenced          |   63 +
 scripts/Kconfiglib/tests/Krelation            |   36 +
 scripts/Kconfiglib/tests/Krepr                |   64 +
 scripts/Kconfiglib/tests/Kstr                 |  293 +
 scripts/Kconfiglib/tests/Kundef               |   23 +
 scripts/Kconfiglib/tests/Kuserfunctions       |   14 +
 scripts/Kconfiglib/tests/Kvisibility          |  342 +
 scripts/Kconfiglib/tests/config_indented      |    3 +
 scripts/Kconfiglib/tests/config_set_bool      |    1 +
 scripts/Kconfiglib/tests/config_set_string    |    1 +
 scripts/Kconfiglib/tests/defconfig_1          |    0
 scripts/Kconfiglib/tests/defconfig_2          |    0
 scripts/Kconfiglib/tests/empty                |    0
 scripts/Kconfiglib/tests/kconfigfunctions.py  |   22 +
 scripts/Kconfiglib/tests/reltest              |   51 +
 .../Kconfiglib/tests/sub/Kconfig_symlink_2    |    1 +
 .../Kconfiglib/tests/sub/Kconfig_symlink_3    |    2 +
 .../Kconfiglib/tests/sub/Klocation_grsourced1 |    1 +
 .../Kconfiglib/tests/sub/Klocation_grsourced2 |    1 +
 .../Kconfiglib/tests/sub/Klocation_gsourced1  |    1 +
 .../Kconfiglib/tests/sub/Klocation_gsourced2  |    1 +
 .../Kconfiglib/tests/sub/Klocation_rsourced   |    2 +
 scripts/Kconfiglib/tests/sub/defconfig_in_sub |    0
 .../tests/sub/sub/Kconfig_symlink_1           |    2 +
 scripts/Kconfiglib/tests/symlink              |    1 +
 scripts/Kconfiglib/testsuite.py               | 3203 ++++++++
 103 files changed, 21866 insertions(+)
 create mode 100644 scripts/Kconfiglib/LICENSE.txt
 create mode 100644 scripts/Kconfiglib/MANIFEST.in
 create mode 100644 scripts/Kconfiglib/README.rst
 create mode 100755 scripts/Kconfiglib/alldefconfig.py
 create mode 100755 scripts/Kconfiglib/allmodconfig.py
 create mode 100755 scripts/Kconfiglib/allnoconfig.py
 create mode 100755 scripts/Kconfiglib/allyesconfig.py
 create mode 100755 scripts/Kconfiglib/defconfig.py
 create mode 100644 scripts/Kconfiglib/examples/Kmenuconfig
 create mode 100644 scripts/Kconfiglib/examples/allnoconfig_walk.py
 create mode 100644 scripts/Kconfiglib/examples/defconfig_oldconfig.py
 create mode 100644 scripts/Kconfiglib/examples/dumpvars.py
 create mode 100644 scripts/Kconfiglib/examples/eval_expr.py
 create mode 100644 scripts/Kconfiglib/examples/find_symbol.py
 create mode 100644 scripts/Kconfiglib/examples/help_grep.py
 create mode 120000 scripts/Kconfiglib/examples/kconfiglib.py
 create mode 100644 scripts/Kconfiglib/examples/list_undefined.py
 create mode 100755 scripts/Kconfiglib/examples/menuconfig_example.py
 create mode 100755 scripts/Kconfiglib/examples/merge_config.py
 create mode 100644 scripts/Kconfiglib/examples/print_config_tree.py
 create mode 100644 scripts/Kconfiglib/examples/print_sym_info.py
 create mode 100644 scripts/Kconfiglib/examples/print_tree.py
 create mode 100755 scripts/Kconfiglib/genconfig.py
 create mode 100755 scripts/Kconfiglib/guiconfig.py
 create mode 100644 scripts/Kconfiglib/kconfiglib.py
 create mode 100755 scripts/Kconfiglib/listnewconfig.py
 create mode 100644 scripts/Kconfiglib/makefile.patch
 create mode 100755 scripts/Kconfiglib/menuconfig.py
 create mode 100755 scripts/Kconfiglib/oldconfig.py
 create mode 100755 scripts/Kconfiglib/olddefconfig.py
 create mode 100755 scripts/Kconfiglib/savedefconfig.py
 create mode 100755 scripts/Kconfiglib/setconfig.py
 create mode 100644 scripts/Kconfiglib/setup.cfg
 create mode 100644 scripts/Kconfiglib/setup.py
 create mode 100644 scripts/Kconfiglib/tests/Kappend
 create mode 100644 scripts/Kconfiglib/tests/Kassignable
 create mode 100644 scripts/Kconfiglib/tests/Kchoice
 create mode 100644 scripts/Kconfiglib/tests/Kdefconfig_existent
 create mode 100644 scripts/Kconfiglib/tests/Kdefconfig_existent_but_n
 create mode 100644 scripts/Kconfiglib/tests/Kdefconfig_nonexistent
 create mode 100644 scripts/Kconfiglib/tests/Kdefconfig_srctree
 create mode 100644 scripts/Kconfiglib/tests/Kdepcopy
 create mode 100644 scripts/Kconfiglib/tests/Kdeploop0
 create mode 100644 scripts/Kconfiglib/tests/Kdeploop1
 create mode 100644 scripts/Kconfiglib/tests/Kdeploop10
 create mode 100644 scripts/Kconfiglib/tests/Kdeploop2
 create mode 100644 scripts/Kconfiglib/tests/Kdeploop3
 create mode 100644 scripts/Kconfiglib/tests/Kdeploop4
 create mode 100644 scripts/Kconfiglib/tests/Kdeploop5
 create mode 100644 scripts/Kconfiglib/tests/Kdeploop6
 create mode 100644 scripts/Kconfiglib/tests/Kdeploop7
 create mode 100644 scripts/Kconfiglib/tests/Kdeploop8
 create mode 100644 scripts/Kconfiglib/tests/Kdeploop9
 create mode 100644 scripts/Kconfiglib/tests/Kdirdep
 create mode 100644 scripts/Kconfiglib/tests/Kescape
 create mode 100644 scripts/Kconfiglib/tests/Keval
 create mode 100644 scripts/Kconfiglib/tests/Kexpr_items
 create mode 100644 scripts/Kconfiglib/tests/Kheader
 create mode 100644 scripts/Kconfiglib/tests/Khelp
 create mode 100644 scripts/Kconfiglib/tests/Kifremoval
 create mode 100644 scripts/Kconfiglib/tests/Kimply
 create mode 100644 scripts/Kconfiglib/tests/Kinclude_path
 create mode 100644 scripts/Kconfiglib/tests/Kinclude_path_sourced_1
 create mode 100644 scripts/Kconfiglib/tests/Kinclude_path_sourced_2
 create mode 100644 scripts/Kconfiglib/tests/Kitemlists
 create mode 100644 scripts/Kconfiglib/tests/Klocation
 create mode 100644 scripts/Kconfiglib/tests/Klocation_sourced
 create mode 100644 scripts/Kconfiglib/tests/Kmainmenu
 create mode 100644 scripts/Kconfiglib/tests/Kmenuconfig
 create mode 100644 scripts/Kconfiglib/tests/Kmisc
 create mode 100644 scripts/Kconfiglib/tests/Kmissingrsource
 create mode 100644 scripts/Kconfiglib/tests/Kmissingsource
 create mode 100644 scripts/Kconfiglib/tests/Korder
 create mode 100644 scripts/Kconfiglib/tests/Kpreprocess
 create mode 100644 scripts/Kconfiglib/tests/Krange
 create mode 100644 scripts/Kconfiglib/tests/Krecursive1
 create mode 100644 scripts/Kconfiglib/tests/Krecursive2
 create mode 100644 scripts/Kconfiglib/tests/Kreferenced
 create mode 100644 scripts/Kconfiglib/tests/Krelation
 create mode 100644 scripts/Kconfiglib/tests/Krepr
 create mode 100644 scripts/Kconfiglib/tests/Kstr
 create mode 100644 scripts/Kconfiglib/tests/Kundef
 create mode 100644 scripts/Kconfiglib/tests/Kuserfunctions
 create mode 100644 scripts/Kconfiglib/tests/Kvisibility
 create mode 100644 scripts/Kconfiglib/tests/config_indented
 create mode 100644 scripts/Kconfiglib/tests/config_set_bool
 create mode 100644 scripts/Kconfiglib/tests/config_set_string
 create mode 100644 scripts/Kconfiglib/tests/defconfig_1
 create mode 100644 scripts/Kconfiglib/tests/defconfig_2
 create mode 100644 scripts/Kconfiglib/tests/empty
 create mode 100644 scripts/Kconfiglib/tests/kconfigfunctions.py
 create mode 100755 scripts/Kconfiglib/tests/reltest
 create mode 100644 scripts/Kconfiglib/tests/sub/Kconfig_symlink_2
 create mode 100644 scripts/Kconfiglib/tests/sub/Kconfig_symlink_3
 create mode 100644 scripts/Kconfiglib/tests/sub/Klocation_grsourced1
 create mode 100644 scripts/Kconfiglib/tests/sub/Klocation_grsourced2
 create mode 100644 scripts/Kconfiglib/tests/sub/Klocation_gsourced1
 create mode 100644 scripts/Kconfiglib/tests/sub/Klocation_gsourced2
 create mode 100644 scripts/Kconfiglib/tests/sub/Klocation_rsourced
 create mode 100644 scripts/Kconfiglib/tests/sub/defconfig_in_sub
 create mode 100644 scripts/Kconfiglib/tests/sub/sub/Kconfig_symlink_1
 create mode 120000 scripts/Kconfiglib/tests/symlink
 create mode 100644 scripts/Kconfiglib/testsuite.py

diff --git a/scripts/Kconfiglib/LICENSE.txt b/scripts/Kconfiglib/LICENSE.txt
new file mode 100644
index 0000000..8b31efc
--- /dev/null
+++ b/scripts/Kconfiglib/LICENSE.txt
@@ -0,0 +1,5 @@
+Copyright (c) 2011-2019, Ulf Magnusson <ulfalizer at gmail.com>
+
+Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/scripts/Kconfiglib/MANIFEST.in b/scripts/Kconfiglib/MANIFEST.in
new file mode 100644
index 0000000..881a794
--- /dev/null
+++ b/scripts/Kconfiglib/MANIFEST.in
@@ -0,0 +1,2 @@
+# Include the license file in source distributions
+include LICENSE.txt
diff --git a/scripts/Kconfiglib/README.rst b/scripts/Kconfiglib/README.rst
new file mode 100644
index 0000000..b59f04e
--- /dev/null
+++ b/scripts/Kconfiglib/README.rst
@@ -0,0 +1,841 @@
+.. contents:: Table of contents
+   :backlinks: none
+
+News
+----
+
+Dependency loop with recent linux-next kernels
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To fix issues with dependency loops on recent linux-next kernels, apply `this
+patch <https://www.spinics.net/lists/linux-kbuild/msg23455.html>`_. Hopefully,
+it will be in ``linux-next`` soon.
+
+``windows-curses`` is no longer automatically installed on Windows
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Starting with Kconfiglib 13.0.0, the `windows-curses
+<https://github.com/zephyrproject-rtos/windows-curses>`__ package is no longer
+automatically installed on Windows, and needs to be installed manually for the
+terminal ``menuconfig`` to work.
+
+This fixes installation of Kconfiglib on MSYS2, which is not compatible with
+``windows-curses``. See `this issue
+<https://github.com/ulfalizer/Kconfiglib/issues/77>`__.
+
+The ``menuconfig`` now shows a hint re. installing ``windows-curses`` when the
+``curses`` module can't be imported on Windows.
+
+Sorry if this change caused problems!
+
+Overview
+--------
+
+Kconfiglib is a `Kconfig
+<https://github.com/torvalds/linux/blob/master/Documentation/kbuild/kconfig-language.rst>`__
+implementation in Python 2/3. It started out as a helper library, but now has a
+enough functionality to also work well as a standalone Kconfig implementation
+(including `terminal and GUI menuconfig interfaces <Menuconfig interfaces_>`_
+and `Kconfig extensions`_).
+
+The entire library is contained in `kconfiglib.py
+<https://github.com/ulfalizer/Kconfiglib/blob/master/kconfiglib.py>`_. The
+bundled scripts are implemented on top of it. Implementing your own scripts
+should be relatively easy, if needed.
+
+Kconfiglib is used exclusively by e.g. the `Zephyr
+<https://www.zephyrproject.org/>`__, `esp-idf
+<https://github.com/espressif/esp-idf>`__, and `ACRN
+<https://projectacrn.org/>`__ projects. It is also used for many small helper
+scripts in various projects.
+
+Since Kconfiglib is based around a library, it can be used e.g. to generate a
+`Kconfig cross-reference
+<https://docs.zephyrproject.org/latest/reference/kconfig/index.html>`_, using
+the same robust Kconfig parser used for other Kconfig tools, instead of brittle
+ad-hoc parsing. The documentation generation script can be found `here
+<https://github.com/zephyrproject-rtos/zephyr/blob/master/doc/scripts/genrest.py>`__.
+
+Kconfiglib implements the recently added `Kconfig preprocessor
+<https://github.com/torvalds/linux/blob/master/Documentation/kbuild/kconfig-macro-language.rst>`__.
+For backwards compatibility, environment variables can be referenced both as
+``$(FOO)`` (the new syntax) and as ``$FOO`` (the old syntax). The old syntax is
+deprecated, but will probably be supported for a long time, as it's needed to
+stay compatible with older Linux kernels. The major version will be increased
+if support is ever dropped. Using the old syntax with an undefined environment
+variable keeps the string as is.
+
+Note: See `this issue <https://github.com/ulfalizer/Kconfiglib/issues/47>`__ if
+you run into a "macro expanded to blank string" error with kernel 4.18+.
+
+See `this page
+<https://docs.zephyrproject.org/latest/guides/kconfig/tips.html>`__ for some
+Kconfig tips and best practices.
+
+Installation
+------------
+
+Installation with pip
+~~~~~~~~~~~~~~~~~~~~~
+
+Kconfiglib is available on `PyPI <https://pypi.python.org/pypi/kconfiglib/>`_ and can be
+installed with e.g.
+
+.. code::
+
+    $ pip(3) install kconfiglib
+
+Microsoft Windows is supported.
+
+The ``pip`` installation will give you both the base library and the following
+executables. All but two (``genconfig`` and ``setconfig``) mirror functionality
+available in the C tools.
+
+- `menuconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/menuconfig.py>`_
+
+- `guiconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/guiconfig.py>`_
+
+- `oldconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/oldconfig.py>`_
+
+- `olddefconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/olddefconfig.py>`_
+
+- `savedefconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/savedefconfig.py>`_
+
+- `defconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/defconfig.py>`_
+
+- `alldefconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/alldefconfig.py>`_
+
+- `allnoconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/allnoconfig.py>`_
+
+- `allmodconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/allmodconfig.py>`_
+
+- `allyesconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/allyesconfig.py>`_
+
+- `listnewconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/listnewconfig.py>`_
+
+- `genconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/genconfig.py>`_
+
+- `setconfig <https://github.com/ulfalizer/Kconfiglib/blob/master/setconfig.py>`_
+
+``genconfig`` is intended to be run at build time. It generates a C header from
+the configuration and (optionally) information that can be used to rebuild only
+files that reference Kconfig symbols that have changed value.
+
+Starting with Kconfiglib version 12.2.0, all utilities are compatible with both
+Python 2 and Python 3. Previously, ``menuconfig.py`` only ran under Python 3
+(i.e., it's now more backwards compatible than before).
+
+**Note:** If you install Kconfiglib with ``pip``'s ``--user`` flag, make sure
+that your ``PATH`` includes the directory where the executables end up. You can
+list the installed files with ``pip(3) show -f kconfiglib``.
+
+All releases have a corresponding tag in the git repository, e.g. ``v14.1.0``
+(the latest version).
+
+`Semantic versioning <http://semver.org/>`_ is used. There's been ten small
+changes to the behavior of the API, a Windows packaging change, and a hashbang
+change to use ``python3``
+(`1 <https://github.com/ulfalizer/Kconfiglib/commit/e8b4ecb6ff6ccc1c7be0818314fbccda2ef2b2ee>`_,
+`2 <https://github.com/ulfalizer/Kconfiglib/commit/db633015a4d7b0ba1e882f665e191f350932b2af>`_,
+`3 <https://github.com/ulfalizer/Kconfiglib/commit/8983f7eb297dd614faf0beee3129559bc8ba338e>`_,
+`4 <https://github.com/ulfalizer/Kconfiglib/commit/cbf32e29a130d22bc734b7778e6304ac9df2a3e8>`_,
+`5 <https://github.com/ulfalizer/Kconfiglib/commit/eb6c21a9b33a2d6e2bed9882d4f930d0cab2f03b>`_,
+`6 <https://github.com/ulfalizer/Kconfiglib/commit/c19fc11355b13d75d97286402c7a933fb23d3b70>`_,
+`7 <https://github.com/ulfalizer/Kconfiglib/commit/7a428aa415606820a44291f475248b08e3952c4b>`_,
+`8 <https://github.com/ulfalizer/Kconfiglib/commit/f247ddf618ad29718e5efd3e69f8baf75d4d347b>`_,
+`9 <https://github.com/ulfalizer/Kconfiglib/commit/4fed39d9271ceb68be4157ab3f96a45b94f77dc0>`_,
+`10 <https://github.com/ulfalizer/Kconfiglib/commit/55bc8c380869ea663092212e8fe388ad7abae596>`_,
+`Windows packaging change <https://github.com/ulfalizer/Kconfiglib/commit/21b4c1e3b6e2867b9a0788d21a358f6b1f581d86>`_,
+`Python 3 hashbang change <https://github.com/ulfalizer/Kconfiglib/commit/9e0a8d29fa76adcb3f27bb2e20f16fefc2a8591e>`_),
+which is why the major version is at 14 rather than 2. I do major version bumps
+for all behavior changes, even tiny ones, and most of these were fixes for baby
+issues in the early days of the Kconfiglib 2 API.
+
+Manual installation
+~~~~~~~~~~~~~~~~~~~
+
+Just drop ``kconfiglib.py`` and the scripts you want somewhere. There are no
+third-party dependencies, but the terminal ``menuconfig`` won't work on Windows
+unless a package like `windows-curses
+<https://github.com/zephyrproject-rtos/windows-curses>`__ is installed.
+
+Installation for the Linux kernel
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+See the module docstring at the top of `kconfiglib.py <https://github.com/ulfalizer/Kconfiglib/blob/master/kconfiglib.py>`_.
+
+Python version compatibility (2.7/3.2+)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Kconfiglib and all utilities run under both Python 2.7 and Python 3.2 and
+later. The code mostly uses basic Python features and has no third-party
+dependencies, so keeping it backwards-compatible is pretty low effort.
+
+The 3.2 requirement comes from ``argparse``. ``format()`` with unnumbered
+``{}`` is used as well.
+
+A recent Python 3 version is recommended if you have a choice, as it'll give
+you better Unicode handling.
+
+Getting started
+---------------
+
+1. `Install <Installation_>`_ the library and the utilities.
+
+2. Write `Kconfig
+   <https://github.com/torvalds/linux/blob/master/Documentation/kbuild/kconfig-language.rst>`__
+   files that describe the available configuration options. See `this page
+   <https://docs.zephyrproject.org/latest/guides/kconfig/tips.html>`__ for some
+   general Kconfig advice.
+
+3. Generate an initial configuration with e.g. ``menuconfig``/``guiconfig`` or
+   ``alldefconfig``. The configuration is saved as ``.config`` by default.
+
+   For more advanced projects, the ``defconfig`` utility can be used to
+   generate the initial configuration from an existing configuration file.
+   Usually, this existing configuration file would be a minimal configuration
+   file, as generated by e.g. ``savedefconfig``.
+
+4. Run ``genconfig`` to generate a header file. By default, it is saved as
+   ``config.h``.
+
+   Normally, ``genconfig`` would be run automatically as part of the build.
+
+   Before writing a header file or other configuration output, Kconfiglib
+   compares the old contents of the file against the new contents. If there's
+   no change, the write is skipped. This avoids updating file metadata like the
+   modification time, and might save work depending on your build setup.
+   
+   Adding new configuration output formats should be relatively straightforward.
+   See the implementation of ``write_config()`` in `kconfiglib.py
+   <https://github.com/ulfalizer/Kconfiglib/blob/master/kconfiglib.py>`_.
+   The documentation for the ``Symbol.config_string`` property has some tips as
+   well.
+   
+5. To update an old ``.config`` file after the Kconfig files have changed (e.g.
+   to add new options), run ``oldconfig`` (prompts for values for new options)
+   or ``olddefconfig`` (gives new options their default value). Entering the
+   ``menuconfig`` or ``guiconfig`` interface and saving the configuration will
+   also update it (the configuration interfaces always prompt for saving
+   on exit if it would modify the contents of the ``.config`` file).
+
+   Due to Kconfig semantics, simply loading an old ``.config`` file performs an
+   implicit ``olddefconfig``, so building will normally not be affected by
+   having an outdated configuration.
+
+Whenever ``.config`` is overwritten, the previous version of the file is saved
+to ``.config.old`` (or, more generally, to ``$KCONFIG_CONFIG.old``).
+
+Using ``.config`` files as Make input
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``.config`` files use Make syntax and can be included directly in Makefiles to
+read configuration values from there. This is why ``n``-valued
+``bool``/``tristate`` values are written out as ``# CONFIG_FOO is not set`` (a
+Make comment) in ``.config``, allowing them to be tested with ``ifdef`` in
+Make.
+
+If you make use of this, you might want to pass ``--config-out <filename>`` to
+``genconfig`` and include the configuration file it generates instead of
+including ``.config`` directly. This has the advantage that the generated
+configuration file will always be a "full" configuration file, even if
+``.config`` is outdated. Otherwise, it might be necessary to run
+``old(def)config`` or ``menuconfig``/``guiconfig`` before rebuilding with an
+outdated ``.config``.
+
+If you use ``--sync-deps`` to generate incremental build information, you can
+include ``deps/auto.conf`` instead, which is also a full configuration file.
+
+Useful helper macros
+~~~~~~~~~~~~~~~~~~~~
+
+The `include/linux/kconfig.h
+<https://github.com/torvalds/linux/blob/master/include/linux/kconfig.h>`_
+header in the Linux kernel defines some useful helper macros for testing
+Kconfig configuration values.
+
+``IS_ENABLED()`` is generally useful, allowing configuration values to be
+tested in ``if`` statements with no runtime overhead.
+
+Incremental building
+~~~~~~~~~~~~~~~~~~~~
+
+See the docstring for ``Kconfig.sync_deps()`` in `kconfiglib.py
+<https://github.com/ulfalizer/Kconfiglib/blob/master/kconfiglib.py>`_ for hints
+on implementing incremental builds (rebuilding just source files that reference
+changed configuration values).
+
+Running the ``scripts/basic/fixdep.c`` tool from the kernel on the output of
+``gcc -MD <source file>`` might give you an idea of how it all fits together.
+
+Library documentation
+---------------------
+
+Kconfiglib comes with extensive documentation in the form of docstrings. To view it, run e.g.
+the following command:
+
+.. code:: sh
+
+    $ pydoc(3) kconfiglib
+
+For HTML output, add ``-w``:
+
+.. code:: sh
+
+    $ pydoc(3) -w kconfiglib
+
+This will also work after installing Kconfiglib with ``pip(3)``.
+
+Documentation for other modules can be viewed in the same way (though a plain
+``--help`` will work when they're run as executables):
+
+.. code:: sh
+
+    $ pydoc(3) menuconfig/guiconfig/...
+
+A good starting point for learning the library is to read the module docstring
+(which you could also just read directly at the beginning of `kconfiglib.py
+<https://github.com/ulfalizer/Kconfiglib/blob/master/kconfiglib.py>`_). It
+gives an introduction to symbol values, the menu tree, and expressions.
+
+After reading the module docstring, a good next step is to read the ``Kconfig``
+class documentation, and then the documentation for the ``Symbol``, ``Choice``,
+and ``MenuNode`` classes.
+
+Please tell me if something is unclear or can be explained better.
+
+Library features
+----------------
+
+Kconfiglib can do the following, among other things:
+
+- **Programmatically get and set symbol values**
+
+  See `allnoconfig.py
+  <https://github.com/ulfalizer/Kconfiglib/blob/master/allnoconfig.py>`_ and
+  `allyesconfig.py
+  <https://github.com/ulfalizer/Kconfiglib/blob/master/allyesconfig.py>`_,
+  which are automatically verified to produce identical output to the standard
+  ``make allnoconfig`` and ``make allyesconfig``.
+
+- **Read and write .config and defconfig files**
+
+  The generated ``.config`` and ``defconfig`` (minimal configuration) files are
+  character-for-character identical to what the C implementation would generate
+  (except for the header comment). The test suite relies on this, as it
+  compares the generated files.
+  
+- **Write C headers**
+
+  The generated headers use the same format as ``include/generated/autoconf.h``
+  from the Linux kernel. Output for symbols appears in the order that they're
+  defined, unlike in the C tools (where the order depends on the hash table
+  implementation).
+
+- **Implement incremental builds**
+
+  This uses the same scheme as the ``include/config`` directory in the kernel:
+  Symbols are translated into files that are touched when the symbol's value
+  changes between builds, which can be used to avoid having to do a full
+  rebuild whenever the configuration is changed.
+
+  See the ``sync_deps()`` function for more information.
+
+- **Inspect symbols**
+
+  Printing a symbol or other item (which calls ``__str__()``) returns its
+  definition in Kconfig format. This also works for symbols defined in multiple
+  locations.
+
+  A helpful ``__repr__()`` is  on all objects too.
+
+  All ``__str__()`` and ``__repr__()`` methods are deliberately implemented
+  with just public APIs, so all symbol information can be fetched separately as
+  well.
+
+- **Inspect expressions**
+
+  Expressions use a simple tuple-based format that can be processed manually
+  if needed. Expression printing and evaluation functions are provided,
+  implemented with public APIs.
+
+- **Inspect the menu tree**
+
+  The underlying menu tree is exposed, including submenus created implicitly
+  from symbols depending on preceding symbols. This can be used e.g. to
+  implement menuconfig-like functionality.
+  
+  See `menuconfig.py
+  <https://github.com/ulfalizer/Kconfiglib/blob/master/menuconfig.py>`_/`guiconfig.py
+  <https://github.com/ulfalizer/Kconfiglib/blob/master/guiconfig.py>`_ and the
+  minimalistic `menuconfig_example.py
+  <https://github.com/ulfalizer/Kconfiglib/blob/master/examples/menuconfig_example.py>`_
+  example.
+
+Kconfig extensions
+~~~~~~~~~~~~~~~~~~
+
+The following Kconfig extensions are available:
+
+- ``source`` supports glob patterns and includes each matching file. A pattern
+  is required to match at least one file.
+
+  A separate ``osource`` statement is available for cases where it's okay for
+  the pattern to match no files (in which case ``osource`` turns into a no-op).
+  
+- A relative ``source`` statement (``rsource``) is available, where file paths
+  are specified relative to the directory of the current Kconfig file. An
+  ``orsource`` statement is available as well, analogous to ``osource``.
+
+- Preprocessor user functions can be defined in Python, which makes it simple
+  to integrate information from existing Python tools into Kconfig (e.g. to
+  have Kconfig symbols depend on hardware information stored in some other
+  format).
+
+  See the *Kconfig extensions* section in the
+  `kconfiglib.py <https://github.com/ulfalizer/Kconfiglib/blob/master/kconfiglib.py>`_
+  module docstring for more information.
+
+- ``def_int``, ``def_hex``, and ``def_string`` are available in addition to
+  ``def_bool`` and ``def_tristate``, allowing ``int``, ``hex``, and ``string``
+  symbols to be given a type and a default at the same time.
+
+  These can be useful in projects that make use of symbols defined in multiple
+  locations, and remove some Kconfig inconsistency.
+  
+- Environment variables are expanded directly in e.g. ``source`` and
+  ``mainmenu`` statements, meaning ``option env`` symbols are redundant.
+
+  This is the standard behavior with the new `Kconfig preprocessor
+  <https://github.com/torvalds/linux/blob/master/Documentation/kbuild/kconfig-macro-language.rst>`__,
+  which Kconfiglib implements.
+
+  ``option env`` symbols are accepted but ignored, which leads the caveat that
+  they must have the same name as the environment variables they reference
+  (Kconfiglib warns if the names differ). This keeps Kconfiglib compatible with
+  older Linux kernels, where the name of the ``option env`` symbol always
+  matched the environment variable. Compatibility with older Linux kernels is
+  the main reason ``option env`` is still supported.
+
+  The C tools have dropped support for ``option env``.
+
+- Two extra optional warnings can be enabled by setting environment variables,
+  covering cases that are easily missed when making changes to Kconfig files:
+
+  * ``KCONFIG_WARN_UNDEF``: If set to ``y``, warnings will be generated for all
+    references to undefined symbols within Kconfig files. The only gotcha is
+    that all hex literals must be prefixed with ``0x`` or ``0X``, to make it
+    possible to distinguish them from symbol references.
+
+    Some projects (e.g. the Linux kernel) use multiple Kconfig trees with many
+    shared Kconfig files, leading to some safe undefined symbol references.
+    ``KCONFIG_WARN_UNDEF`` is useful in projects that only have a single
+    Kconfig tree though.
+
+    ``KCONFIG_STRICT`` is an older alias for this environment variable,
+    supported for backwards compatibility.
+
+  * ``KCONFIG_WARN_UNDEF_ASSIGN``: If set to ``y``, warnings will be generated
+    for all assignments to undefined symbols within ``.config`` files. By
+    default, no such warnings are generated.
+
+    This warning can also be enabled/disabled by setting
+    ``Kconfig.warn_assign_undef`` to ``True``/``False``.
+
+Other features
+--------------
+
+- **Single-file implementation**
+  
+  The entire library is contained in `kconfiglib.py
+  <https://github.com/ulfalizer/Kconfiglib/blob/master/kconfiglib.py>`_.
+
+  The tools implemented on top of it are one file each.
+
+- **Robust and highly compatible with the C Kconfig tools**
+  
+ ??The `test suite <https://github.com/ulfalizer/Kconfiglib/blob/master/testsuite.py>`_
+  automatically compares output from Kconfiglib and the C tools
+  by diffing the generated ``.config`` files for the real kernel Kconfig and
+  defconfig files, for all ARCHes.
+  
+  This currently involves comparing the output for 36 ARCHes and 498 defconfig
+  files (or over 18000 ARCH/defconfig combinations in "obsessive" test suite
+  mode). All tests are expected to pass.
+
+  A comprehensive suite of selftests is included as well.
+
+- **Not horribly slow despite being a pure Python implementation**
+  
+  The `allyesconfig.py
+  <https://github.com/ulfalizer/Kconfiglib/blob/master/allyesconfig.py>`_
+  script currently runs in about 1.3 seconds on the Linux kernel on a Core i7
+  2600K (with a warm file cache), including the ``make`` overhead from ``make
+  scriptconfig``. Note that the Linux kernel Kconfigs are absolutely massive
+  (over 14k symbols for x86) compared to most projects, and also have overhead
+  from running shell commands via the Kconfig preprocessor.
+  
+  Kconfiglib is especially speedy in cases where multiple ``.config`` files
+  need to be processed, because the ``Kconfig`` files will only need to be parsed
+  once.
+
+  For long-running jobs, `PyPy <https://pypy.org/>`_ gives a big performance
+  boost. CPython is faster for short-running jobs as PyPy needs some time to
+  warm up.
+  
+  Kconfiglib also works well with the
+  `multiprocessing <https://docs.python.org/3/library/multiprocessing.html>`_
+  module. No global state is kept.
+
+- **Generates more warnings than the C implementation**
+
+  Generates the same warnings as the C implementation, plus additional ones.
+  Also detects dependency and ``source`` loops.
+
+  All warnings point out the location(s) in the ``Kconfig`` files where a
+  symbol is defined, where applicable.
+
+- **Unicode support**
+
+  Unicode characters in string literals in ``Kconfig`` and ``.config`` files are
+  correctly handled. This support mostly comes for free from Python.
+
+- **Windows support**
+
+  Nothing Linux-specific is used. Universal newlines mode is used for both
+  Python 2 and Python 3.
+  
+  The `Zephyr <https://www.zephyrproject.org/>`_ project uses Kconfiglib to
+  generate ``.config`` files and C headers on Linux as well as Windows.
+
+- **Internals that (mostly) mirror the C implementation**
+  
+  While being simpler to understand and tweak.
+
+Menuconfig interfaces
+---------------------
+
+Three configuration interfaces are currently available:
+
+- `menuconfig.py <https://github.com/ulfalizer/Kconfiglib/blob/master/menuconfig.py>`_
+  is a terminal-based configuration interface implemented using the standard
+  Python ``curses`` module. ``xconfig`` features like showing invisible symbols and
+  showing symbol names are included, and it's possible to jump directly to a symbol
+  in the menu tree (even if it's currently invisible).
+  
+  .. image:: https://raw.githubusercontent.com/ulfalizer/Kconfiglib/screenshots/screenshots/menuconfig.gif
+
+  *There is now also a show-help mode that shows the help text of the currently
+  selected symbol in the help window at the bottom.*
+
+  Starting with Kconfiglib 12.2.0, ``menuconfig.py`` runs under both Python 2
+  and Python 3 (previously, it only ran under Python 3, so this was a
+  backport). Running it under Python 3 provides better support for Unicode text
+  entry (``get_wch()`` is not available in the ``curses`` module on Python 2).
+
+  There are no third-party dependencies on \*nix. On Windows,
+  the ``curses`` modules is not available by default, but support
+  can be added by installing the ``windows-curses`` package:
+  
+  .. code-block:: shell
+
+      $ pip install windows-curses
+
+  This uses wheels built from `this repository
+  <https://github.com/zephyrproject-rtos/windows-curses>`_, which is in turn
+  based on Christoph Gohlke's `Python Extension Packages for Windows
+  <https://www.lfd.uci.edu/~gohlke/pythonlibs/#curses>`_.
+
+  See the docstring at the top of `menuconfig.py
+  <https://github.com/ulfalizer/Kconfiglib/blob/master/menuconfig.py>`_ for
+  more information about the terminal menuconfig implementation.
+
+- `guiconfig.py
+  <https://github.com/ulfalizer/Kconfiglib/blob/master/guiconfig.py>`_ is a
+  graphical configuration interface written in `Tkinter
+  <https://docs.python.org/3/library/tkinter.html>`_. Like ``menuconfig.py``,
+  it supports showing all symbols (with invisible symbols in red) and jumping
+  directly to symbols. Symbol values can also be changed directly from the
+  jump-to dialog.
+
+  When single-menu mode is enabled, a single menu is shown at a time, like in
+  the terminal menuconfig. Only this mode distinguishes between symbols defined
+  with ``config`` and symbols defined with ``menuconfig``.
+
+  ``guiconfig.py`` has been tested on X11, Windows, and macOS, and is
+  compatible with both Python 2 and Python 3.
+
+  Despite being part of the Python standard library, ``tkinter`` often isn't
+  included by default in Python installations on Linux. These commands will
+  install it on a few different distributions:
+
+  - Ubuntu: ``sudo apt install python-tk``/``sudo apt install python3-tk``
+
+  - Fedora: ``dnf install python2-tkinter``/``dnf install python3-tkinter``
+
+  - Arch: ``sudo pacman -S tk``
+
+  - Clear Linux: ``sudo swupd bundle-add python3-tcl``
+
+  Screenshot below, with show-all mode enabled and the jump-to dialog open:
+
+  .. image:: https://raw.githubusercontent.com/ulfalizer/Kconfiglib/screenshots/screenshots/guiconfig.png
+
+  To avoid having to carry around a bunch of GIFs, the image data is embedded
+  in ``guiconfig.py``. To use separate GIF files instead, change
+  ``_USE_EMBEDDED_IMAGES`` to ``False`` in ``guiconfig.py``. The image files
+  can be found in the `screenshots
+  <https://github.com/ulfalizer/Kconfiglib/tree/screenshots/guiconfig>`_
+  branch.
+
+  I did my best with the images, but some are definitely only art adjacent.
+  Touch-ups are welcome. :)
+
+- `pymenuconfig <https://github.com/RomaVis/pymenuconfig>`_, built by `RomaVis
+  <https://github.com/RomaVis>`_, is an older portable Python 2/3 TkInter
+  menuconfig implementation.
+
+  Screenshot below:
+
+  .. image:: https://raw.githubusercontent.com/RomaVis/pymenuconfig/master/screenshot.PNG
+
+  While working on the terminal menuconfig implementation, I added a few APIs
+  to Kconfiglib that turned out to be handy. ``pymenuconfig`` predates
+  ``menuconfig.py`` and ``guiconfig.py``, and so didn't have them available.
+  Blame me for any workarounds.
+
+Examples
+--------
+
+Example scripts
+~~~~~~~~~~~~~~~
+
+The `examples/ <https://github.com/ulfalizer/Kconfiglib/blob/master/examples>`_ directory contains some simple example scripts. Among these are the following ones. Make sure you run them with the latest version of Kconfiglib, as they might make use of newly added features.
+
+- `eval_expr.py <https://github.com/ulfalizer/Kconfiglib/blob/master/examples/eval_expr.py>`_ evaluates an expression in the context of a configuration.
+
+- `find_symbol.py <https://github.com/ulfalizer/Kconfiglib/blob/master/examples/find_symbol.py>`_ searches through expressions to find references to a symbol, also printing a "backtrace" with parents for each reference found.
+
+- `help_grep.py <https://github.com/ulfalizer/Kconfiglib/blob/master/examples/help_grep.py>`_ searches for a string in all help texts.
+
+- `print_tree.py <https://github.com/ulfalizer/Kconfiglib/blob/master/examples/print_tree.py>`_ prints a tree of all configuration items.
+
+- `print_config_tree.py <https://github.com/ulfalizer/Kconfiglib/blob/master/examples/print_config_tree.py>`_ is similar to ``print_tree.py``, but dumps the tree as it would appear in ``menuconfig``, including values. This can be handy for visually diffing between ``.config`` files and different versions of ``Kconfig`` files.
+
+- `list_undefined.py <https://github.com/ulfalizer/Kconfiglib/blob/master/examples/list_undefined.py>`_ finds references to symbols that are not defined by any architecture in the Linux kernel.
+
+- `merge_config.py <https://github.com/ulfalizer/Kconfiglib/blob/master/examples/merge_config.py>`_ merges configuration fragments to produce a complete .config, similarly to ``scripts/kconfig/merge_config.sh`` from the kernel.
+
+- `menuconfig_example.py <https://github.com/ulfalizer/Kconfiglib/blob/master/examples/menuconfig_example.py>`_ implements a configuration interface that uses notation similar to ``make menuconfig``. It's deliberately kept as simple as possible to demonstrate just the core concepts.
+
+Real-world examples
+~~~~~~~~~~~~~~~~~~~
+
+- `kconfig.py
+  <https://github.com/zephyrproject-rtos/zephyr/blob/master/scripts/kconfig/kconfig.py>`_
+  from the `Zephyr <https://www.zephyrproject.org/>`_ project handles
+  ``.config`` and header file generation, also doing configuration fragment
+  merging
+
+- `genrest.py
+  <https://github.com/zephyrproject-rtos/zephyr/blob/master/doc/scripts/genrest.py>`_
+  generates a Kconfig symbol cross-reference, which can be viewed `here
+  <http://docs.zephyrproject.org/reference/kconfig/index.html>`__
+
+- `CMake and IDE integration
+  <https://github.com/espressif/esp-idf/tree/master/tools/kconfig_new>`_ from
+  the ESP-IDF project, via a configuration server program.
+
+- `A script for turning on USB-related options
+  <https://github.com/google/syzkaller/blob/master/dashboard/config/kconfiglib-merge-usb-configs.py>`_,
+  from the `syzkaller <https://github.com/google/syzkaller>`_ project.
+
+- `Various automated checks
+  <https://github.com/zephyrproject-rtos/ci-tools/blob/master/scripts/check_compliance.py>`_,
+  including a check for references to undefined Kconfig symbols in source code.
+  See the ``KconfigCheck`` class.
+
+- `Various utilities
+  <https://github.com/projectacrn/acrn-hypervisor/tree/master/scripts/kconfig>`_
+  from the `ACRN <https://projectacrn.org/>`_ project
+
+These use the older Kconfiglib 1 API, which was clunkier and not as general
+(functions instead of properties, no direct access to the menu structure or
+properties, uglier ``__str__()`` output):
+
+- `genboardscfg.py <http://git.denx.de/?p=u-boot.git;a=blob;f=tools/genboardscfg.py;hb=HEAD>`_ from `Das U-Boot <http://www.denx.de/wiki/U-Boot>`_ generates some sort of legacy board database by pulling information from a newly added Kconfig-based configuration system (as far as I understand it :).
+
+- `gen-manual-lists.py <https://git.busybox.net/buildroot/tree/support/scripts/gen-manual-lists.py?id=5676a2deea896f38123b99781da0a612865adeb0>`_ generated listings for an appendix in the `Buildroot <https://buildroot.org>`_ manual. (The listing has since been removed.)
+
+- `gen_kconfig_doc.py <https://github.com/espressif/esp-idf/blob/master/docs/gen-kconfig-doc.py>`_ from the `esp-idf <https://github.com/espressif/esp-idf>`_ project generates documentation from Kconfig files.
+
+- `SConf <https://github.com/CoryXie/SConf>`_ builds an interactive configuration interface (like ``menuconfig``) on top of Kconfiglib, for use e.g. with `SCons <scons.org>`_.
+
+- `kconfig-diff.py <https://gist.github.com/dubiousjim/5638961>`_ -- a script by `dubiousjim <https://github.com/dubiousjim>`_ that compares kernel configurations.
+
+- Originally, Kconfiglib was used in chapter 4 of my `master's thesis <http://liu.diva-portal.org/smash/get/diva2:473038/FULLTEXT01.pdf>`_ to automatically generate a "minimal" kernel for a given system. Parts of it bother me a bit now, but that's how it goes with old work.
+
+Sample ``make iscriptconfig`` session
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The following log should give some idea of the functionality available in the API:
+
+.. code-block::
+
+    $ make iscriptconfig
+    A Kconfig instance 'kconf' for the architecture x86 has been created.
+    >>> kconf  # Calls Kconfig.__repr__()
+    <configuration with 13711 symbols, main menu prompt "Linux/x86 4.14.0-rc7 Kernel Configuration", srctree ".", config symbol prefix "CONFIG_", warnings enabled, undef. symbol assignment warnings disabled>
+    >>> kconf.mainmenu_text  # Expanded main menu text
+    'Linux/x86 4.14.0-rc7 Kernel Configuration'
+    >>> kconf.top_node  # The implicit top-level menu
+    <menu node for menu, prompt "Linux/x86 4.14.0-rc7 Kernel Configuration" (visibility y), deps y, 'visible if' deps y, has child, Kconfig:5>
+    >>> kconf.top_node.list  # First child menu node
+    <menu node for symbol SRCARCH, deps y, has next, Kconfig:7>
+    >>> print(kconf.top_node.list)  # Calls MenuNode.__str__()
+    config SRCARCH
+    	string
+    	option env="SRCARCH"
+    	default "x86"
+    >>> sym = kconf.top_node.list.next.item  # Item contained in next menu node
+    >>> print(sym)  # Calls Symbol.__str__()
+    config 64BIT
+    	bool "64-bit kernel" if ARCH = "x86"
+    	default ARCH != "i386"
+    	help
+    	  Say yes to build a 64-bit kernel - formerly known as x86_64
+    	  Say no to build a 32-bit kernel - formerly known as i386
+    >>> sym  # Calls Symbol.__repr__()
+    <symbol 64BIT, bool, "64-bit kernel", value y, visibility y, direct deps y, arch/x86/Kconfig:2>
+    >>> sym.assignable  # Currently assignable values (0, 1, 2 = n, m, y)
+    (0, 2)
+    >>> sym.set_value(0)  # Set it to n
+    True
+    >>> sym.tri_value  # Check the new value
+    0
+    >>> sym = kconf.syms["X86_MPPARSE"]  # Look up symbol by name
+    >>> print(sym)
+    config X86_MPPARSE
+    	bool "Enable MPS table" if (ACPI || SFI) && X86_LOCAL_APIC
+    	default y if X86_LOCAL_APIC
+    	help
+    	  For old smp systems that do not have proper acpi support. Newer systems
+    	  (esp with 64bit cpus) with acpi support, MADT and DSDT will override it
+    >>> default = sym.defaults[0]  # Fetch its first default
+    >>> sym = default[1]  # Fetch the default's condition (just a Symbol here)
+    >>> print(sym)
+    config X86_LOCAL_APIC
+    	bool
+    	default y
+    	select IRQ_DOMAIN_HIERARCHY
+    	select PCI_MSI_IRQ_DOMAIN if PCI_MSI
+    	depends on X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_APIC || PCI_MSI
+    >>> sym.nodes  # Show the MenuNode(s) associated with it
+    [<menu node for symbol X86_LOCAL_APIC, deps n, has next, arch/x86/Kconfig:1015>]
+    >>> kconfiglib.expr_str(sym.defaults[0][1])  # Print the default's condition
+    'X86_64 || SMP || X86_32_NON_STANDARD || X86_UP_APIC || PCI_MSI'
+    >>> kconfiglib.expr_value(sym.defaults[0][1])  # Evaluate it (0 = n)
+    0
+    >>> kconf.syms["64BIT"].set_value(2)
+    True
+    >>> kconfiglib.expr_value(sym.defaults[0][1])  # Evaluate it again (2 = y)
+    2
+    >>> kconf.write_config("myconfig")  # Save a .config
+    >>> ^D
+    $ cat myconfig
+    # Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)
+    CONFIG_64BIT=y
+    CONFIG_X86_64=y
+    CONFIG_X86=y
+    CONFIG_INSTRUCTION_DECODER=y
+    CONFIG_OUTPUT_FORMAT="elf64-x86-64"
+    CONFIG_ARCH_DEFCONFIG="arch/x86/configs/x86_64_defconfig"
+    CONFIG_LOCKDEP_SUPPORT=y
+    CONFIG_STACKTRACE_SUPPORT=y
+    CONFIG_MMU=y
+    ...
+ 
+Test suite
+----------
+
+The test suite is run with
+
+.. code::
+
+    $ python(3) Kconfiglib/testsuite.py
+    
+`pypy <https://pypy.org/>`_ works too, and is much speedier for everything except ``allnoconfig.py``/``allnoconfig_simpler.py``/``allyesconfig.py``, where it doesn't have time to warm up since
+the scripts are run via ``make scriptconfig``.
+
+The test suite must be run from the top-level kernel directory. It requires that the
+Kconfiglib git repository has been cloned into it and that the makefile patch has been applied.
+
+To get rid of warnings generated for the kernel ``Kconfig`` files, add ``2>/dev/null`` to the command to
+discard ``stderr``.
+
+**NOTE: Forgetting to apply the Makefile patch will cause some tests that compare generated configurations to fail**
+
+**NOTE: The test suite overwrites .config in the kernel root, so make sure to back it up.**
+
+The test suite consists of a set of selftests and a set of compatibility tests that
+compare configurations generated by Kconfiglib with
+configurations generated by the C tools, for a number of cases. See
+`testsuite.py <https://github.com/ulfalizer/Kconfiglib/blob/master/testsuite.py>`_
+for the available options.
+
+The `tests/reltest <https://github.com/ulfalizer/Kconfiglib/blob/master/tests/reltest>`_ script runs the test suite
+and all the example scripts for both Python 2 and Python 3, verifying that everything works.
+
+Rarely, the output from the C tools is changed slightly (most recently due to a
+`change <https://www.spinics.net/lists/linux-kbuild/msg17074.html>`_ I added).
+If you get test suite failures, try running the test suite again against the
+`linux-next tree <https://www.kernel.org/doc/man-pages/linux-next.html>`_,
+which has all the latest changes. I will make it clear if any
+non-backwards-compatible changes appear.
+
+A lot of time is spent waiting around for ``make`` and the C utilities (which need to reparse all the
+Kconfig files for each defconfig test). Adding some multiprocessing to the test suite would make sense
+too.
+
+Notes
+-----
+
+* This is version 2 of Kconfiglib, which is not backwards-compatible with
+  Kconfiglib 1. A summary of changes between Kconfiglib 1 and Kconfiglib
+  2 can be found `here
+  <https://github.com/ulfalizer/Kconfiglib/blob/screenshots/kconfiglib-2-changes.txt>`__.
+
+* I sometimes see people add custom output formats, which is pretty
+  straightforward to do (see the implementations of ``write_autoconf()`` and
+  ``write_config()`` for a template, and also the documentation of the
+  ``Symbol.config_string`` property). If you come up with something you think
+  might be useful to other people, I'm happy to take it in upstream. Batteries
+  included and all that.
+
+* Kconfiglib assumes the modules symbol is ``MODULES``, which is backwards-compatible.
+  A warning is printed by default if ``option modules`` is set on some other symbol.
+  
+  Let me know if you need proper ``option modules`` support. It wouldn't be that
+  hard to add.
+
+Thanks
+------
+
+- To `RomaVis <https://github.com/RomaVis>`_, for making
+  `pymenuconfig <https://github.com/RomaVis/pymenuconfig>`_ and suggesting
+  the ``rsource`` keyword.
+
+- To `Mitja Horvat <https://github.com/pinkfluid>`_, for adding support
+  for user-defined styles to the terminal menuconfig.
+
+- To `Philip Craig <https://github.com/philipc>`_ for adding
+  support for the ``allnoconfig_y`` option and fixing an obscure issue
+  with ``comment``\s inside ``choice``\s (that didn't affect correctness but
+  made outputs differ). ``allnoconfig_y`` is used to force certain symbols
+  to ``y`` during ``make allnoconfig`` to improve coverage.
+
+License
+-------
+
+See `LICENSE.txt <https://github.com/ulfalizer/Kconfiglib/blob/master/LICENSE.txt>`_. SPDX license identifiers are used in the
+source code.
diff --git a/scripts/Kconfiglib/alldefconfig.py b/scripts/Kconfiglib/alldefconfig.py
new file mode 100755
index 0000000..56c4caa
--- /dev/null
+++ b/scripts/Kconfiglib/alldefconfig.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Writes a configuration file where all symbols are set to their their default
+values.
+
+The default output filename is '.config'. A different filename can be passed in
+the KCONFIG_CONFIG environment variable.
+
+Usage for the Linux kernel:
+
+  $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/alldefconfig.py
+"""
+import kconfiglib
+
+
+def main():
+    kconf = kconfiglib.standard_kconfig(__doc__)
+    kconf.load_allconfig("alldef.config")
+    print(kconf.write_config())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/Kconfiglib/allmodconfig.py b/scripts/Kconfiglib/allmodconfig.py
new file mode 100755
index 0000000..bfb72b4
--- /dev/null
+++ b/scripts/Kconfiglib/allmodconfig.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Writes a configuration file where as many symbols as possible are set to 'm'.
+
+The default output filename is '.config'. A different filename can be passed
+in the KCONFIG_CONFIG environment variable.
+
+Usage for the Linux kernel:
+
+  $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/allmodconfig.py
+"""
+import kconfiglib
+
+
+def main():
+    kconf = kconfiglib.standard_kconfig(__doc__)
+
+    # See allnoconfig.py
+    kconf.warn = False
+
+    for sym in kconf.unique_defined_syms:
+        if sym.orig_type == kconfiglib.BOOL:
+            # 'bool' choice symbols get their default value, as determined by
+            # e.g. 'default's on the choice
+            if not sym.choice:
+                # All other bool symbols get set to 'y', like for allyesconfig
+                sym.set_value(2)
+        elif sym.orig_type == kconfiglib.TRISTATE:
+            sym.set_value(1)
+
+    for choice in kconf.unique_choices:
+        choice.set_value(2 if choice.orig_type == kconfiglib.BOOL else 1)
+
+    kconf.warn = True
+
+    kconf.load_allconfig("allmod.config")
+
+    print(kconf.write_config())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/Kconfiglib/allnoconfig.py b/scripts/Kconfiglib/allnoconfig.py
new file mode 100755
index 0000000..de90d8b
--- /dev/null
+++ b/scripts/Kconfiglib/allnoconfig.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Writes a configuration file where as many symbols as possible are set to 'n'.
+
+The default output filename is '.config'. A different filename can be passed
+in the KCONFIG_CONFIG environment variable.
+
+Usage for the Linux kernel:
+
+  $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/allnoconfig.py
+"""
+
+# See examples/allnoconfig_walk.py for another way to implement this script
+
+import kconfiglib
+
+
+def main():
+    kconf = kconfiglib.standard_kconfig(__doc__)
+
+    # Avoid warnings that would otherwise get printed by Kconfiglib for the
+    # following:
+    #
+    # 1. Assigning a value to a symbol without a prompt, which never has any
+    #    effect
+    #
+    # 2. Assigning values invalid for the type (only bool/tristate symbols
+    #    accept 0/1/2, for n/m/y). The assignments will be ignored for other
+    #    symbol types, which is what we want.
+    kconf.warn = False
+    for sym in kconf.unique_defined_syms:
+        sym.set_value(2 if sym.is_allnoconfig_y else 0)
+    kconf.warn = True
+
+    kconf.load_allconfig("allno.config")
+
+    print(kconf.write_config())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/Kconfiglib/allyesconfig.py b/scripts/Kconfiglib/allyesconfig.py
new file mode 100755
index 0000000..90eb9b8
--- /dev/null
+++ b/scripts/Kconfiglib/allyesconfig.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Writes a configuration file where as many symbols as possible are set to 'y'.
+
+The default output filename is '.config'. A different filename can be passed
+in the KCONFIG_CONFIG environment variable.
+
+Usage for the Linux kernel:
+
+  $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/allyesconfig.py
+"""
+import kconfiglib
+
+
+def main():
+    kconf = kconfiglib.standard_kconfig(__doc__)
+
+    # See allnoconfig.py
+    kconf.warn = False
+
+    # Try to set all symbols to 'y'. Dependencies might truncate the value down
+    # later, but this will at least give the highest possible value.
+    #
+    # Assigning 0/1/2 to non-bool/tristate symbols has no effect (int/hex
+    # symbols still take a string, because they preserve formatting).
+    for sym in kconf.unique_defined_syms:
+        # Set choice symbols to 'm'. This value will be ignored for choices in
+        # 'y' mode (the "normal" mode), which will instead just get their
+        # default selection, but will set all symbols in m-mode choices to 'm',
+        # which is as high as they can go.
+        #
+        # Here's a convoluted example of how you might get an m-mode choice
+        # even during allyesconfig:
+        #
+        #   choice
+        #           tristate "weird choice"
+        #           depends on m
+        sym.set_value(1 if sym.choice else 2)
+
+    # Set all choices to the highest possible mode
+    for choice in kconf.unique_choices:
+        choice.set_value(2)
+
+    kconf.warn = True
+
+    kconf.load_allconfig("allyes.config")
+
+    print(kconf.write_config())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/Kconfiglib/defconfig.py b/scripts/Kconfiglib/defconfig.py
new file mode 100755
index 0000000..b179273
--- /dev/null
+++ b/scripts/Kconfiglib/defconfig.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Reads a specified configuration file, then writes a new configuration file.
+This can be used to initialize the configuration from e.g. an arch-specific
+configuration file. This input configuration file would usually be a minimal
+configuration file, as generated by e.g. savedefconfig.
+
+The default output filename is '.config'. A different filename can be passed in
+the KCONFIG_CONFIG environment variable.
+"""
+import argparse
+
+import kconfiglib
+
+
+def main():
+    parser = argparse.ArgumentParser(
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        description=__doc__)
+
+    parser.add_argument(
+        "--kconfig",
+        default="Kconfig",
+        help="Top-level Kconfig file (default: Kconfig)")
+
+    parser.add_argument(
+        "config",
+        metavar="CONFIGURATION",
+        help="Input configuration file")
+
+    args = parser.parse_args()
+
+    kconf = kconfiglib.Kconfig(args.kconfig, suppress_traceback=True)
+    print(kconf.load_config(args.config))
+    print(kconf.write_config())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/Kconfiglib/examples/Kmenuconfig b/scripts/Kconfiglib/examples/Kmenuconfig
new file mode 100644
index 0000000..f1cb67b
--- /dev/null
+++ b/scripts/Kconfiglib/examples/Kmenuconfig
@@ -0,0 +1,102 @@
+mainmenu "Example Kconfig configuration"
+
+config MODULES
+	bool "Enable loadable module support"
+	option modules
+	default y
+
+menu "Bool and tristate symbols"
+
+config BOOL
+	bool "Bool symbol"
+	default y
+
+config BOOL_DEP
+	bool "Dependent bool symbol"
+	depends on BOOL
+
+# Mix it up a bit with an 'if' instead of a 'depends on'
+if BOOL
+
+config TRI_DEP
+	tristate "Dependent tristate symbol"
+	select SELECTED_BY_TRI_DEP
+	imply IMPLIED_BY_TRI_DEP
+
+endif
+
+config TWO_MENU_NODES
+	bool "First prompt"
+	depends on BOOL
+
+config TRI
+	tristate "Tristate symbol"
+
+config TWO_MENU_NODES
+	bool "Second prompt"
+
+comment "These are selected by TRI_DEP"
+
+config SELECTED_BY_TRI_DEP
+	tristate "Tristate selected by TRI_DEP"
+
+config IMPLIED_BY_TRI_DEP
+	tristate "Tristate implied by TRI_DEP"
+
+endmenu
+
+
+menu "String, int, and hex symbols"
+
+config STRING
+	string "String symbol"
+	default "foo"
+
+config INT
+	int "Int symbol"
+	default 747
+
+config HEX
+	hex "Hex symbol"
+	default 0xABC
+
+endmenu
+
+
+menu "Various choices"
+
+choice BOOL_CHOICE
+	bool "Bool choice"
+
+config BOOL_CHOICE_SYM_1
+	bool "Bool choice sym 1"
+
+config BOOL_CHOICE_SYM_2
+	bool "Bool choice sym 2"
+
+endchoice
+
+choice TRI_CHOICE
+	tristate "Tristate choice"
+
+config TRI_CHOICE_SYM_1
+	tristate "Tristate choice sym 1"
+
+config TRI_CHOICE_SYM_2
+	tristate "Tristate choice sym 2"
+
+endchoice
+
+choice OPT_BOOL_CHOICE
+	bool "Optional bool choice"
+	optional
+
+config OPT_BOOL_CHOICE_SYM_1
+	bool "Optional bool choice sym 1"
+
+config OPT_BOOL_CHOICE_SYM_2
+	bool "Optional bool choice sym 2"
+
+endchoice
+
+endmenu
diff --git a/scripts/Kconfiglib/examples/allnoconfig_walk.py b/scripts/Kconfiglib/examples/allnoconfig_walk.py
new file mode 100644
index 0000000..5a8cc23
--- /dev/null
+++ b/scripts/Kconfiglib/examples/allnoconfig_walk.py
@@ -0,0 +1,66 @@
+# This is tree-walking version of allnoconfig.py, for demonstration purposes.
+# Verified by the test suite to generate identical output to 'make allnoconfig'
+# for all ARCHes.
+#
+# Note: A more practical version would use Kconfig.node_iter(). The manual tree
+# walking is for demonstration purposes.
+#
+# Usage for the Linux kernel:
+#
+#   $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/examples/allnoconfig_walk.py
+
+import sys
+
+from kconfiglib import Kconfig, Symbol
+
+
+def do_allnoconfig(node):
+    global changed
+
+    # Walk the tree of menu nodes. You can imagine this as going down/into menu
+    # entries in the menuconfig interface, setting each to n (or the lowest
+    # assignable value).
+
+    while node:
+        if isinstance(node.item, Symbol):
+            sym = node.item
+
+            # Is the symbol a non-allnoconfig_y symbol that can be set to a
+            # lower value than its current value?
+            if (not sym.is_allnoconfig_y and
+                sym.assignable and
+                sym.assignable[0] < sym.tri_value):
+
+                # Yup, lower it
+                sym.set_value(sym.assignable[0])
+                changed = True
+
+        # Recursively lower children
+        if node.list:
+            do_allnoconfig(node.list)
+
+        node = node.next
+
+
+# Parse the Kconfig files
+kconf = Kconfig(sys.argv[1])
+
+# Do an initial pass to set 'option allnoconfig_y' symbols to y
+for sym in kconf.unique_defined_syms:
+    if sym.is_allnoconfig_y:
+        sym.set_value(2)
+
+while True:
+    # Changing later symbols in the configuration can sometimes allow earlier
+    # symbols to be lowered, e.g. if a later symbol 'select's an earlier
+    # symbol. To handle such situations, we do additional passes over the tree
+    # until we're no longer able to change the value of any symbol in a pass.
+    changed = False
+
+    do_allnoconfig(kconf.top_node)
+
+    # Did the pass change any symbols?
+    if not changed:
+        break
+
+print(kconf.write_config())
diff --git a/scripts/Kconfiglib/examples/defconfig_oldconfig.py b/scripts/Kconfiglib/examples/defconfig_oldconfig.py
new file mode 100644
index 0000000..68336c6
--- /dev/null
+++ b/scripts/Kconfiglib/examples/defconfig_oldconfig.py
@@ -0,0 +1,39 @@
+# Produces exactly the same output as the following script:
+#
+# make defconfig
+# echo CONFIG_ETHERNET=n >> .config
+# make oldconfig
+# echo CONFIG_ETHERNET=y >> .config
+# yes n | make oldconfig
+#
+# This came up in https://github.com/ulfalizer/Kconfiglib/issues/15.
+#
+# Usage:
+#
+#   $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/examples/defconfig_oldconfig.py
+
+import sys
+
+import kconfiglib
+
+
+kconf = kconfiglib.Kconfig(sys.argv[1])
+
+# Mirrors defconfig
+kconf.load_config("arch/x86/configs/x86_64_defconfig")
+kconf.write_config()
+
+# Mirrors the first oldconfig
+kconf.load_config()
+kconf.syms["ETHERNET"].set_value(0)
+kconf.write_config()
+
+# Mirrors the second oldconfig
+kconf.load_config()
+kconf.syms["ETHERNET"].set_value(2)
+for s in kconf.unique_defined_syms:
+    if s.user_value is None and 0 in s.assignable:
+        s.set_value(0)
+
+# Write the final configuration
+print(kconf.write_config())
diff --git a/scripts/Kconfiglib/examples/dumpvars.py b/scripts/Kconfiglib/examples/dumpvars.py
new file mode 100644
index 0000000..0f8ab43
--- /dev/null
+++ b/scripts/Kconfiglib/examples/dumpvars.py
@@ -0,0 +1,15 @@
+# Prints all (set) environment variables referenced in the Kconfig files
+# together with their values, as a list of assignments.
+#
+# Note: This only works for environment variables referenced via the $(FOO)
+# preprocessor syntax. The older $FOO syntax is maintained for backwards
+# compatibility.
+
+import os
+import sys
+
+import kconfiglib
+
+
+print(" ".join("{}='{}'".format(var, os.environ[var])
+               for var in kconfiglib.Kconfig(sys.argv[1]).env_vars))
diff --git a/scripts/Kconfiglib/examples/eval_expr.py b/scripts/Kconfiglib/examples/eval_expr.py
new file mode 100644
index 0000000..23eedb4
--- /dev/null
+++ b/scripts/Kconfiglib/examples/eval_expr.py
@@ -0,0 +1,24 @@
+# Evaluates an expression (e.g. "X86_64 || (X86_32 && X86_LOCAL_APIC)") in the
+# context of a configuration. Note that this always yields a tristate value (n,
+# m, or y).
+#
+# Usage:
+#
+#   $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/examples/eval_expr.py SCRIPT_ARG=<expr>
+
+import sys
+
+import kconfiglib
+
+
+if len(sys.argv) < 3:
+    sys.exit("Pass the expression to evaluate with SCRIPT_ARG=<expression>")
+
+kconf = kconfiglib.Kconfig(sys.argv[1])
+expr = sys.argv[2]
+
+# Enable modules so that m doesn't get demoted to n
+kconf.modules.set_value(2)
+
+print("the expression '{}' evaluates to {}"
+      .format(expr, kconf.eval_string(expr)))
diff --git a/scripts/Kconfiglib/examples/find_symbol.py b/scripts/Kconfiglib/examples/find_symbol.py
new file mode 100644
index 0000000..f747103
--- /dev/null
+++ b/scripts/Kconfiglib/examples/find_symbol.py
@@ -0,0 +1,112 @@
+# Prints all menu nodes that reference a given symbol any of their properties
+# or property conditions, along with their parent menu nodes.
+#
+# Usage:
+#
+#   $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/examples/find_symbol.py SCRIPT_ARG=<name>
+#
+# Example output for SCRIPT_ARG=X86:
+#
+#   Found 470 locations that reference X86:
+#
+#   ========== Location 1 (init/Kconfig:1108) ==========
+#
+#   config SGETMASK_SYSCALL
+#   	bool
+#   	prompt "sgetmask/ssetmask syscalls support" if EXPERT
+#   	default PARISC || M68K || PPC || MIPS || X86 || SPARC || MICROBLAZE || SUPERH
+#   	help
+#   	  sys_sgetmask and sys_ssetmask are obsolete system calls
+#   	  no longer supported in libc but still enabled by default in some
+#   	  architectures.
+#
+#   	  If unsure, leave the default option here.
+#
+#   ---------- Parent 1 (init/Kconfig:1077)  ----------
+#
+#   menuconfig EXPERT
+#   	bool
+#   	prompt "Configure standard kernel features (expert users)"
+#   	select DEBUG_KERNEL
+#   	help
+#   	  This option allows certain base kernel options and settings
+#   	  to be disabled or tweaked. This is for specialized
+#   	  environments which can tolerate a "non-standard" kernel.
+#   	  Only use this if you really know what you are doing.
+#
+#   ---------- Parent 2 (init/Kconfig:39)  ----------
+#
+#   menu "General setup"
+#
+#   ========== Location 2 (arch/Kconfig:29) ==========
+#
+#   config OPROFILE_EVENT_MULTIPLEX
+#   	bool
+#   	prompt "OProfile multiplexing support (EXPERIMENTAL)"
+#   	default "n"
+#   	depends on OPROFILE && X86
+#   	help
+#   	  The number of hardware counters is limited. The multiplexing
+#   	  feature enables OProfile to gather more events than counters
+#   	  are provided by the hardware. This is realized by switching
+#   	  between events at a user specified time interval.
+#
+#   	  If unsure, say N.
+#
+#   ---------- Parent 1 (arch/Kconfig:16)  ----------
+#
+#   config OPROFILE
+#   	tristate
+#   	prompt "OProfile system profiling"
+#   	select RING_BUFFER
+#   	select RING_BUFFER_ALLOW_SWAP
+#   	depends on PROFILING && HAVE_OPROFILE
+#   	help
+#   	  OProfile is a profiling system capable of profiling the
+#   	  whole system, include the kernel, kernel modules, libraries,
+#   	  and applications.
+#
+#   	  If unsure, say N.
+#
+#   ---------- Parent 2 (init/Kconfig:39)  ----------
+#
+#   menu "General setup"
+#
+#   ... (tons more)
+
+import sys
+
+import kconfiglib
+
+
+if len(sys.argv) < 3:
+    sys.exit('Pass symbol name (without "CONFIG_" prefix) with SCRIPT_ARG=<name>')
+
+kconf = kconfiglib.Kconfig(sys.argv[1])
+sym_name = sys.argv[2]
+if sym_name not in kconf.syms:
+    print("No symbol {} exists in the configuration".format(sym_name))
+    sys.exit(0)
+
+referencing = [node for node in kconf.node_iter()
+               if kconf.syms[sym_name] in node.referenced]
+if not referencing:
+    print("No references to {} found".format(sym_name))
+    sys.exit(0)
+
+print("Found {} locations that reference {}:\n"
+      .format(len(referencing), sym_name))
+
+for i, node in enumerate(referencing, 1):
+    print("========== Location {} ({}:{}) ==========\n\n{}"
+          .format(i, node.filename, node.linenr, node))
+
+    # Print the parents of the menu node too
+
+    node = node.parent
+    parent_i = 1
+    while node is not kconf.top_node:
+        print("---------- Parent {} ({}:{})  ----------\n\n{}"
+              .format(parent_i, node.filename, node.linenr, node))
+        node = node.parent
+        parent_i += 1
diff --git a/scripts/Kconfiglib/examples/help_grep.py b/scripts/Kconfiglib/examples/help_grep.py
new file mode 100644
index 0000000..157d8f2
--- /dev/null
+++ b/scripts/Kconfiglib/examples/help_grep.py
@@ -0,0 +1,64 @@
+# Does a case-insensitive search for a regular expression in the help texts of
+# symbols and choices and the prompts of menus and comments. Prints the
+# matching items together with their locations and the matching text.
+#
+# Usage:
+#
+#   $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/examples/help_grep.py SCRIPT_ARG=<regex>
+#
+# Shortened example output for SCRIPT_ARG=general:
+#
+#   menu "General setup"
+#   location: init/Kconfig:39
+#
+#   config SYSVIPC
+#   	bool
+#   	prompt "System V IPC"
+#   	help
+#   	  ...
+#   	  exchange information. It is generally considered to be a good thing,
+#   	  ...
+#
+#   location: init/Kconfig:233
+#
+#   config BSD_PROCESS_ACCT
+#   	bool
+#   	prompt "BSD Process Accounting" if MULTIUSER
+#   	help
+#   	  ...
+#   	  information.  This is generally a good idea, so say Y.
+#
+#   location: init/Kconfig:403
+#
+#   ...
+
+
+import re
+import sys
+
+from kconfiglib import Kconfig, Symbol, Choice, MENU, COMMENT
+
+
+if len(sys.argv) < 3:
+    sys.exit("Pass the regex with SCRIPT_ARG=<regex>")
+
+search = re.compile(sys.argv[2], re.IGNORECASE).search
+
+for node in Kconfig(sys.argv[1]).node_iter():
+    match = False
+
+    if isinstance(node.item, (Symbol, Choice)) and \
+       node.help is not None and search(node.help):
+        print(node.item)
+        match = True
+
+    elif node.item == MENU and search(node.prompt[0]):
+        print('menu "{}"'.format(node.prompt[0]))
+        match = True
+
+    elif node.item == COMMENT and search(node.prompt[0]):
+        print('comment "{}"'.format(node.prompt[0]))
+        match = True
+
+    if match:
+        print("location: {}:{}\n".format(node.filename, node.linenr))
diff --git a/scripts/Kconfiglib/examples/kconfiglib.py b/scripts/Kconfiglib/examples/kconfiglib.py
new file mode 120000
index 0000000..b9dfb64
--- /dev/null
+++ b/scripts/Kconfiglib/examples/kconfiglib.py
@@ -0,0 +1 @@
+../kconfiglib.py
\ No newline at end of file
diff --git a/scripts/Kconfiglib/examples/list_undefined.py b/scripts/Kconfiglib/examples/list_undefined.py
new file mode 100644
index 0000000..4a3bc9b
--- /dev/null
+++ b/scripts/Kconfiglib/examples/list_undefined.py
@@ -0,0 +1,156 @@
+# Prints a list of symbols that are referenced in the Kconfig files of some
+# architecture but not defined by the Kconfig files of any architecture.
+#
+# A Kconfig file might be shared between many architectures and legitimately
+# reference undefined symbols for some of them, but if no architecture defines
+# the symbol, it usually indicates a problem or potential cleanup.
+#
+# This script could be sped up a lot if needed. See the comment near the
+# referencing_nodes() call.
+#
+# Run with the following command in the kernel root:
+#
+#   $ python(3) Kconfiglib/examples/list_undefined.py
+#
+# Example output:
+#
+#   Registering defined and undefined symbols for all arches
+#     Processing mips
+#     Processing ia64
+#     Processing metag
+#     ...
+#
+#   Finding references to each undefined symbol
+#     Processing mips
+#     Processing ia64
+#     Processing metag
+#     ...
+#
+#   The following globally undefined symbols were found, listed here
+#   together with the locations of the items that reference them.
+#   References might come from enclosing menus and ifs.
+#
+#     ARM_ERRATA_753970: arch/arm/mach-mvebu/Kconfig:56, arch/arm/mach-mvebu/Kconfig:39
+#     SUNXI_CCU_MP: drivers/clk/sunxi-ng/Kconfig:14
+#     SUNXI_CCU_DIV: drivers/clk/sunxi-ng/Kconfig:14
+#     AC97: sound/ac97/Kconfig:6
+#     ...
+
+import os
+import subprocess
+
+from kconfiglib import Kconfig
+
+
+# Referenced inside the Kconfig files
+os.environ["KERNELVERSION"] = str(
+    subprocess.check_output(("make", "kernelversion")).decode("utf-8").rstrip()
+)
+
+
+def all_arch_srcarch_pairs():
+    """
+    Generates all valid (ARCH, SRCARCH) tuples for the kernel, corresponding to
+    different architectures. SRCARCH holds the arch/ subdirectory.
+    """
+    for srcarch in os.listdir("arch"):
+        # Each subdirectory of arch/ containing a Kconfig file corresponds to
+        # an architecture
+        if os.path.exists(os.path.join("arch", srcarch, "Kconfig")):
+            yield (srcarch, srcarch)
+
+    # Some architectures define additional ARCH settings with ARCH != SRCARCH
+    # (search for "Additional ARCH settings for" in the top-level Makefile)
+
+    yield ("i386", "x86")
+    yield ("x86_64", "x86")
+
+    yield ("sparc32", "sparc")
+    yield ("sparc64", "sparc")
+
+    yield ("sh64", "sh")
+
+    yield ("um", "um")
+
+
+def all_arch_srcarch_kconfigs():
+    """
+    Generates Kconfig instances for all the architectures in the kernel
+    """
+
+    os.environ["srctree"] = "."
+    os.environ["HOSTCC"] = "gcc"
+    os.environ["HOSTCXX"] = "g++"
+    os.environ["CC"] = "gcc"
+    os.environ["LD"] = "ld"
+
+    for arch, srcarch in all_arch_srcarch_pairs():
+        print("  Processing " + arch)
+
+        os.environ["ARCH"] = arch
+        os.environ["SRCARCH"] = srcarch
+
+        # um (User Mode Linux) uses a different base Kconfig file
+        yield Kconfig("Kconfig" if arch != "um" else "arch/x86/um/Kconfig",
+                      warn=False)
+
+
+print("Registering defined and undefined symbols for all arches")
+
+# Sets holding the names of all defined and undefined symbols, for all
+# architectures
+defined = set()
+undefined = set()
+
+for kconf in all_arch_srcarch_kconfigs():
+    for name, sym in kconf.syms.items():
+        if sym.nodes:
+            # If the symbol has a menu node, it is defined
+            defined.add(name)
+        else:
+            # Undefined symbol. We skip some of the uninteresting ones.
+
+            # Due to how Kconfig works, integer literals show up as symbols
+            # (from e.g. 'default 1'). Skip those.
+            try:
+                int(name, 0)
+                continue
+            except ValueError:
+                # Interesting undefined symbol
+                undefined.add(name)
+
+
+print("\nFinding references to each undefined symbol")
+
+def referencing_nodes(kconf, name):
+    # Returns a list of all menu nodes that reference a symbol named 'name' in
+    # any of their properties or property conditions
+    res = []
+
+    for node in kconf.node_iter():
+        for ref in node.referenced:
+            if ref.name == name:
+                res.append(node)
+
+    return res
+
+
+# Maps each globally undefined symbol to the menu nodes that reference it
+undef_sym_refs = [(name, set()) for name in undefined - defined]
+
+for kconf in all_arch_srcarch_kconfigs():
+    for name, refs in undef_sym_refs:
+        # This means that we search the entire configuration tree for each
+        # undefined symbol, which is terribly inefficient. We could speed
+        # things up by tweaking referencing_nodes() to compare each symbol to
+        # multiple symbols while walking the configuration tree.
+        for node in referencing_nodes(kconf, name):
+            refs.add("{}:{}".format(node.filename, node.linenr))
+
+
+print("\nThe following globally undefined symbols were found, listed here\n"
+      "together with the locations of the items that reference them.\n"
+      "References might come from enclosing menus and ifs.\n")
+
+for name, refs in undef_sym_refs:
+    print("  {}: {}".format(name, ", ".join(refs)))
diff --git a/scripts/Kconfiglib/examples/menuconfig_example.py b/scripts/Kconfiglib/examples/menuconfig_example.py
new file mode 100755
index 0000000..606f756
--- /dev/null
+++ b/scripts/Kconfiglib/examples/menuconfig_example.py
@@ -0,0 +1,341 @@
+#!/usr/bin/env python3
+
+# Implements a simple configuration interface on top of Kconfiglib to
+# demonstrate concepts for building a menuconfig-like. Emulates how the
+# standard menuconfig prints menu entries.
+#
+# Always displays the entire Kconfig tree to keep things as simple as possible
+# (all symbols, choices, menus, and comments).
+#
+# Usage:
+#
+#   $ python(3) Kconfiglib/examples/menuconfig.py <Kconfig file>
+#
+# A sample Kconfig is available in Kconfiglib/examples/Kmenuconfig.
+#
+# Here's a notation guide. The notation matches the one used by menuconfig
+# (scripts/kconfig/mconf):
+#
+#   [ ] prompt      - Bool
+#   < > prompt      - Tristate
+#   {M} prompt      - Tristate selected to m. Can only be set to m or y.
+#   -*- prompt      - Bool/tristate selected to y, pinning it
+#   -M- prompt      - Tristate selected to m that also has m visibility,
+#                     pinning it to m
+#   (foo) prompt    - String/int/hex symbol with value "foo"
+#   --> prompt      - The selected symbol in a choice in y mode. This
+#                     syntax is unique to this example.
+#
+# When modules are disabled, the .type attribute of TRISTATE symbols and
+# choices automatically changes to BOOL. This trick is used by the C
+# implementation as well, and gives the expected behavior without having to do
+# anything extra here. The original type is available in .orig_type if needed.
+#
+# The Kconfiglib/examples/Kmenuconfig example uses named choices to be able to
+# refer to choices by name. Named choices are supported in the C tools too, but
+# I don't think I've ever seen them used in the wild.
+#
+# Sample session:
+#
+#   $ python Kconfiglib/examples/menuconfig.py Kconfiglib/examples/Kmenuconfig
+#
+#   ======== Example Kconfig configuration ========
+#
+#   [*] Enable loadable module support (MODULES)
+#       Bool and tristate symbols
+#           [*] Bool symbol (BOOL)
+#                   [ ] Dependent bool symbol (BOOL_DEP)
+#                   < > Dependent tristate symbol (TRI_DEP)
+#                   [ ] First prompt (TWO_MENU_NODES)
+#           < > Tristate symbol (TRI)
+#           [ ] Second prompt (TWO_MENU_NODES)
+#               *** These are selected by TRI_DEP ***
+#           < > Tristate selected by TRI_DEP (SELECTED_BY_TRI_DEP)
+#           < > Tristate implied by TRI_DEP (IMPLIED_BY_TRI_DEP)
+#       String, int, and hex symbols
+#           (foo) String symbol (STRING)
+#           (747) Int symbol (INT)
+#           (0xABC) Hex symbol (HEX)
+#       Various choices
+#           -*- Bool choice (BOOL_CHOICE)
+#                   --> Bool choice sym 1 (BOOL_CHOICE_SYM_1)
+#                       Bool choice sym 2 (BOOL_CHOICE_SYM_2)
+#           {M} Tristate choice (TRI_CHOICE)
+#                   < > Tristate choice sym 1 (TRI_CHOICE_SYM_1)
+#                   < > Tristate choice sym 2 (TRI_CHOICE_SYM_2)
+#           [ ] Optional bool choice (OPT_BOOL_CHOICE)
+#
+#   Enter a symbol/choice name, "load_config", or "write_config" (or press CTRL+D to exit): BOOL
+#   Value for BOOL (available: n, y): n
+#
+#   ======== Example Kconfig configuration ========
+#
+#   [*] Enable loadable module support (MODULES)
+#       Bool and tristate symbols
+#           [ ] Bool symbol (BOOL)
+#           < > Tristate symbol (TRI)
+#           [ ] Second prompt (TWO_MENU_NODES)
+#               *** These are selected by TRI_DEP ***
+#           < > Tristate selected by TRI_DEP (SELECTED_BY_TRI_DEP)
+#           < > Tristate implied by TRI_DEP (IMPLIED_BY_TRI_DEP)
+#       String, int, and hex symbols
+#           (foo) String symbol (STRING)
+#           (747) Int symbol (INT)
+#           (0xABC) Hex symbol (HEX)
+#       Various choices
+#           -*- Bool choice (BOOL_CHOICE)
+#                   --> Bool choice sym 1 (BOOL_CHOICE_SYM_1)
+#                       Bool choice sym 2 (BOOL_CHOICE_SYM_2)
+#           {M} Tristate choice (TRI_CHOICE)
+#                   < > Tristate choice sym 1 (TRI_CHOICE_SYM_1)
+#                   < > Tristate choice sym 2 (TRI_CHOICE_SYM_2)
+#          [ ] Optional bool choice (OPT_BOOL_CHOICE)
+#
+#   Enter a symbol/choice name, "load_config", or "write_config" (or press CTRL+D to exit): MODULES
+#   Value for MODULES (available: n, y): n
+#
+#   ======== Example Kconfig configuration ========
+#
+#   [ ] Enable loadable module support (MODULES)
+#       Bool and tristate symbols
+#           [ ] Bool symbol (BOOL)
+#           [ ] Tristate symbol (TRI)
+#           [ ] Second prompt (TWO_MENU_NODES)
+#               *** These are selected by TRI_DEP ***
+#           [ ] Tristate selected by TRI_DEP (SELECTED_BY_TRI_DEP)
+#           [ ] Tristate implied by TRI_DEP (IMPLIED_BY_TRI_DEP)
+#       String, int, and hex symbols
+#           (foo) String symbol (STRING)
+#           (747) Int symbol (INT)
+#           (0xABC) Hex symbol (HEX)
+#       Various choices
+#           -*- Bool choice (BOOL_CHOICE)
+#                   --> Bool choice sym 1 (BOOL_CHOICE_SYM_1)
+#                       Bool choice sym 2 (BOOL_CHOICE_SYM_2)
+#           -*- Tristate choice (TRI_CHOICE)
+#                   --> Tristate choice sym 1 (TRI_CHOICE_SYM_1)
+#                       Tristate choice sym 2 (TRI_CHOICE_SYM_2)
+#           [ ] Optional bool choice (OPT_BOOL_CHOICE)
+#
+#   Enter a symbol/choice name, "load_config", or "write_config" (or press CTRL+D to exit): ^D
+
+from __future__ import print_function
+import readline
+import sys
+
+from kconfiglib import Kconfig, \
+                       Symbol, MENU, COMMENT, \
+                       BOOL, TRISTATE, STRING, INT, HEX, UNKNOWN, \
+                       expr_value, \
+                       TRI_TO_STR
+
+
+# Python 2/3 compatibility hack
+if sys.version_info[0] < 3:
+    input = raw_input
+
+
+def indent_print(s, indent):
+    print(indent*" " + s)
+
+
+def value_str(sc):
+    """
+    Returns the value part ("[*]", "<M>", "(foo)" etc.) of a menu entry.
+
+    sc: Symbol or Choice.
+    """
+    if sc.type in (STRING, INT, HEX):
+        return "({})".format(sc.str_value)
+
+    # BOOL or TRISTATE
+
+    # The choice mode is an upper bound on the visibility of choice symbols, so
+    # we can check the choice symbols' own visibility to see if the choice is
+    # in y mode
+    if isinstance(sc, Symbol) and sc.choice and sc.visibility == 2:
+        # For choices in y mode, print '-->' next to the selected symbol
+        return "-->" if sc.choice.selection is sc else "   "
+
+    tri_val_str = (" ", "M", "*")[sc.tri_value]
+
+    if len(sc.assignable) == 1:
+        # Pinned to a single value
+        return "-{}-".format(tri_val_str)
+
+    if sc.type == BOOL:
+        return "[{}]".format(tri_val_str)
+
+    if sc.type == TRISTATE:
+        if sc.assignable == (1, 2):
+            # m and y available
+            return "{" + tri_val_str + "}"  # Gets a bit confusing with .format()
+        return "<{}>".format(tri_val_str)
+
+
+def node_str(node):
+    """
+    Returns the complete menu entry text for a menu node, or "" for invisible
+    menu nodes. Invisible menu nodes are those that lack a prompt or that do
+    not have a satisfied prompt condition.
+
+    Example return value: "[*] Bool symbol (BOOL)"
+
+    The symbol name is printed in parentheses to the right of the prompt. This
+    is so that symbols can easily be referred to in the configuration
+    interface.
+    """
+    if not node.prompt:
+        return ""
+
+    # Even for menu nodes for symbols and choices, it's wrong to check
+    # Symbol.visibility / Choice.visibility here. The reason is that a symbol
+    # (and a choice, in theory) can be defined in multiple locations, giving it
+    # multiple menu nodes, which do not necessarily all have the same prompt
+    # visibility. Symbol.visibility / Choice.visibility is calculated as the OR
+    # of the visibility of all the prompts.
+    prompt, prompt_cond = node.prompt
+    if not expr_value(prompt_cond):
+        return ""
+
+    if node.item == MENU:
+        return "    " + prompt
+
+    if node.item == COMMENT:
+        return "    *** {} ***".format(prompt)
+
+    # Symbol or Choice
+
+    sc = node.item
+
+    if sc.type == UNKNOWN:
+        # Skip symbols defined without a type (these are obscure and generate
+        # a warning)
+        return ""
+
+    # {:3} sets the field width to three. Gives nice alignment for empty string
+    # values.
+    res = "{:3} {}".format(value_str(sc), prompt)
+
+    # Don't print the name for unnamed choices (the normal kind)
+    if sc.name is not None:
+        res += " ({})".format(sc.name)
+
+    return res
+
+
+def print_menuconfig_nodes(node, indent):
+    """
+    Prints a tree with all the menu entries rooted at 'node'. Child menu
+    entries are indented.
+    """
+    while node:
+        string = node_str(node)
+        if string:
+            indent_print(string, indent)
+
+        if node.list:
+            print_menuconfig_nodes(node.list, indent + 8)
+
+        node = node.next
+
+
+def print_menuconfig(kconf):
+    """
+    Prints all menu entries for the configuration.
+    """
+    # Print the expanded mainmenu text at the top. This is the same as
+    # kconf.top_node.prompt[0], but with variable references expanded.
+    print("\n======== {} ========\n".format(kconf.mainmenu_text))
+
+    print_menuconfig_nodes(kconf.top_node.list, 0)
+    print("")
+
+
+def get_value_from_user(sc):
+    """
+    Prompts the user for a value for the symbol or choice 'sc'. For
+    bool/tristate symbols and choices, provides a list of all the assignable
+    values.
+    """
+    if not sc.visibility:
+        print(sc.name + " is not currently visible")
+        return False
+
+    prompt = "Value for {}".format(sc.name)
+    if sc.type in (BOOL, TRISTATE):
+        prompt += " (available: {})" \
+                  .format(", ".join(TRI_TO_STR[val] for val in sc.assignable))
+    prompt += ": "
+
+    val = input(prompt)
+
+    # Automatically add a "0x" prefix for hex symbols, like the menuconfig
+    # interface does. This isn't done when loading .config files, hence why
+    # set_value() doesn't do it automatically.
+    if sc.type == HEX and not val.startswith(("0x", "0X")):
+        val = "0x" + val
+
+    # Let Kconfiglib itself print a warning here if the value is invalid. We
+    # could also disable warnings temporarily with 'kconf.warn = False' and
+    # print our own warning.
+    return sc.set_value(val)
+
+
+if __name__ == "__main__":
+    if len(sys.argv) != 2:
+        sys.exit("usage: menuconfig.py <Kconfig file>")
+
+    # Load Kconfig configuration files
+    kconf = Kconfig(sys.argv[1])
+
+    # Print the initial configuration tree
+    print_menuconfig(kconf)
+
+    while True:
+        try:
+            cmd = input('Enter a symbol/choice name, "load_config", or '
+                        '"write_config" (or press CTRL+D to exit): ').strip()
+        except EOFError:
+            print("")
+            break
+
+        if cmd == "load_config":
+            config_filename = input(".config file to load: ")
+            try:
+                # Returns a message telling which file got loaded
+                print(kconf.load_config(config_filename))
+            except EnvironmentError as e:
+                print(e, file=sys.stderr)
+
+            print_menuconfig(kconf)
+            continue
+
+        if cmd == "write_config":
+            config_filename = input("To this file: ")
+            try:
+                # Returns a message telling which file got saved
+                print(kconf.write_config(config_filename))
+            except EnvironmentError as e:
+                print(e, file=sys.stderr)
+
+            continue
+
+        # Assume 'cmd' is the name of a symbol or choice if it isn't one of the
+        # commands above, prompt the user for a value for it, and print the new
+        # configuration tree
+
+        if cmd in kconf.syms:
+            if get_value_from_user(kconf.syms[cmd]):
+                print_menuconfig(kconf)
+
+            continue
+
+        if cmd in kconf.named_choices:
+            if get_value_from_user(kconf.named_choices[cmd]):
+                print_menuconfig(kconf)
+
+            continue
+
+        print("No symbol/choice named '{}' in the configuration".format(cmd),
+              file=sys.stderr)
diff --git a/scripts/Kconfiglib/examples/merge_config.py b/scripts/Kconfiglib/examples/merge_config.py
new file mode 100755
index 0000000..777fe2c
--- /dev/null
+++ b/scripts/Kconfiglib/examples/merge_config.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python3
+
+# This script functions similarly to scripts/kconfig/merge_config.sh from the
+# kernel tree, merging multiple configurations fragments to produce a complete
+# .config, with unspecified values filled in as for alldefconfig.
+#
+# The generated .config respects symbol dependencies, and a warning is printed
+# if any symbol gets a different value from the assigned value.
+#
+# For a real-world merging example based on this script, see
+# https://github.com/zephyrproject-rtos/zephyr/blob/master/scripts/kconfig/kconfig.py.
+#
+# Here's a demo:
+#
+# Kconfig contents:
+#
+#     config FOO
+#         bool "FOO"
+#
+#     config BAR
+#         bool "BAR"
+#
+#     config BAZ
+#         string "BAZ"
+#
+#     config QAZ
+#         bool "QAZ" if n
+#
+#
+# conf1 contents:
+#
+#     CONFIG_FOO=y
+#
+#
+# conf2 contents:
+#
+#     CONFIG_BAR=y
+#
+#
+# conf3 contents:
+#
+#     # Assigned twice (would generate warning if 'warn_assign_override' was
+#     # True)
+#     # CONFIG_FOO is not set
+#
+#     # Ops... this symbol doesn't exist
+#     CONFIG_OPS=y
+#
+#     CONFIG_BAZ="baz string"
+#
+#
+# conf4 contents:
+#
+#     CONFIG_QAZ=y
+#
+#
+# Running:
+#
+#     $ python(3) merge_config.py Kconfig merged conf1 conf2 conf3 conf4
+#     Merged configuration 'conf1'
+#     Merged configuration 'conf2'
+#     conf3:5: warning: attempt to assign the value 'y' to the undefined symbol OPS
+#     Merged configuration 'conf3'
+#     Merged configuration 'conf4'
+#     Configuration saved to 'merged'
+#     warning: QAZ (defined at Kconfig:10) was assigned the value 'y' but got the value 'n' -- check dependencies
+#     $ cat merged
+#     Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)
+#     # CONFIG_FOO is not set
+#     CONFIG_BAR=y
+#     CONFIG_BAZ="baz string"
+
+from __future__ import print_function
+import sys
+
+from kconfiglib import Kconfig, BOOL, TRISTATE, TRI_TO_STR
+
+
+if len(sys.argv) < 4:
+    sys.exit("usage: merge_config.py Kconfig merged_config config1 [config2 ...]")
+
+kconf = Kconfig(sys.argv[1], suppress_traceback=True)
+
+# Enable warnings for assignments to undefined symbols
+kconf.warn_assign_undef = True
+
+# (This script uses alldefconfig as the base. Other starting states could be
+# set up here as well. The approach in examples/allnoconfig_simpler.py could
+# provide an allnoconfig starting state for example.)
+
+# Disable warnings generated for multiple assignments to the same symbol within
+# a (set of) configuration files. Assigning a symbol multiple times might be
+# done intentionally when merging configuration files.
+kconf.warn_assign_override = False
+kconf.warn_assign_redun = False
+
+# Create a merged configuration by loading the fragments with replace=False.
+# load_config() and write_config() returns a message to print.
+for config in sys.argv[3:]:
+    print(kconf.load_config(config, replace=False))
+
+# Write the merged configuration
+print(kconf.write_config(sys.argv[2]))
+
+# Print warnings for symbols whose actual value doesn't match the assigned
+# value
+for sym in kconf.defined_syms:
+    # Was the symbol assigned to?
+    if sym.user_value is not None:
+        # Tristate values are represented as 0, 1, 2. Having them as
+        # "n", "m", "y" is more convenient here, so convert.
+        if sym.type in (BOOL, TRISTATE):
+            user_value = TRI_TO_STR[sym.user_value]
+        else:
+            user_value = sym.user_value
+
+        if user_value != sym.str_value:
+            print("warning: {} was assigned the value '{}' but got the "
+                  "value '{}' -- check dependencies".format(
+                      sym.name_and_loc, user_value, sym.str_value),
+                  file=sys.stderr)
diff --git a/scripts/Kconfiglib/examples/print_config_tree.py b/scripts/Kconfiglib/examples/print_config_tree.py
new file mode 100644
index 0000000..dc81d9d
--- /dev/null
+++ b/scripts/Kconfiglib/examples/print_config_tree.py
@@ -0,0 +1,199 @@
+# Prints menu entries as a tree with its value in the .config file. This can be
+# handy e.g. for diffing between different .config files or versions of Kconfig files.
+#
+# Usage:
+#
+#   $ make [ARCH=<arch>] scriptconfig SCRIPT=print_config_tree.py [SCRIPT_ARG=<.config>]
+#
+#   If the variable WITH_HELP_DESC is modified to 'True', the help is added
+#   to the symbols.
+#
+# Here's a notation guide. The notation matches the one used by menuconfig
+# (scripts/kconfig/mconf):
+#
+#   [ ] prompt      - Bool
+#   < > prompt      - Tristate
+#   {M} prompt      - Tristate selected to m. Can only be set to m or y.
+#   -*- prompt      - Bool/tristate selected to y, pinning it
+#   -M- prompt      - Tristate selected to m that also has m visibility,
+#                     pinning it to m
+#   (foo) prompt    - String/int/hex symbol with value "foo"
+#   --> prompt      - The selected symbol in a choice in y mode. This
+#                     syntax is unique to this example.
+#
+# When modules are disabled, the .type attribute of TRISTATE symbols and
+# choices automatically changes to BOOL. This trick is used by the C
+# implementation as well, and gives the expected behavior without having to do
+# anything extra here. The original type is available in .orig_type if needed.
+#
+# Example output:
+#
+#   $ make scriptconfig SCRIPT=Kconfiglib/examples/print_config_tree.py [SCRIPT_ARG=<.config file>]
+#
+#   ======== Linux/x86 4.9.82 Kernel Configuration ========
+#
+#   [*] 64-bit kernel (64BIT)
+#       General setup
+#          ()  Cross-compiler tool prefix (CROSS_COMPILE)
+#          [ ] Compile also drivers which will not load (COMPILE_TEST)
+#          ()  Local version - append to kernel release (LOCALVERSION)
+#          [*] Automatically append version information to the version string (LOCALVERSION_AUTO)
+#          -*- Kernel compression mode
+#          ...
+#
+# With the variable WITH_HELP_DESC modified to 'True':
+#
+#   ======== Linux/x86 4.9.82 Kernel Configuration ========
+#
+#   [*] 64-bit kernel - Say yes to build a 64-bit kernel - formerly known as x86_64 Say no to build a 32-bit kernel - formerly known as i386  (64BIT)
+#       General setup
+#           ()  Cross-compiler tool prefix - Same as running 'make CROSS_COMPILE=prefix-' but stored for default make runs in this kernel build directory.  You don't need to set this unless you want the configured kernel build directory to select the cross-compiler automatically.  (CROSS_COMPILE)
+#           [ ] Compile also drivers which will not load - Some drivers can be compiled on a different platform than they are intended to be run on. Despite they cannot be loaded there (or even when they load they cannot be used due to missing HW support), developers still, opposing to distributors, might want to build such drivers to compile-test them.  If you are a developer and want to build everything available, say Y here. If you are a user/distributor, say N here to exclude useless drivers to be distributed.  (COMPILE_TEST)
+#           ...
+
+import sys
+
+from kconfiglib import Kconfig, \
+                       Symbol, MENU, COMMENT, \
+                       BOOL, TRISTATE, STRING, INT, HEX, UNKNOWN, \
+                       expr_value
+
+
+# Add help description to output
+WITH_HELP_DESC = False
+
+
+def indent_print(s, indent):
+    print(indent*" " + s)
+
+
+def value_str(sc):
+    """
+    Returns the value part ("[*]", "<M>", "(foo)" etc.) of a menu entry.
+
+    sc: Symbol or Choice.
+    """
+    if sc.type in (STRING, INT, HEX):
+        return "({})".format(sc.str_value)
+
+    # BOOL or TRISTATE
+
+    # The choice mode is an upper bound on the visibility of choice symbols, so
+    # we can check the choice symbols' own visibility to see if the choice is
+    # in y mode
+    if isinstance(sc, Symbol) and sc.choice and sc.visibility == 2:
+        # For choices in y mode, print '-->' next to the selected symbol
+        return "-->" if sc.choice.selection is sc else "   "
+
+    tri_val_str = (" ", "M", "*")[sc.tri_value]
+
+    if len(sc.assignable) == 1:
+        # Pinned to a single value
+        return "-{}-".format(tri_val_str)
+
+    if sc.type == BOOL:
+        return "[{}]".format(tri_val_str)
+
+    if sc.type == TRISTATE:
+        if sc.assignable == (1, 2):
+            # m and y available
+            return "{" + tri_val_str + "}"  # Gets a bit confusing with .format()
+        return "<{}>".format(tri_val_str)
+
+
+def node_str(node):
+    """
+    Returns the complete menu entry text for a menu node, or "" for invisible
+    menu nodes. Invisible menu nodes are those that lack a prompt or that do
+    not have a satisfied prompt condition.
+
+    Example return value: "[*] Bool symbol (BOOL)"
+
+    The symbol name is printed in parentheses to the right of the prompt.
+    """
+    if not node.prompt:
+        return ""
+
+    # Even for menu nodes for symbols and choices, it's wrong to check
+    # Symbol.visibility / Choice.visibility here. The reason is that a symbol
+    # (and a choice, in theory) can be defined in multiple locations, giving it
+    # multiple menu nodes, which do not necessarily all have the same prompt
+    # visibility. Symbol.visibility / Choice.visibility is calculated as the OR
+    # of the visibility of all the prompts.
+    prompt, prompt_cond = node.prompt
+    if not expr_value(prompt_cond):
+        return ""
+
+    if node.item == MENU:
+        return "    " + prompt
+
+    if node.item == COMMENT:
+        return "    *** {} ***".format(prompt)
+
+    # Symbol or Choice
+
+    sc = node.item
+
+    if sc.type == UNKNOWN:
+        # Skip symbols defined without a type (these are obscure and generate
+        # a warning)
+        return ""
+
+    # Add help text
+    if WITH_HELP_DESC:
+        prompt += ' - ' + str(node.help).replace('\n', ' ').replace('\r', '')
+
+    # {:3} sets the field width to three. Gives nice alignment for empty string
+    # values.
+    res = "{:3} {}".format(value_str(sc), prompt)
+
+    # Don't print the name for unnamed choices (the normal kind)
+    if sc.name is not None:
+        res += " ({})".format(sc.name)
+
+    return res
+
+
+def print_menuconfig_nodes(node, indent):
+    """
+    Prints a tree with all the menu entries rooted at 'node'. Child menu
+    entries are indented.
+    """
+    while node:
+        string = node_str(node)
+        if string:
+            indent_print(string, indent)
+
+        if node.list:
+            print_menuconfig_nodes(node.list, indent + 8)
+
+        node = node.next
+
+
+def print_menuconfig(kconf):
+    """
+    Prints all menu entries for the configuration.
+    """
+    # Print the expanded mainmenu text at the top. This is the same as
+    # kconf.top_node.prompt[0], but with variable references expanded.
+    print("\n======== {} ========\n".format(kconf.mainmenu_text))
+
+    print_menuconfig_nodes(kconf.top_node.list, 0)
+    print("")
+
+
+if __name__ == "__main__":
+
+    # Load Kconfig configuration files
+    kconf = Kconfig(sys.argv[1])
+
+    # Set default .config file or load it from argv
+    if len(sys.argv) == 2:
+        config_filename = '.config'
+    else:
+        config_filename = sys.argv[2]
+
+    kconf.load_config(config_filename)
+
+    # Print the configuration tree
+    print_menuconfig(kconf)
diff --git a/scripts/Kconfiglib/examples/print_sym_info.py b/scripts/Kconfiglib/examples/print_sym_info.py
new file mode 100644
index 0000000..ea6fc72
--- /dev/null
+++ b/scripts/Kconfiglib/examples/print_sym_info.py
@@ -0,0 +1,54 @@
+# Loads a Kconfig and a .config and prints a symbol.
+#
+# Usage:
+#
+#   $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/examples/print_sym_info.py SCRIPT_ARG=<name>
+#
+# Example output for SCRIPT_ARG=MODULES:
+#
+# menuconfig MODULES
+# 	bool
+# 	prompt "Enable loadable module support"
+# 	option modules
+# 	help
+# 	  Kernel modules are small pieces of compiled code which can
+# 	  be inserted in the running kernel, rather than being
+# 	  permanently built into the kernel.  You use the "modprobe"
+# 	  tool to add (and sometimes remove) them.  If you say Y here,
+# 	  many parts of the kernel can be built as modules (by
+# 	  answering M instead of Y where indicated): this is most
+# 	  useful for infrequently used options which are not required
+# 	  for booting.  For more information, see the man pages for
+# 	  modprobe, lsmod, modinfo, insmod and rmmod.
+#
+# 	  If you say Y here, you will need to run "make
+# 	  modules_install" to put the modules under /lib/modules/
+# 	  where modprobe can find them (you may need to be root to do
+# 	  this).
+#
+# 	  If unsure, say Y.
+#
+# value = n
+# visibility = y
+# currently assignable values: n, y
+# defined at init/Kconfig:1674
+
+import sys
+
+from kconfiglib import Kconfig, TRI_TO_STR
+
+
+if len(sys.argv) < 3:
+    sys.exit('Pass symbol name (without "CONFIG_" prefix) with SCRIPT_ARG=<name>')
+
+kconf = Kconfig(sys.argv[1])
+sym = kconf.syms[sys.argv[2]]
+
+print(sym)
+print("value = " + sym.str_value)
+print("visibility = " + TRI_TO_STR[sym.visibility])
+print("currently assignable values: " +
+      ", ".join([TRI_TO_STR[v] for v in sym.assignable]))
+
+for node in sym.nodes:
+    print("defined at {}:{}".format(node.filename, node.linenr))
diff --git a/scripts/Kconfiglib/examples/print_tree.py b/scripts/Kconfiglib/examples/print_tree.py
new file mode 100644
index 0000000..49cb954
--- /dev/null
+++ b/scripts/Kconfiglib/examples/print_tree.py
@@ -0,0 +1,75 @@
+# Prints the menu tree of the configuration. Dependencies between symbols can
+# sometimes implicitly alter the menu structure (see kconfig-language.txt), and
+# that's implemented too.
+#
+# Note: See the Kconfig.node_iter() function as well, which provides a simpler
+# interface for walking the menu tree.
+#
+# Usage:
+#
+#   $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/examples/print_tree.py
+#
+# Example output:
+#
+#   ...
+#   config HAVE_KERNEL_LZO
+#   config HAVE_KERNEL_LZ4
+#   choice
+#     config KERNEL_GZIP
+#     config KERNEL_BZIP2
+#     config KERNEL_LZMA
+#     config KERNEL_XZ
+#     config KERNEL_LZO
+#     config KERNEL_LZ4
+#   config DEFAULT_HOSTNAME
+#   config SWAP
+#   config SYSVIPC
+#     config SYSVIPC_SYSCTL
+#   config POSIX_MQUEUE
+#     config POSIX_MQUEUE_SYSCTL
+#   config CROSS_MEMORY_ATTACH
+#   config FHANDLE
+#   config USELIB
+#   config AUDIT
+#   config HAVE_ARCH_AUDITSYSCALL
+#   config AUDITSYSCALL
+#   config AUDIT_WATCH
+#   config AUDIT_TREE
+#   menu "IRQ subsystem"
+#     config MAY_HAVE_SPARSE_IRQ
+#     config GENERIC_IRQ_LEGACY
+#     config GENERIC_IRQ_PROBE
+#   ...
+
+import sys
+
+from kconfiglib import Kconfig, Symbol, Choice, MENU, COMMENT
+
+
+def indent_print(s, indent):
+    print(indent*" " + s)
+
+
+def print_items(node, indent):
+    while node:
+        if isinstance(node.item, Symbol):
+            indent_print("config " + node.item.name, indent)
+
+        elif isinstance(node.item, Choice):
+            indent_print("choice", indent)
+
+        elif node.item == MENU:
+            indent_print('menu "{}"'.format(node.prompt[0]), indent)
+
+        elif node.item == COMMENT:
+            indent_print('comment "{}"'.format(node.prompt[0]), indent)
+
+
+        if node.list:
+            print_items(node.list, indent + 2)
+
+        node = node.next
+
+
+kconf = Kconfig(sys.argv[1])
+print_items(kconf.top_node, 0)
diff --git a/scripts/Kconfiglib/genconfig.py b/scripts/Kconfiglib/genconfig.py
new file mode 100755
index 0000000..62f065b
--- /dev/null
+++ b/scripts/Kconfiglib/genconfig.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Generates a header file with #defines from the configuration, matching the
+format of include/generated/autoconf.h in the Linux kernel.
+
+Optionally, also writes the configuration output as a .config file. See
+--config-out.
+
+The --sync-deps, --file-list, and --env-list options generate information that
+can be used to avoid needless rebuilds/reconfigurations.
+
+Before writing a header or configuration file, Kconfiglib compares the old
+contents of the file against the new contents. If there's no change, the write
+is skipped. This avoids updating file metadata like the modification time, and
+might save work depending on your build setup.
+
+By default, the configuration is generated from '.config'. A different
+configuration file can be passed in the KCONFIG_CONFIG environment variable.
+
+A custom header string can be inserted at the beginning of generated
+configuration and header files by setting the KCONFIG_CONFIG_HEADER and
+KCONFIG_AUTOHEADER_HEADER environment variables, respectively (this also works
+for other scripts). The string is not automatically made a comment (this is by
+design, to allow anything to be added), and no trailing newline is added, so
+add '/* */', '#', and newlines as appropriate.
+
+See https://www.gnu.org/software/make/manual/make.html#Multi_002dLine for a
+handy way to define multi-line variables in makefiles, for use with custom
+headers. Remember to export the variable to the environment.
+"""
+import argparse
+import os
+import sys
+
+import kconfiglib
+
+
+DEFAULT_SYNC_DEPS_PATH = "deps/"
+
+
+def main():
+    parser = argparse.ArgumentParser(
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        description=__doc__)
+
+    parser.add_argument(
+        "--header-path",
+        metavar="HEADER_FILE",
+        help="""
+Path to write the generated header file to. If not specified, the path in the
+environment variable KCONFIG_AUTOHEADER is used if it is set, and 'config.h'
+otherwise.
+""")
+
+    parser.add_argument(
+        "--config-out",
+        metavar="CONFIG_FILE",
+        help="""
+Write the configuration to CONFIG_FILE. This is useful if you include .config
+files in Makefiles, as the generated configuration file will be a full .config
+file even if .config is outdated. The generated configuration matches what
+olddefconfig would produce. If you use sync-deps, you can include
+deps/auto.conf instead. --config-out is meant for cases where incremental build
+information isn't needed.
+""")
+
+    parser.add_argument(
+        "--sync-deps",
+        metavar="OUTPUT_DIR",
+        nargs="?",
+        const=DEFAULT_SYNC_DEPS_PATH,
+        help="""
+Enable generation of symbol dependency information for incremental builds,
+optionally specifying the output directory (default: {}). See the docstring of
+Kconfig.sync_deps() in Kconfiglib for more information.
+""".format(DEFAULT_SYNC_DEPS_PATH))
+
+    parser.add_argument(
+        "--file-list",
+        metavar="OUTPUT_FILE",
+        help="""
+Write a list of all Kconfig files to OUTPUT_FILE, with one file per line. The
+paths are relative to $srctree (or to the current directory if $srctree is
+unset). Files appear in the order they're 'source'd.
+""")
+
+    parser.add_argument(
+        "--env-list",
+        metavar="OUTPUT_FILE",
+        help="""
+Write a list of all environment variables referenced in Kconfig files to
+OUTPUT_FILE, with one variable per line. Each line has the format NAME=VALUE.
+Only environment variables referenced with the preprocessor $(VAR) syntax are
+included, and not variables referenced with the older $VAR syntax (which is
+only supported for backwards compatibility).
+""")
+
+    parser.add_argument(
+        "kconfig",
+        metavar="KCONFIG",
+        nargs="?",
+        default="Kconfig",
+        help="Top-level Kconfig file (default: Kconfig)")
+
+    args = parser.parse_args()
+
+
+    kconf = kconfiglib.Kconfig(args.kconfig, suppress_traceback=True)
+    kconf.load_config()
+
+    if args.header_path is None:
+        if "KCONFIG_AUTOHEADER" in os.environ:
+            kconf.write_autoconf()
+        else:
+            # Kconfiglib defaults to include/generated/autoconf.h to be
+            # compatible with the C tools. 'config.h' is used here instead for
+            # backwards compatibility. It's probably a saner default for tools
+            # as well.
+            kconf.write_autoconf("config.h")
+    else:
+        kconf.write_autoconf(args.header_path)
+
+    if args.config_out is not None:
+        kconf.write_config(args.config_out, save_old=False)
+
+    if args.sync_deps is not None:
+        kconf.sync_deps(args.sync_deps)
+
+    if args.file_list is not None:
+        with _open_write(args.file_list) as f:
+            for path in kconf.kconfig_filenames:
+                f.write(path + "\n")
+
+    if args.env_list is not None:
+        with _open_write(args.env_list) as f:
+            for env_var in kconf.env_vars:
+                f.write("{}={}\n".format(env_var, os.environ[env_var]))
+
+
+def _open_write(path):
+    # Python 2/3 compatibility. io.open() is available on both, but makes
+    # write() expect 'unicode' strings on Python 2.
+
+    if sys.version_info[0] < 3:
+        return open(path, "w")
+    return open(path, "w", encoding="utf-8")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/Kconfiglib/guiconfig.py b/scripts/Kconfiglib/guiconfig.py
new file mode 100755
index 0000000..7804fdc
--- /dev/null
+++ b/scripts/Kconfiglib/guiconfig.py
@@ -0,0 +1,2324 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Overview
+========
+
+A Tkinter-based menuconfig implementation, based around a treeview control and
+a help display. The interface should feel familiar to people used to qconf
+('make xconfig'). Compatible with both Python 2 and Python 3.
+
+The display can be toggled between showing the full tree and showing just a
+single menu (like menuconfig.py). Only single-menu mode distinguishes between
+symbols defined with 'config' and symbols defined with 'menuconfig'.
+
+A show-all mode is available that shows invisible items in red.
+
+Supports both mouse and keyboard controls. The following keyboard shortcuts are
+available:
+
+  Ctrl-S   : Save configuration
+  Ctrl-O   : Open configuration
+  Ctrl-A   : Toggle show-all mode
+  Ctrl-N   : Toggle show-name mode
+  Ctrl-M   : Toggle single-menu mode
+  Ctrl-F, /: Open jump-to dialog
+  ESC      : Close
+
+Running
+=======
+
+guiconfig.py can be run either as a standalone executable or by calling the
+menuconfig() function with an existing Kconfig instance. The second option is a
+bit inflexible in that it will still load and save .config, etc.
+
+When run in standalone mode, the top-level Kconfig file to load can be passed
+as a command-line argument. With no argument, it defaults to "Kconfig".
+
+The KCONFIG_CONFIG environment variable specifies the .config file to load (if
+it exists) and save. If KCONFIG_CONFIG is unset, ".config" is used.
+
+When overwriting a configuration file, the old version is saved to
+<filename>.old (e.g. .config.old).
+
+$srctree is supported through Kconfiglib.
+"""
+
+# Note: There's some code duplication with menuconfig.py below, especially for
+# the help text. Maybe some of it could be moved into kconfiglib.py or a shared
+# helper script, but OTOH it's pretty nice to have things standalone and
+# customizable.
+
+import errno
+import os
+import sys
+
+_PY2 = sys.version_info[0] < 3
+
+if _PY2:
+    # Python 2
+    from Tkinter import *
+    import ttk
+    import tkFont as font
+    import tkFileDialog as filedialog
+    import tkMessageBox as messagebox
+else:
+    # Python 3
+    from tkinter import *
+    import tkinter.ttk as ttk
+    import tkinter.font as font
+    from tkinter import filedialog, messagebox
+
+from kconfiglib import Symbol, Choice, MENU, COMMENT, MenuNode, \
+                       BOOL, TRISTATE, STRING, INT, HEX, \
+                       AND, OR, \
+                       expr_str, expr_value, split_expr, \
+                       standard_sc_expr_str, \
+                       TRI_TO_STR, TYPE_TO_STR, \
+                       standard_kconfig, standard_config_filename
+
+
+# If True, use GIF image data embedded in this file instead of separate GIF
+# files. See _load_images().
+_USE_EMBEDDED_IMAGES = True
+
+
+# Help text for the jump-to dialog
+_JUMP_TO_HELP = """\
+Type one or more strings/regexes and press Enter to list items that match all
+of them. Python's regex flavor is used (see the 're' module). Double-clicking
+an item will jump to it. Item values can be toggled directly within the dialog.\
+"""
+
+
+def _main():
+    menuconfig(standard_kconfig(__doc__))
+
+
+# Global variables used below:
+#
+#   _root:
+#     The Toplevel instance for the main window
+#
+#   _tree:
+#     The Treeview in the main window
+#
+#   _jump_to_tree:
+#     The Treeview in the jump-to dialog. None if the jump-to dialog isn't
+#     open. Doubles as a flag.
+#
+#   _jump_to_matches:
+#     List of Nodes shown in the jump-to dialog
+#
+#   _menupath:
+#     The Label that shows the menu path of the selected item
+#
+#   _backbutton:
+#     The button shown in single-menu mode for jumping to the parent menu
+#
+#   _status_label:
+#     Label with status text shown at the bottom of the main window
+#     ("Modified", "Saved to ...", etc.)
+#
+#   _id_to_node:
+#     We can't use Node objects directly as Treeview item IDs, so we use their
+#     id()s instead. This dictionary maps Node id()s back to Nodes. (The keys
+#     are actually str(id(node)), just to simplify lookups.)
+#
+#   _cur_menu:
+#     The current menu. Ignored outside single-menu mode.
+#
+#   _show_all_var/_show_name_var/_single_menu_var:
+#     Tkinter Variable instances bound to the corresponding checkboxes
+#
+#   _show_all/_single_menu:
+#     Plain Python bools that track _show_all_var and _single_menu_var, to
+#     speed up and simplify things a bit
+#
+#   _conf_filename:
+#     File to save the configuration to
+#
+#   _minconf_filename:
+#     File to save minimal configurations to
+#
+#   _conf_changed:
+#     True if the configuration has been changed. If False, we don't bother
+#     showing the save-and-quit dialog.
+#
+#     We reset this to False whenever the configuration is saved.
+#
+#   _*_img:
+#     PhotoImage instances for images
+
+
+def menuconfig(kconf):
+    """
+    Launches the configuration interface, returning after the user exits.
+
+    kconf:
+      Kconfig instance to be configured
+    """
+    global _kconf
+    global _conf_filename
+    global _minconf_filename
+    global _jump_to_tree
+    global _cur_menu
+
+    _kconf = kconf
+
+    _jump_to_tree = None
+
+    _create_id_to_node()
+
+    _create_ui()
+
+    # Filename to save configuration to
+    _conf_filename = standard_config_filename()
+
+    # Load existing configuration and check if it's outdated
+    _set_conf_changed(_load_config())
+
+    # Filename to save minimal configuration to
+    _minconf_filename = "defconfig"
+
+    # Current menu in single-menu mode
+    _cur_menu = _kconf.top_node
+
+    # Any visible items in the top menu?
+    if not _shown_menu_nodes(kconf.top_node):
+        # Nothing visible. Start in show-all mode and try again.
+        _show_all_var.set(True)
+        if not _shown_menu_nodes(kconf.top_node):
+            # Give up and show an error. It's nice to be able to assume that
+            # the tree is non-empty in the rest of the code.
+            _root.wait_visibility()
+            messagebox.showerror(
+                "Error",
+                "Empty configuration -- nothing to configure.\n\n"
+                "Check that environment variables are set properly.")
+            _root.destroy()
+            return
+
+    # Build the initial tree
+    _update_tree()
+
+    # Select the first item and focus the Treeview, so that keyboard controls
+    # work immediately
+    _select(_tree, _tree.get_children()[0])
+    _tree.focus_set()
+
+    # Make geometry information available for centering the window. This
+    # indirectly creates the window, so hide it so that it's never shown at the
+    # old location.
+    _root.withdraw()
+    _root.update_idletasks()
+
+    # Center the window
+    _root.geometry("+{}+{}".format(
+        (_root.winfo_screenwidth() - _root.winfo_reqwidth())//2,
+        (_root.winfo_screenheight() - _root.winfo_reqheight())//2))
+
+    # Show it
+    _root.deiconify()
+
+    # Prevent the window from being automatically resized. Otherwise, it
+    # changes size when scrollbars appear/disappear before the user has
+    # manually resized it.
+    _root.geometry(_root.geometry())
+
+    _root.mainloop()
+
+
+def _load_config():
+    # Loads any existing .config file. See the Kconfig.load_config() docstring.
+    #
+    # Returns True if .config is missing or outdated. We always prompt for
+    # saving the configuration in that case.
+
+    print(_kconf.load_config())
+    if not os.path.exists(_conf_filename):
+        # No .config
+        return True
+
+    return _needs_save()
+
+
+def _needs_save():
+    # Returns True if a just-loaded .config file is outdated (would get
+    # modified when saving)
+
+    if _kconf.missing_syms:
+        # Assignments to undefined symbols in the .config
+        return True
+
+    for sym in _kconf.unique_defined_syms:
+        if sym.user_value is None:
+            if sym.config_string:
+                # Unwritten symbol
+                return True
+        elif sym.orig_type in (BOOL, TRISTATE):
+            if sym.tri_value != sym.user_value:
+                # Written bool/tristate symbol, new value
+                return True
+        elif sym.str_value != sym.user_value:
+            # Written string/int/hex symbol, new value
+            return True
+
+    # No need to prompt for save
+    return False
+
+
+def _create_id_to_node():
+    global _id_to_node
+
+    _id_to_node = {str(id(node)): node for node in _kconf.node_iter()}
+
+
+def _create_ui():
+    # Creates the main window UI
+
+    global _root
+    global _tree
+
+    # Create the root window. This initializes Tkinter and makes e.g.
+    # PhotoImage available, so do it early.
+    _root = Tk()
+
+    _load_images()
+    _init_misc_ui()
+    _fix_treeview_issues()
+
+    _create_top_widgets()
+    # Create the pane with the Kconfig tree and description text
+    panedwindow, _tree = _create_kconfig_tree_and_desc(_root)
+    panedwindow.grid(column=0, row=1, sticky="nsew")
+    _create_status_bar()
+
+    _root.columnconfigure(0, weight=1)
+    # Only the pane with the Kconfig tree and description grows vertically
+    _root.rowconfigure(1, weight=1)
+
+    # Start with show-name disabled
+    _do_showname()
+
+    _tree.bind("<Left>", _tree_left_key)
+    _tree.bind("<Right>", _tree_right_key)
+    # Note: Binding this for the jump-to tree as well would cause issues due to
+    # the Tk bug mentioned in _tree_open()
+    _tree.bind("<<TreeviewOpen>>", _tree_open)
+    # add=True to avoid overriding the description text update
+    _tree.bind("<<TreeviewSelect>>", _update_menu_path, add=True)
+
+    _root.bind("<Control-s>", _save)
+    _root.bind("<Control-o>", _open)
+    _root.bind("<Control-a>", _toggle_showall)
+    _root.bind("<Control-n>", _toggle_showname)
+    _root.bind("<Control-m>", _toggle_tree_mode)
+    _root.bind("<Control-f>", _jump_to_dialog)
+    _root.bind("/", _jump_to_dialog)
+    _root.bind("<Escape>", _on_quit)
+
+
+def _load_images():
+    # Loads GIF images, creating the global _*_img PhotoImage variables.
+    # Base64-encoded images embedded in this script are used if
+    # _USE_EMBEDDED_IMAGES is True, and separate image files in the same
+    # directory as the script otherwise.
+    #
+    # Using a global variable indirectly prevents the image from being
+    # garbage-collected. Passing an image to a Tkinter function isn't enough to
+    # keep it alive.
+
+    def load_image(name, data):
+        var_name = "_{}_img".format(name)
+
+        if _USE_EMBEDDED_IMAGES:
+            globals()[var_name] = PhotoImage(data=data, format="gif")
+        else:
+            globals()[var_name] = PhotoImage(
+                file=os.path.join(os.path.dirname(__file__), name + ".gif"),
+                format="gif")
+
+    # Note: Base64 data can be put on the clipboard with
+    #   $ base64 -w0 foo.gif | xclip
+
+    load_image("icon", "R0lGODlhMAAwAPEDAAAAAADQAO7u7v///yH5BAUKAAMALAAAAAAwADAAAAL/nI+gy+2Pokyv2jazuZxryQjiSJZmyXxHeLbumH6sEATvW8OLNtf5bfLZRLFITzgEipDJ4mYxYv6A0ubuqYhWk66tVTE4enHer7jcKvt0LLUw6P45lvEprT6c0+v7OBuqhYdHohcoqIbSAHc4ljhDwrh1UlgSydRCWWlp5wiYZvmSuSh4IzrqV6p4cwhkCsmY+nhK6uJ6t1mrOhuJqfu6+WYiCiwl7HtLjNSZZZis/MeM7NY3TaRKS40ooDeoiVqIultsrav92bi9c3a5KkkOsOJZpSS99m4k/0zPng4Gks9JSbB+8DIcoQfnjwpZCHv5W+ip4aQrKrB0uOikYhiMCBw1/uPoQUMBADs=")
+    load_image("n_bool", "R0lGODdhEAAQAPAAAAgICP///ywAAAAAEAAQAAACIISPacHtvp5kcb5qG85hZ2+BkyiRF8BBaEqtrKkqslEAADs=")
+    load_image("y_bool", "R0lGODdhEAAQAPEAAAgICADQAP///wAAACwAAAAAEAAQAAACMoSPacLtvlh4YrIYsst2cV19AvaVF9CUXBNJJoum7ymrsKuCnhiupIWjSSjAFuWhSCIKADs=")
+    load_image("n_tri", "R0lGODlhEAAQAPD/AAEBAf///yH5BAUKAAIALAAAAAAQABAAAAInlI+pBrAKQnCPSUlXvFhznlkfeGwjKZhnJ65h6nrfi6h0st2QXikFADs=")
+    load_image("m_tri", "R0lGODlhEAAQAPEDAAEBAeQMuv///wAAACH5BAUKAAMALAAAAAAQABAAAAI5nI+pBrAWAhPCjYhiAJQCnWmdoElHGVBoiK5M21ofXFpXRIrgiecqxkuNciZIhNOZFRNI24PhfEoLADs=")
+    load_image("y_tri", "R0lGODlhEAAQAPEDAAICAgDQAP///wAAACH5BAUKAAMALAAAAAAQABAAAAI0nI+pBrAYBhDCRRUypfmergmgZ4xjMpmaw2zmxk7cCB+pWiVqp4MzDwn9FhGZ5WFjIZeGAgA7")
+    load_image("m_my", "R0lGODlhEAAQAPEDAAAAAOQMuv///wAAACH5BAUKAAMALAAAAAAQABAAAAI5nIGpxiAPI2ghxFinq/ZygQhc94zgZopmOLYf67anGr+oZdp02emfV5n9MEHN5QhqICETxkABbQ4KADs=")
+    load_image("y_my", "R0lGODlhEAAQAPH/AAAAAADQAAPRA////yH5BAUKAAQALAAAAAAQABAAAAM+SArcrhCMSSuIM9Q8rxxBWIXawIBkmWonupLd565Um9G1PIs59fKmzw8WnAlusBYR2SEIN6DmAmqBLBxYSAIAOw==")
+    load_image("n_locked", "R0lGODlhEAAQAPABAAAAAP///yH5BAUKAAEALAAAAAAQABAAAAIgjB8AyKwN04pu0vMutpqqz4Hih4ydlnUpyl2r23pxUAAAOw==")
+    load_image("m_locked", "R0lGODlhEAAQAPD/AAAAAOQMuiH5BAUKAAIALAAAAAAQABAAAAIylC8AyKwN04ohnGcqqlZmfXDWI26iInZoyiore05walolV39ftxsYHgL9QBBMBGFEFAAAOw==")
+    load_image("y_locked", "R0lGODlhEAAQAPD/AAAAAADQACH5BAUKAAIALAAAAAAQABAAAAIylC8AyKzNgnlCtoDTwvZwrHydIYpQmR3KWq4uK74IOnp0HQPmnD3cOVlUIAgKsShkFAAAOw==")
+    load_image("not_selected", "R0lGODlhEAAQAPD/AAAAAP///yH5BAUKAAIALAAAAAAQABAAAAIrlA2px6IBw2IpWglOvTYhzmUbGD3kNZ5QqrKn2YrqigCxZoMelU6No9gdCgA7")
+    load_image("selected", "R0lGODlhEAAQAPD/AAAAAP///yH5BAUKAAIALAAAAAAQABAAAAIzlA2px6IBw2IpWglOvTah/kTZhimASJomiqonlLov1qptHTsgKSEzh9H8QI0QzNPwmRoFADs=")
+    load_image("edit", "R0lGODlhEAAQAPIFAAAAAKOLAMuuEPvXCvrxvgAAAAAAAAAAACH5BAUKAAUALAAAAAAQABAAAANCWLqw/gqMBp8cszJxcwVC2FEOEIAi5kVBi3IqWZhuCGMyfdpj2e4pnK+WAshmvxeAcETWlsxPkkBtsqBMa8TIBSQAADs=")
+
+
+def _fix_treeview_issues():
+    # Fixes some Treeview issues
+
+    global _treeview_rowheight
+
+    style = ttk.Style()
+
+    # The treeview rowheight isn't adjusted automatically on high-DPI displays,
+    # so do it ourselves. The font will probably always be TkDefaultFont, but
+    # play it safe and look it up.
+
+    _treeview_rowheight = font.Font(font=style.lookup("Treeview", "font")) \
+        .metrics("linespace") + 2
+
+    style.configure("Treeview", rowheight=_treeview_rowheight)
+
+    # Work around regression in https://core.tcl.tk/tk/tktview?name=509cafafae,
+    # which breaks tag background colors
+
+    for option in "foreground", "background":
+        # Filter out any styles starting with ("!disabled", "!selected", ...).
+        # style.map() returns an empty list for missing options, so this should
+        # be future-safe.
+        style.map(
+            "Treeview",
+            **{option: [elm for elm in style.map("Treeview", query_opt=option)
+                        if elm[:2] != ("!disabled", "!selected")]})
+
+
+def _init_misc_ui():
+    # Does misc. UI initialization, like setting the title, icon, and theme
+
+    _root.title(_kconf.mainmenu_text)
+    # iconphoto() isn't available in Python 2's Tkinter
+    _root.tk.call("wm", "iconphoto", _root._w, "-default", _icon_img)
+    # Reducing the width of the window to 1 pixel makes it move around, at
+    # least on GNOME. Prevent weird stuff like that.
+    _root.minsize(128, 128)
+    _root.protocol("WM_DELETE_WINDOW", _on_quit)
+
+    # Use the 'clam' theme on *nix if it's available. It looks nicer than the
+    # 'default' theme.
+    if _root.tk.call("tk", "windowingsystem") == "x11":
+        style = ttk.Style()
+        if "clam" in style.theme_names():
+            style.theme_use("clam")
+
+
+def _create_top_widgets():
+    # Creates the controls above the Kconfig tree in the main window
+
+    global _show_all_var
+    global _show_name_var
+    global _single_menu_var
+    global _menupath
+    global _backbutton
+
+    topframe = ttk.Frame(_root)
+    topframe.grid(column=0, row=0, sticky="ew")
+
+    ttk.Button(topframe, text="Save", command=_save) \
+        .grid(column=0, row=0, sticky="ew", padx=".05c", pady=".05c")
+
+    ttk.Button(topframe, text="Save as...", command=_save_as) \
+        .grid(column=1, row=0, sticky="ew")
+
+    ttk.Button(topframe, text="Save minimal (advanced)...",
+               command=_save_minimal) \
+        .grid(column=2, row=0, sticky="ew", padx=".05c")
+
+    ttk.Button(topframe, text="Open...", command=_open) \
+        .grid(column=3, row=0)
+
+    ttk.Button(topframe, text="Jump to...", command=_jump_to_dialog) \
+        .grid(column=4, row=0, padx=".05c")
+
+    _show_name_var = BooleanVar()
+    ttk.Checkbutton(topframe, text="Show name", command=_do_showname,
+                    variable=_show_name_var) \
+        .grid(column=0, row=1, sticky="nsew", padx=".05c", pady="0 .05c",
+              ipady=".2c")
+
+    _show_all_var = BooleanVar()
+    ttk.Checkbutton(topframe, text="Show all", command=_do_showall,
+                    variable=_show_all_var) \
+        .grid(column=1, row=1, sticky="nsew", pady="0 .05c")
+
+    # Allow the show-all and single-menu status to be queried via plain global
+    # Python variables, which is faster and simpler
+
+    def show_all_updated(*_):
+        global _show_all
+        _show_all = _show_all_var.get()
+
+    _trace_write(_show_all_var, show_all_updated)
+    _show_all_var.set(False)
+
+    _single_menu_var = BooleanVar()
+    ttk.Checkbutton(topframe, text="Single-menu mode", command=_do_tree_mode,
+                    variable=_single_menu_var) \
+        .grid(column=2, row=1, sticky="nsew", padx=".05c", pady="0 .05c")
+
+    _backbutton = ttk.Button(topframe, text="<--", command=_leave_menu,
+                             state="disabled")
+    _backbutton.grid(column=0, row=4, sticky="nsew", padx=".05c", pady="0 .05c")
+
+    def tree_mode_updated(*_):
+        global _single_menu
+        _single_menu = _single_menu_var.get()
+
+        if _single_menu:
+            _backbutton.grid()
+        else:
+            _backbutton.grid_remove()
+
+    _trace_write(_single_menu_var, tree_mode_updated)
+    _single_menu_var.set(False)
+
+    # Column to the right of the buttons that the menu path extends into, so
+    # that it can grow wider than the buttons
+    topframe.columnconfigure(5, weight=1)
+
+    _menupath = ttk.Label(topframe)
+    _menupath.grid(column=0, row=3, columnspan=6, sticky="w", padx="0.05c",
+                   pady="0 .05c")
+
+
+def _create_kconfig_tree_and_desc(parent):
+    # Creates a Panedwindow with a Treeview that shows Kconfig nodes and a Text
+    # that shows a description of the selected node. Returns a tuple with the
+    # Panedwindow and the Treeview. This code is shared between the main window
+    # and the jump-to dialog.
+
+    panedwindow = ttk.Panedwindow(parent, orient=VERTICAL)
+
+    tree_frame, tree = _create_kconfig_tree(panedwindow)
+    desc_frame, desc = _create_kconfig_desc(panedwindow)
+
+    panedwindow.add(tree_frame, weight=1)
+    panedwindow.add(desc_frame)
+
+    def tree_select(_):
+        # The Text widget does not allow editing the text in its disabled
+        # state. We need to temporarily enable it.
+        desc["state"] = "normal"
+
+        sel = tree.selection()
+        if not sel:
+            desc.delete("1.0", "end")
+            desc["state"] = "disabled"
+            return
+
+        # Text.replace() is not available in Python 2's Tkinter
+        desc.delete("1.0", "end")
+        desc.insert("end", _info_str(_id_to_node[sel[0]]))
+
+        desc["state"] = "disabled"
+
+    tree.bind("<<TreeviewSelect>>", tree_select)
+    tree.bind("<1>", _tree_click)
+    tree.bind("<Double-1>", _tree_double_click)
+    tree.bind("<Return>", _tree_enter)
+    tree.bind("<KP_Enter>", _tree_enter)
+    tree.bind("<space>", _tree_toggle)
+    tree.bind("n", _tree_set_val(0))
+    tree.bind("m", _tree_set_val(1))
+    tree.bind("y", _tree_set_val(2))
+
+    return panedwindow, tree
+
+
+def _create_kconfig_tree(parent):
+    # Creates a Treeview for showing Kconfig nodes
+
+    frame = ttk.Frame(parent)
+
+    tree = ttk.Treeview(frame, selectmode="browse", height=20,
+                        columns=("name",))
+    tree.heading("#0", text="Option", anchor="w")
+    tree.heading("name", text="Name", anchor="w")
+
+    tree.tag_configure("n-bool", image=_n_bool_img)
+    tree.tag_configure("y-bool", image=_y_bool_img)
+    tree.tag_configure("m-tri", image=_m_tri_img)
+    tree.tag_configure("n-tri", image=_n_tri_img)
+    tree.tag_configure("m-tri", image=_m_tri_img)
+    tree.tag_configure("y-tri", image=_y_tri_img)
+    tree.tag_configure("m-my", image=_m_my_img)
+    tree.tag_configure("y-my", image=_y_my_img)
+    tree.tag_configure("n-locked", image=_n_locked_img)
+    tree.tag_configure("m-locked", image=_m_locked_img)
+    tree.tag_configure("y-locked", image=_y_locked_img)
+    tree.tag_configure("not-selected", image=_not_selected_img)
+    tree.tag_configure("selected", image=_selected_img)
+    tree.tag_configure("edit", image=_edit_img)
+    tree.tag_configure("invisible", foreground="red")
+
+    tree.grid(column=0, row=0, sticky="nsew")
+
+    _add_vscrollbar(frame, tree)
+
+    frame.columnconfigure(0, weight=1)
+    frame.rowconfigure(0, weight=1)
+
+    # Create items for all menu nodes. These can be detached/moved later.
+    # Micro-optimize this a bit.
+    insert = tree.insert
+    id_ = id
+    Symbol_ = Symbol
+    for node in _kconf.node_iter():
+        item = node.item
+        insert("", "end", iid=id_(node),
+               values=item.name if item.__class__ is Symbol_ else "")
+
+    return frame, tree
+
+
+def _create_kconfig_desc(parent):
+    # Creates a Text for showing the description of the selected Kconfig node
+
+    frame = ttk.Frame(parent)
+
+    desc = Text(frame, height=12, wrap="none", borderwidth=0,
+                state="disabled")
+    desc.grid(column=0, row=0, sticky="nsew")
+
+    # Work around not being to Ctrl-C/V text from a disabled Text widget, with a
+    # tip found in https://stackoverflow.com/questions/3842155/is-there-a-way-to-make-the-tkinter-text-widget-read-only
+    desc.bind("<1>", lambda _: desc.focus_set())
+
+    _add_vscrollbar(frame, desc)
+
+    frame.columnconfigure(0, weight=1)
+    frame.rowconfigure(0, weight=1)
+
+    return frame, desc
+
+
+def _add_vscrollbar(parent, widget):
+    # Adds a vertical scrollbar to 'widget' that's only shown as needed
+
+    vscrollbar = ttk.Scrollbar(parent, orient="vertical",
+                               command=widget.yview)
+    vscrollbar.grid(column=1, row=0, sticky="ns")
+
+    def yscrollcommand(first, last):
+        # Only show the scrollbar when needed. 'first' and 'last' are
+        # strings.
+        if float(first) <= 0.0 and float(last) >= 1.0:
+            vscrollbar.grid_remove()
+        else:
+            vscrollbar.grid()
+
+        vscrollbar.set(first, last)
+
+    widget["yscrollcommand"] = yscrollcommand
+
+
+def _create_status_bar():
+    # Creates the status bar at the bottom of the main window
+
+    global _status_label
+
+    _status_label = ttk.Label(_root, anchor="e", padding="0 0 0.4c 0")
+    _status_label.grid(column=0, row=3, sticky="ew")
+
+
+def _set_status(s):
+    # Sets the text in the status bar to 's'
+
+    _status_label["text"] = s
+
+
+def _set_conf_changed(changed):
+    # Updates the status re. whether there are unsaved changes
+
+    global _conf_changed
+
+    _conf_changed = changed
+    if changed:
+        _set_status("Modified")
+
+
+def _update_tree():
+    # Updates the Kconfig tree in the main window by first detaching all nodes
+    # and then updating and reattaching them. The tree structure might have
+    # changed.
+
+    # If a selected/focused item is detached and later reattached, it stays
+    # selected/focused. That can give multiple selections even though
+    # selectmode=browse. Save and later restore the selection and focus as a
+    # workaround.
+    old_selection = _tree.selection()
+    old_focus = _tree.focus()
+
+    # Detach all tree items before re-stringing them. This is relatively fast,
+    # luckily.
+    _tree.detach(*_id_to_node.keys())
+
+    if _single_menu:
+        _build_menu_tree()
+    else:
+        _build_full_tree(_kconf.top_node)
+
+    _tree.selection_set(old_selection)
+    _tree.focus(old_focus)
+
+
+def _build_full_tree(menu):
+    # Updates the tree starting from menu.list, in full-tree mode. To speed
+    # things up, only open menus are updated. The menu-at-a-time logic here is
+    # to deal with invisible items that can show up outside show-all mode (see
+    # _shown_full_nodes()).
+
+    for node in _shown_full_nodes(menu):
+        _add_to_tree(node, _kconf.top_node)
+
+        # _shown_full_nodes() includes nodes from menus rooted at symbols, so
+        # we only need to check "real" menus/choices here
+        if node.list and not isinstance(node.item, Symbol):
+            if _tree.item(id(node), "open"):
+                _build_full_tree(node)
+            else:
+                # We're just probing here, so _shown_menu_nodes() will work
+                # fine, and might be a bit faster
+                shown = _shown_menu_nodes(node)
+                if shown:
+                    # Dummy element to make the open/closed toggle appear
+                    _tree.move(id(shown[0]), id(shown[0].parent), "end")
+
+
+def _shown_full_nodes(menu):
+    # Returns the list of menu nodes shown in 'menu' (a menu node for a menu)
+    # for full-tree mode. A tricky detail is that invisible items need to be
+    # shown if they have visible children.
+
+    def rec(node):
+        res = []
+
+        while node:
+            if _visible(node) or _show_all:
+                res.append(node)
+                if node.list and isinstance(node.item, Symbol):
+                    # Nodes from menu created from dependencies
+                    res += rec(node.list)
+
+            elif node.list and isinstance(node.item, Symbol):
+                # Show invisible symbols (defined with either 'config' and
+                # 'menuconfig') if they have visible children. This can happen
+                # for an m/y-valued symbol with an optional prompt
+                # ('prompt "foo" is COND') that is currently disabled.
+                shown_children = rec(node.list)
+                if shown_children:
+                    res.append(node)
+                    res += shown_children
+
+            node = node.next
+
+        return res
+
+    return rec(menu.list)
+
+
+def _build_menu_tree():
+    # Updates the tree in single-menu mode. See _build_full_tree() as well.
+
+    for node in _shown_menu_nodes(_cur_menu):
+        _add_to_tree(node, _cur_menu)
+
+
+def _shown_menu_nodes(menu):
+    # Used for single-menu mode. Similar to _shown_full_nodes(), but doesn't
+    # include children of symbols defined with 'menuconfig'.
+
+    def rec(node):
+        res = []
+
+        while node:
+            if _visible(node) or _show_all:
+                res.append(node)
+                if node.list and not node.is_menuconfig:
+                    res += rec(node.list)
+
+            elif node.list and isinstance(node.item, Symbol):
+                shown_children = rec(node.list)
+                if shown_children:
+                    # Invisible item with visible children
+                    res.append(node)
+                    if not node.is_menuconfig:
+                        res += shown_children
+
+            node = node.next
+
+        return res
+
+    return rec(menu.list)
+
+
+def _visible(node):
+    # Returns True if the node should appear in the menu (outside show-all
+    # mode)
+
+    return node.prompt and expr_value(node.prompt[1]) and not \
+        (node.item == MENU and not expr_value(node.visibility))
+
+
+def _add_to_tree(node, top):
+    # Adds 'node' to the tree, at the end of its menu. We rely on going through
+    # the nodes linearly to get the correct order. 'top' holds the menu that
+    # corresponds to the top-level menu, and can vary in single-menu mode.
+
+    parent = node.parent
+    _tree.move(id(node), "" if parent is top else id(parent), "end")
+    _tree.item(
+        id(node),
+        text=_node_str(node),
+        # The _show_all test avoids showing invisible items in red outside
+        # show-all mode, which could look confusing/broken. Invisible symbols
+        # are shown outside show-all mode if an invisible symbol has visible
+        # children in an implicit menu.
+        tags=_img_tag(node) if _visible(node) or not _show_all else
+            _img_tag(node) + " invisible")
+
+
+def _node_str(node):
+    # Returns the string shown to the right of the image (if any) for the node
+
+    if node.prompt:
+        if node.item == COMMENT:
+            s = "*** {} ***".format(node.prompt[0])
+        else:
+            s = node.prompt[0]
+
+        if isinstance(node.item, Symbol):
+            sym = node.item
+
+            # Print "(NEW)" next to symbols without a user value (from e.g. a
+            # .config), but skip it for choice symbols in choices in y mode,
+            # and for symbols of UNKNOWN type (which generate a warning though)
+            if sym.user_value is None and sym.type and not \
+                (sym.choice and sym.choice.tri_value == 2):
+
+                s += " (NEW)"
+
+    elif isinstance(node.item, Symbol):
+        # Symbol without prompt (can show up in show-all)
+        s = "<{}>".format(node.item.name)
+
+    else:
+        # Choice without prompt. Use standard_sc_expr_str() so that it shows up
+        # as '<choice (name if any)>'.
+        s = standard_sc_expr_str(node.item)
+
+
+    if isinstance(node.item, Symbol):
+        sym = node.item
+        if sym.orig_type == STRING:
+            s += ": " + sym.str_value
+        elif sym.orig_type in (INT, HEX):
+            s = "({}) {}".format(sym.str_value, s)
+
+    elif isinstance(node.item, Choice) and node.item.tri_value == 2:
+        # Print the prompt of the selected symbol after the choice for
+        # choices in y mode
+        sym = node.item.selection
+        if sym:
+            for sym_node in sym.nodes:
+                # Use the prompt used at this choice location, in case the
+                # choice symbol is defined in multiple locations
+                if sym_node.parent is node and sym_node.prompt:
+                    s += " ({})".format(sym_node.prompt[0])
+                    break
+            else:
+                # If the symbol isn't defined at this choice location, then
+                # just use whatever prompt we can find for it
+                for sym_node in sym.nodes:
+                    if sym_node.prompt:
+                        s += " ({})".format(sym_node.prompt[0])
+                        break
+
+    # In single-menu mode, print "--->" next to nodes that have menus that can
+    # potentially be entered. Print "----" if the menu is empty. We don't allow
+    # those to be entered.
+    if _single_menu and node.is_menuconfig:
+        s += "  --->" if _shown_menu_nodes(node) else "  ----"
+
+    return s
+
+
+def _img_tag(node):
+    # Returns the tag for the image that should be shown next to 'node', or the
+    # empty string if it shouldn't have an image
+
+    item = node.item
+
+    if item in (MENU, COMMENT) or not item.orig_type:
+        return ""
+
+    if item.orig_type in (STRING, INT, HEX):
+        return "edit"
+
+    # BOOL or TRISTATE
+
+    if _is_y_mode_choice_sym(item):
+        # Choice symbol in y-mode choice
+        return "selected" if item.choice.selection is item else "not-selected"
+
+    if len(item.assignable) <= 1:
+        # Pinned to a single value
+        return "" if isinstance(item, Choice) else item.str_value + "-locked"
+
+    if item.type == BOOL:
+        return item.str_value + "-bool"
+
+    # item.type == TRISTATE
+    if item.assignable == (1, 2):
+        return item.str_value + "-my"
+    return item.str_value + "-tri"
+
+
+def _is_y_mode_choice_sym(item):
+    # The choice mode is an upper bound on the visibility of choice symbols, so
+    # we can check the choice symbols' own visibility to see if the choice is
+    # in y mode
+    return isinstance(item, Symbol) and item.choice and item.visibility == 2
+
+
+def _tree_click(event):
+    # Click on the Kconfig Treeview
+
+    tree = event.widget
+    if tree.identify_element(event.x, event.y) == "image":
+        item = tree.identify_row(event.y)
+        # Select the item before possibly popping up a dialog for
+        # string/int/hex items, so that its help is visible
+        _select(tree, item)
+        _change_node(_id_to_node[item], tree.winfo_toplevel())
+        return "break"
+
+
+def _tree_double_click(event):
+    # Double-click on the Kconfig treeview
+
+    # Do an extra check to avoid weirdness when double-clicking in the tree
+    # heading area
+    if not _in_heading(event):
+        return _tree_enter(event)
+
+
+def _in_heading(event):
+    # Returns True if 'event' took place in the tree heading
+
+    tree = event.widget
+    return hasattr(tree, "identify_region") and \
+        tree.identify_region(event.x, event.y) in ("heading", "separator")
+
+
+def _tree_enter(event):
+    # Enter press or double-click within the Kconfig treeview. Prefer to
+    # open/close/enter menus, but toggle the value if that's not possible.
+
+    tree = event.widget
+    sel = tree.focus()
+    if sel:
+        node = _id_to_node[sel]
+
+        if tree.get_children(sel):
+            _tree_toggle_open(sel)
+        elif _single_menu_mode_menu(node, tree):
+            _enter_menu_and_select_first(node)
+        else:
+            _change_node(node, tree.winfo_toplevel())
+
+        return "break"
+
+
+def _tree_toggle(event):
+    # Space press within the Kconfig treeview. Prefer to toggle the value, but
+    # open/close/enter the menu if that's not possible.
+
+    tree = event.widget
+    sel = tree.focus()
+    if sel:
+        node = _id_to_node[sel]
+
+        if _changeable(node):
+            _change_node(node, tree.winfo_toplevel())
+        elif _single_menu_mode_menu(node, tree):
+            _enter_menu_and_select_first(node)
+        elif tree.get_children(sel):
+            _tree_toggle_open(sel)
+
+        return "break"
+
+
+def _tree_left_key(_):
+    # Left arrow key press within the Kconfig treeview
+
+    if _single_menu:
+        # Leave the current menu in single-menu mode
+        _leave_menu()
+        return "break"
+
+    # Otherwise, default action
+
+
+def _tree_right_key(_):
+    # Right arrow key press within the Kconfig treeview
+
+    sel = _tree.focus()
+    if sel:
+        node = _id_to_node[sel]
+        # If the node can be entered in single-menu mode, do it
+        if _single_menu_mode_menu(node, _tree):
+            _enter_menu_and_select_first(node)
+            return "break"
+
+    # Otherwise, default action
+
+
+def _single_menu_mode_menu(node, tree):
+    # Returns True if single-menu mode is on and 'node' is an (interface)
+    # menu that can be entered
+
+    return _single_menu and tree is _tree and node.is_menuconfig and \
+           _shown_menu_nodes(node)
+
+
+def _changeable(node):
+    # Returns True if 'node' is a Symbol/Choice whose value can be changed
+
+    sc = node.item
+
+    if not isinstance(sc, (Symbol, Choice)):
+        return False
+
+    # This will hit for invisible symbols, which appear in show-all mode and
+    # when an invisible symbol has visible children (which can happen e.g. for
+    # symbols with optional prompts)
+    if not (node.prompt and expr_value(node.prompt[1])):
+        return False
+
+    return sc.orig_type in (STRING, INT, HEX) or len(sc.assignable) > 1 \
+           or _is_y_mode_choice_sym(sc)
+
+
+def _tree_toggle_open(item):
+    # Opens/closes the Treeview item 'item'
+
+    if _tree.item(item, "open"):
+        _tree.item(item, open=False)
+    else:
+        node = _id_to_node[item]
+        if not isinstance(node.item, Symbol):
+            # Can only get here in full-tree mode
+            _build_full_tree(node)
+        _tree.item(item, open=True)
+
+
+def _tree_set_val(tri_val):
+    def tree_set_val(event):
+        # n/m/y press within the Kconfig treeview
+
+        # Sets the value of the currently selected item to 'tri_val', if that
+        # value can be assigned
+
+        sel = event.widget.focus()
+        if sel:
+            sc = _id_to_node[sel].item
+            if isinstance(sc, (Symbol, Choice)) and tri_val in sc.assignable:
+                _set_val(sc, tri_val)
+
+    return tree_set_val
+
+
+def _tree_open(_):
+    # Lazily populates the Kconfig tree when menus are opened in full-tree mode
+
+    if _single_menu:
+        # Work around https://core.tcl.tk/tk/tktview?name=368fa4561e
+        # ("ttk::treeview open/closed indicators can be toggled while hidden").
+        # Clicking on the hidden indicator will call _build_full_tree() in
+        # single-menu mode otherwise.
+        return
+
+    node = _id_to_node[_tree.focus()]
+    # _shown_full_nodes() includes nodes from menus rooted at symbols, so we
+    # only need to check "real" menus and choices here
+    if not isinstance(node.item, Symbol):
+        _build_full_tree(node)
+
+
+def _update_menu_path(_):
+    # Updates the displayed menu path when nodes are selected in the Kconfig
+    # treeview
+
+    sel = _tree.selection()
+    _menupath["text"] = _menu_path_info(_id_to_node[sel[0]]) if sel else ""
+
+
+def _item_row(item):
+    # Returns the row number 'item' appears on within the Kconfig treeview,
+    # starting from the top of the tree. Used to preserve scrolling.
+    #
+    # ttkTreeview.c in the Tk sources defines a RowNumber() function that does
+    # the same thing, but it's not exposed.
+
+    row = 0
+
+    while True:
+        prev = _tree.prev(item)
+        if prev:
+            item = prev
+            row += _n_rows(item)
+        else:
+            item = _tree.parent(item)
+            if not item:
+                return row
+            row += 1
+
+
+def _n_rows(item):
+    # _item_row() helper. Returns the number of rows occupied by 'item' and #
+    # its children.
+
+    rows = 1
+
+    if _tree.item(item, "open"):
+        for child in _tree.get_children(item):
+            rows += _n_rows(child)
+
+    return rows
+
+
+def _attached(item):
+    # Heuristic for checking if a Treeview item is attached. Doesn't seem to be
+    # good APIs for this. Might fail for super-obscure cases with tiny trees,
+    # but you'd just get a small scroll mess-up.
+
+    return bool(_tree.next(item) or _tree.prev(item) or _tree.parent(item))
+
+
+def _change_node(node, parent):
+    # Toggles/changes the value of 'node'. 'parent' is the parent window
+    # (either the main window or the jump-to dialog), in case we need to pop up
+    # a dialog.
+
+    if not _changeable(node):
+        return
+
+    # sc = symbol/choice
+    sc = node.item
+
+    if sc.type in (INT, HEX, STRING):
+        s = _set_val_dialog(node, parent)
+
+        # Tkinter can return 'unicode' strings on Python 2, which Kconfiglib
+        # can't deal with. UTF-8-encode the string to work around it.
+        if _PY2 and isinstance(s, unicode):
+            s = s.encode("utf-8", "ignore")
+
+        if s is not None:
+            _set_val(sc, s)
+
+    elif len(sc.assignable) == 1:
+        # Handles choice symbols for choices in y mode, which are a special
+        # case: .assignable can be (2,) while .tri_value is 0.
+        _set_val(sc, sc.assignable[0])
+
+    else:
+        # Set the symbol to the value after the current value in
+        # sc.assignable, with wrapping
+        val_index = sc.assignable.index(sc.tri_value)
+        _set_val(sc, sc.assignable[(val_index + 1) % len(sc.assignable)])
+
+
+def _set_val(sc, val):
+    # Wrapper around Symbol/Choice.set_value() for updating the menu state and
+    # _conf_changed
+
+    # Use the string representation of tristate values. This makes the format
+    # consistent for all symbol types.
+    if val in TRI_TO_STR:
+        val = TRI_TO_STR[val]
+
+    if val != sc.str_value:
+        sc.set_value(val)
+        _set_conf_changed(True)
+
+        # Update the tree and try to preserve the scroll. Do a cheaper variant
+        # than in the show-all case, that might mess up the scroll slightly in
+        # rare cases, but is fast and flicker-free.
+
+        stayput = _loc_ref_item()  # Item to preserve scroll for
+        old_row = _item_row(stayput)
+
+        _update_tree()
+
+        # If the reference item disappeared (can happen if the change was done
+        # from the jump-to dialog), then avoid messing with the scroll and hope
+        # for the best
+        if _attached(stayput):
+            _tree.yview_scroll(_item_row(stayput) - old_row, "units")
+
+        if _jump_to_tree:
+            _update_jump_to_display()
+
+
+def _set_val_dialog(node, parent):
+    # Pops up a dialog for setting the value of the string/int/hex
+    # symbol at node 'node'. 'parent' is the parent window.
+
+    def ok(_=None):
+        # No 'nonlocal' in Python 2
+        global _entry_res
+
+        s = entry.get()
+        if sym.type == HEX and not s.startswith(("0x", "0X")):
+            s = "0x" + s
+
+        if _check_valid(dialog, entry, sym, s):
+            _entry_res = s
+            dialog.destroy()
+
+    def cancel(_=None):
+        global _entry_res
+        _entry_res = None
+        dialog.destroy()
+
+    sym = node.item
+
+    dialog = Toplevel(parent)
+    dialog.title("Enter {} value".format(TYPE_TO_STR[sym.type]))
+    dialog.resizable(False, False)
+    dialog.transient(parent)
+    dialog.protocol("WM_DELETE_WINDOW", cancel)
+
+    ttk.Label(dialog, text=node.prompt[0] + ":") \
+        .grid(column=0, row=0, columnspan=2, sticky="w", padx=".3c",
+              pady=".2c .05c")
+
+    entry = ttk.Entry(dialog, width=30)
+    # Start with the previous value in the editbox, selected
+    entry.insert(0, sym.str_value)
+    entry.selection_range(0, "end")
+    entry.grid(column=0, row=1, columnspan=2, sticky="ew", padx=".3c")
+    entry.focus_set()
+
+    range_info = _range_info(sym)
+    if range_info:
+        ttk.Label(dialog, text=range_info) \
+            .grid(column=0, row=2, columnspan=2, sticky="w", padx=".3c",
+                  pady=".2c 0")
+
+    ttk.Button(dialog, text="OK", command=ok) \
+        .grid(column=0, row=4 if range_info else 3, sticky="e", padx=".3c",
+              pady=".4c")
+
+    ttk.Button(dialog, text="Cancel", command=cancel) \
+        .grid(column=1, row=4 if range_info else 3, padx="0 .3c")
+
+    # Give all horizontal space to the grid cell with the OK button, so that
+    # Cancel moves to the right
+    dialog.columnconfigure(0, weight=1)
+
+    _center_on_root(dialog)
+
+    # Hack to scroll the entry so that the end of the text is shown, from
+    # https://stackoverflow.com/questions/29334544/why-does-tkinters-entry-xview-moveto-fail.
+    # Related Tk ticket: https://core.tcl.tk/tk/info/2513186fff
+    def scroll_entry(_):
+        _root.update_idletasks()
+        entry.unbind("<Expose>")
+        entry.xview_moveto(1)
+    entry.bind("<Expose>", scroll_entry)
+
+    # The dialog must be visible before we can grab the input
+    dialog.wait_visibility()
+    dialog.grab_set()
+
+    dialog.bind("<Return>", ok)
+    dialog.bind("<KP_Enter>", ok)
+    dialog.bind("<Escape>", cancel)
+
+    # Wait for the user to be done with the dialog
+    parent.wait_window(dialog)
+
+    # Regrab the input in the parent
+    parent.grab_set()
+
+    return _entry_res
+
+
+def _center_on_root(dialog):
+    # Centers 'dialog' on the root window. It often ends up at some bad place
+    # like the top-left corner of the screen otherwise. See the menuconfig()
+    # function, which has similar logic.
+
+    dialog.withdraw()
+    _root.update_idletasks()
+
+    dialog_width = dialog.winfo_reqwidth()
+    dialog_height = dialog.winfo_reqheight()
+
+    screen_width = _root.winfo_screenwidth()
+    screen_height = _root.winfo_screenheight()
+
+    x = _root.winfo_rootx() + (_root.winfo_width() - dialog_width)//2
+    y = _root.winfo_rooty() + (_root.winfo_height() - dialog_height)//2
+
+    # Clamp so that no part of the dialog is outside the screen
+    if x + dialog_width > screen_width:
+        x = screen_width - dialog_width
+    elif x < 0:
+        x = 0
+    if y + dialog_height > screen_height:
+        y = screen_height - dialog_height
+    elif y < 0:
+        y = 0
+
+    dialog.geometry("+{}+{}".format(x, y))
+
+    dialog.deiconify()
+
+
+def _check_valid(dialog, entry, sym, s):
+    # Returns True if the string 's' is a well-formed value for 'sym'.
+    # Otherwise, pops up an error and returns False.
+
+    if sym.type not in (INT, HEX):
+        # Anything goes for non-int/hex symbols
+        return True
+
+    base = 10 if sym.type == INT else 16
+    try:
+        int(s, base)
+    except ValueError:
+        messagebox.showerror(
+            "Bad value",
+            "'{}' is a malformed {} value".format(
+                s, TYPE_TO_STR[sym.type]),
+            parent=dialog)
+        entry.focus_set()
+        return False
+
+    for low_sym, high_sym, cond in sym.ranges:
+        if expr_value(cond):
+            low_s = low_sym.str_value
+            high_s = high_sym.str_value
+
+            if not int(low_s, base) <= int(s, base) <= int(high_s, base):
+                messagebox.showerror(
+                    "Value out of range",
+                    "{} is outside the range {}-{}".format(s, low_s, high_s),
+                    parent=dialog)
+                entry.focus_set()
+                return False
+
+            break
+
+    return True
+
+
+def _range_info(sym):
+    # Returns a string with information about the valid range for the symbol
+    # 'sym', or None if 'sym' doesn't have a range
+
+    if sym.type in (INT, HEX):
+        for low, high, cond in sym.ranges:
+            if expr_value(cond):
+                return "Range: {}-{}".format(low.str_value, high.str_value)
+
+    return None
+
+
+def _save(_=None):
+    # Tries to save the configuration
+
+    if _try_save(_kconf.write_config, _conf_filename, "configuration"):
+        _set_conf_changed(False)
+
+    _tree.focus_set()
+
+
+def _save_as():
+    # Pops up a dialog for saving the configuration to a specific location
+
+    global _conf_filename
+
+    filename = _conf_filename
+    while True:
+        filename = filedialog.asksaveasfilename(
+            title="Save configuration as",
+            initialdir=os.path.dirname(filename),
+            initialfile=os.path.basename(filename),
+            parent=_root)
+
+        if not filename:
+            break
+
+        if _try_save(_kconf.write_config, filename, "configuration"):
+            _conf_filename = filename
+            break
+
+    _tree.focus_set()
+
+
+def _save_minimal():
+    # Pops up a dialog for saving a minimal configuration (defconfig) to a
+    # specific location
+
+    global _minconf_filename
+
+    filename = _minconf_filename
+    while True:
+        filename = filedialog.asksaveasfilename(
+            title="Save minimal configuration as",
+            initialdir=os.path.dirname(filename),
+            initialfile=os.path.basename(filename),
+            parent=_root)
+
+        if not filename:
+            break
+
+        if _try_save(_kconf.write_min_config, filename,
+                     "minimal configuration"):
+
+            _minconf_filename = filename
+            break
+
+    _tree.focus_set()
+
+
+def _open(_=None):
+    # Pops up a dialog for loading a configuration
+
+    global _conf_filename
+
+    if _conf_changed and \
+        not messagebox.askokcancel(
+            "Unsaved changes",
+            "You have unsaved changes. Load new configuration anyway?"):
+
+        return
+
+    filename = _conf_filename
+    while True:
+        filename = filedialog.askopenfilename(
+            title="Open configuration",
+            initialdir=os.path.dirname(filename),
+            initialfile=os.path.basename(filename),
+            parent=_root)
+
+        if not filename:
+            break
+
+        if _try_load(filename):
+            # Maybe something fancier could be done here later to try to
+            # preserve the scroll
+
+            _conf_filename = filename
+            _set_conf_changed(_needs_save())
+
+            if _single_menu and not _shown_menu_nodes(_cur_menu):
+                # Turn on show-all if we're in single-menu mode and would end
+                # up with an empty menu
+                _show_all_var.set(True)
+
+            _update_tree()
+
+            break
+
+    _tree.focus_set()
+
+
+def _toggle_showname(_):
+    # Toggles show-name mode on/off
+
+    _show_name_var.set(not _show_name_var.get())
+    _do_showname()
+
+
+def _do_showname():
+    # Updates the UI for the current show-name setting
+
+    # Columns do not automatically shrink/expand, so we have to update
+    # column widths ourselves
+
+    tree_width = _tree.winfo_width()
+
+    if _show_name_var.get():
+        _tree["displaycolumns"] = ("name",)
+        _tree["show"] = "tree headings"
+        name_width = tree_width//3
+        _tree.column("#0", width=max(tree_width - name_width, 1))
+        _tree.column("name", width=name_width)
+    else:
+        _tree["displaycolumns"] = ()
+        _tree["show"] = "tree"
+        _tree.column("#0", width=tree_width)
+
+    _tree.focus_set()
+
+
+def _toggle_showall(_):
+    # Toggles show-all mode on/off
+
+    _show_all_var.set(not _show_all)
+    _do_showall()
+
+
+def _do_showall():
+    # Updates the UI for the current show-all setting
+
+    # Don't allow turning off show-all if we'd end up with no visible nodes
+    if _nothing_shown():
+        _show_all_var.set(True)
+        return
+
+    # Save scroll information. old_scroll can end up negative here, if the
+    # reference item isn't shown (only invisible items on the screen, and
+    # show-all being turned off).
+
+    stayput = _vis_loc_ref_item()
+    # Probe the middle of the first row, to play it safe. identify_row(0) seems
+    # to return the row before the top row.
+    old_scroll = _item_row(stayput) - \
+        _item_row(_tree.identify_row(_treeview_rowheight//2))
+
+    _update_tree()
+
+    if _show_all:
+        # Deep magic: Unless we call update_idletasks(), the scroll adjustment
+        # below is restricted to the height of the old tree, instead of the
+        # height of the new tree. Since the tree with show-all on is guaranteed
+        # to be taller, and we want the maximum range, we only call it when
+        # turning show-all on.
+        #
+        # Strictly speaking, something similar ought to be done when changing
+        # symbol values, but it causes annoying flicker, and in 99% of cases
+        # things work anyway there (with usually minor scroll mess-ups in the
+        # 1% case).
+        _root.update_idletasks()
+
+    # Restore scroll
+    _tree.yview(_item_row(stayput) - old_scroll)
+
+    _tree.focus_set()
+
+
+def _nothing_shown():
+    # _do_showall() helper. Returns True if no nodes would get
+    # shown with the current show-all setting. Also handles the
+    # (obscure) case when there are no visible nodes in the entire
+    # tree, meaning guiconfig was automatically started in
+    # show-all mode, which mustn't be turned off.
+
+    return not _shown_menu_nodes(
+        _cur_menu if _single_menu else _kconf.top_node)
+
+
+def _toggle_tree_mode(_):
+    # Toggles single-menu mode on/off
+
+    _single_menu_var.set(not _single_menu)
+    _do_tree_mode()
+
+
+def _do_tree_mode():
+    # Updates the UI for the current tree mode (full-tree or single-menu)
+
+    loc_ref_node = _id_to_node[_loc_ref_item()]
+
+    if not _single_menu:
+        # _jump_to() -> _enter_menu() already updates the tree, but
+        # _jump_to() -> load_parents() doesn't, because it isn't always needed.
+        # We always need to update the tree here, e.g. to add/remove "--->".
+        _update_tree()
+
+    _jump_to(loc_ref_node)
+    _tree.focus_set()
+
+
+def _enter_menu_and_select_first(menu):
+    # Enters the menu 'menu' and selects the first item. Used in single-menu
+    # mode.
+
+    _enter_menu(menu)
+    _select(_tree, _tree.get_children()[0])
+
+
+def _enter_menu(menu):
+    # Enters the menu 'menu'. Used in single-menu mode.
+
+    global _cur_menu
+
+    _cur_menu = menu
+    _update_tree()
+
+    _backbutton["state"] = "disabled" if menu is _kconf.top_node else "normal"
+
+
+def _leave_menu():
+    # Leaves the current menu. Used in single-menu mode.
+
+    global _cur_menu
+
+    if _cur_menu is not _kconf.top_node:
+        old_menu = _cur_menu
+
+        _cur_menu = _parent_menu(_cur_menu)
+        _update_tree()
+
+        _select(_tree, id(old_menu))
+
+        if _cur_menu is _kconf.top_node:
+            _backbutton["state"] = "disabled"
+
+    _tree.focus_set()
+
+
+def _select(tree, item):
+    # Selects, focuses, and see()s 'item' in 'tree'
+
+    tree.selection_set(item)
+    tree.focus(item)
+    tree.see(item)
+
+
+def _loc_ref_item():
+    # Returns a Treeview item that can serve as a reference for the current
+    # scroll location. We try to make this item stay on the same row on the
+    # screen when updating the tree.
+
+    # If the selected item is visible, use that
+    sel = _tree.selection()
+    if sel and _tree.bbox(sel[0]):
+        return sel[0]
+
+    # Otherwise, use the middle item on the screen. If it doesn't exist, the
+    # tree is probably really small, so use the first item in the entire tree.
+    return _tree.identify_row(_tree.winfo_height()//2) or \
+        _tree.get_children()[0]
+
+
+def _vis_loc_ref_item():
+    # Like _loc_ref_item(), but finds a visible item around the reference item.
+    # Used when changing show-all mode, where non-visible (red) items will
+    # disappear.
+
+    item = _loc_ref_item()
+
+    vis_before = _vis_before(item)
+    if vis_before and _tree.bbox(vis_before):
+        return vis_before
+
+    vis_after = _vis_after(item)
+    if vis_after and _tree.bbox(vis_after):
+        return vis_after
+
+    return vis_before or vis_after
+
+
+def _vis_before(item):
+    # _vis_loc_ref_item() helper. Returns the first visible (not red) item,
+    # searching backwards from 'item'.
+
+    while item:
+        if not _tree.tag_has("invisible", item):
+            return item
+
+        prev = _tree.prev(item)
+        item = prev if prev else _tree.parent(item)
+
+    return None
+
+
+def _vis_after(item):
+    # _vis_loc_ref_item() helper. Returns the first visible (not red) item,
+    # searching forwards from 'item'.
+
+    while item:
+        if not _tree.tag_has("invisible", item):
+            return item
+
+        next = _tree.next(item)
+        if next:
+            item = next
+        else:
+            item = _tree.parent(item)
+            if not item:
+                break
+            item = _tree.next(item)
+
+    return None
+
+
+def _on_quit(_=None):
+    # Called when the user wants to exit
+
+    if not _conf_changed:
+        _quit("No changes to save (for '{}')".format(_conf_filename))
+        return
+
+    while True:
+        ync = messagebox.askyesnocancel("Quit", "Save changes?")
+        if ync is None:
+            return
+
+        if not ync:
+            _quit("Configuration ({}) was not saved".format(_conf_filename))
+            return
+
+        if _try_save(_kconf.write_config, _conf_filename, "configuration"):
+            # _try_save() already prints the "Configuration saved to ..."
+            # message
+            _quit()
+            return
+
+
+def _quit(msg=None):
+    # Quits the application
+
+    # Do not call sys.exit() here, in case we're being run from a script
+    _root.destroy()
+    if msg:
+        print(msg)
+
+
+def _try_save(save_fn, filename, description):
+    # Tries to save a configuration file. Pops up an error and returns False on
+    # failure.
+    #
+    # save_fn:
+    #   Function to call with 'filename' to save the file
+    #
+    # description:
+    #   String describing the thing being saved
+
+    try:
+        # save_fn() returns a message to print
+        msg = save_fn(filename)
+        _set_status(msg)
+        print(msg)
+        return True
+    except EnvironmentError as e:
+        messagebox.showerror(
+            "Error saving " + description,
+            "Error saving {} to '{}': {} (errno: {})"
+            .format(description, e.filename, e.strerror,
+                    errno.errorcode[e.errno]))
+        return False
+
+
+def _try_load(filename):
+    # Tries to load a configuration file. Pops up an error and returns False on
+    # failure.
+    #
+    # filename:
+    #   Configuration file to load
+
+    try:
+        msg = _kconf.load_config(filename)
+        _set_status(msg)
+        print(msg)
+        return True
+    except EnvironmentError as e:
+        messagebox.showerror(
+            "Error loading configuration",
+            "Error loading '{}': {} (errno: {})"
+            .format(filename, e.strerror, errno.errorcode[e.errno]))
+        return False
+
+
+def _jump_to_dialog(_=None):
+    # Pops up a dialog for jumping directly to a particular node. Symbol values
+    # can also be changed within the dialog.
+    #
+    # Note: There's nothing preventing this from doing an incremental search
+    # like menuconfig.py does, but currently it's a bit jerky for large Kconfig
+    # trees, at least when inputting the beginning of the search string. We'd
+    # need to somehow only update the tree items that are shown in the Treeview
+    # to fix it.
+
+    global _jump_to_tree
+
+    def search(_=None):
+        _update_jump_to_matches(msglabel, entry.get())
+
+    def jump_to_selected(event=None):
+        # Jumps to the selected node and closes the dialog
+
+        # Ignore double clicks on the image and in the heading area
+        if event and (tree.identify_element(event.x, event.y) == "image" or
+                      _in_heading(event)):
+            return
+
+        sel = tree.selection()
+        if not sel:
+            return
+
+        node = _id_to_node[sel[0]]
+
+        if node not in _shown_menu_nodes(_parent_menu(node)):
+            _show_all_var.set(True)
+            if not _single_menu:
+                # See comment in _do_tree_mode()
+                _update_tree()
+
+        _jump_to(node)
+
+        dialog.destroy()
+
+    def tree_select(_):
+        jumpto_button["state"] = "normal" if tree.selection() else "disabled"
+
+
+    dialog = Toplevel(_root)
+    dialog.geometry("+{}+{}".format(
+        _root.winfo_rootx() + 50, _root.winfo_rooty() + 50))
+    dialog.title("Jump to symbol/choice/menu/comment")
+    dialog.minsize(128, 128)  # See _create_ui()
+    dialog.transient(_root)
+
+    ttk.Label(dialog, text=_JUMP_TO_HELP) \
+        .grid(column=0, row=0, columnspan=2, sticky="w", padx=".1c",
+              pady=".1c")
+
+    entry = ttk.Entry(dialog)
+    entry.grid(column=0, row=1, sticky="ew", padx=".1c", pady=".1c")
+    entry.focus_set()
+
+    entry.bind("<Return>", search)
+    entry.bind("<KP_Enter>", search)
+
+    ttk.Button(dialog, text="Search", command=search) \
+        .grid(column=1, row=1, padx="0 .1c", pady="0 .1c")
+
+    msglabel = ttk.Label(dialog)
+    msglabel.grid(column=0, row=2, sticky="w", pady="0 .1c")
+
+    panedwindow, tree = _create_kconfig_tree_and_desc(dialog)
+    panedwindow.grid(column=0, row=3, columnspan=2, sticky="nsew")
+
+    # Clear tree
+    tree.set_children("")
+
+    _jump_to_tree = tree
+
+    jumpto_button = ttk.Button(dialog, text="Jump to selected item",
+                               state="disabled", command=jump_to_selected)
+    jumpto_button.grid(column=0, row=4, columnspan=2, sticky="ns", pady=".1c")
+
+    dialog.columnconfigure(0, weight=1)
+    # Only the pane with the Kconfig tree and description grows vertically
+    dialog.rowconfigure(3, weight=1)
+
+    # See the menuconfig() function
+    _root.update_idletasks()
+    dialog.geometry(dialog.geometry())
+
+    # The dialog must be visible before we can grab the input
+    dialog.wait_visibility()
+    dialog.grab_set()
+
+    tree.bind("<Double-1>", jump_to_selected)
+    tree.bind("<Return>", jump_to_selected)
+    tree.bind("<KP_Enter>", jump_to_selected)
+    # add=True to avoid overriding the description text update
+    tree.bind("<<TreeviewSelect>>", tree_select, add=True)
+
+    dialog.bind("<Escape>", lambda _: dialog.destroy())
+
+    # Wait for the user to be done with the dialog
+    _root.wait_window(dialog)
+
+    _jump_to_tree = None
+
+    _tree.focus_set()
+
+
+def _update_jump_to_matches(msglabel, search_string):
+    # Searches for nodes matching the search string and updates
+    # _jump_to_matches. Puts a message in 'msglabel' if there are no matches,
+    # or regex errors.
+
+    global _jump_to_matches
+
+    _jump_to_tree.selection_set(())
+
+    try:
+        # We could use re.IGNORECASE here instead of lower(), but this is
+        # faster for regexes like '.*debug$' (though the '.*' is redundant
+        # there). Those probably have bad interactions with re.search(), which
+        # matches anywhere in the string.
+        regex_searches = [re.compile(regex).search
+                          for regex in search_string.lower().split()]
+    except re.error as e:
+        msg = "Bad regular expression"
+        # re.error.msg was added in Python 3.5
+        if hasattr(e, "msg"):
+            msg += ": " + e.msg
+        msglabel["text"] = msg
+        # Clear tree
+        _jump_to_tree.set_children("")
+        return
+
+    _jump_to_matches = []
+    add_match = _jump_to_matches.append
+
+    for node in _sorted_sc_nodes():
+        # Symbol/choice
+        sc = node.item
+
+        for search in regex_searches:
+            # Both the name and the prompt might be missing, since
+            # we're searching both symbols and choices
+
+            # Does the regex match either the symbol name or the
+            # prompt (if any)?
+            if not (sc.name and search(sc.name.lower()) or
+                    node.prompt and search(node.prompt[0].lower())):
+
+                # Give up on the first regex that doesn't match, to
+                # speed things up a bit when multiple regexes are
+                # entered
+                break
+
+        else:
+            add_match(node)
+
+    # Search menus and comments
+
+    for node in _sorted_menu_comment_nodes():
+        for search in regex_searches:
+            if not search(node.prompt[0].lower()):
+                break
+        else:
+            add_match(node)
+
+    msglabel["text"] = "" if _jump_to_matches else "No matches"
+
+    _update_jump_to_display()
+
+    if _jump_to_matches:
+        item = id(_jump_to_matches[0])
+        _jump_to_tree.selection_set(item)
+        _jump_to_tree.focus(item)
+
+
+def _update_jump_to_display():
+    # Updates the images and text for the items in _jump_to_matches, and sets
+    # them as the items of _jump_to_tree
+
+    # Micro-optimize a bit
+    item = _jump_to_tree.item
+    id_ = id
+    node_str = _node_str
+    img_tag = _img_tag
+    visible = _visible
+    for node in _jump_to_matches:
+        item(id_(node),
+             text=node_str(node),
+             tags=img_tag(node) if visible(node) else
+                 img_tag(node) + " invisible")
+
+    _jump_to_tree.set_children("", *map(id, _jump_to_matches))
+
+
+def _jump_to(node):
+    # Jumps directly to 'node' and selects it
+
+    if _single_menu:
+        _enter_menu(_parent_menu(node))
+    else:
+        _load_parents(node)
+
+    _select(_tree, id(node))
+
+
+# Obscure Python: We never pass a value for cached_nodes, and it keeps pointing
+# to the same list. This avoids a global.
+def _sorted_sc_nodes(cached_nodes=[]):
+    # Returns a sorted list of symbol and choice nodes to search. The symbol
+    # nodes appear first, sorted by name, and then the choice nodes, sorted by
+    # prompt and (secondarily) name.
+
+    if not cached_nodes:
+        # Add symbol nodes
+        for sym in sorted(_kconf.unique_defined_syms,
+                          key=lambda sym: sym.name):
+            # += is in-place for lists
+            cached_nodes += sym.nodes
+
+        # Add choice nodes
+
+        choices = sorted(_kconf.unique_choices,
+                         key=lambda choice: choice.name or "")
+
+        cached_nodes += sorted(
+            [node for choice in choices for node in choice.nodes],
+            key=lambda node: node.prompt[0] if node.prompt else "")
+
+    return cached_nodes
+
+
+def _sorted_menu_comment_nodes(cached_nodes=[]):
+    # Returns a list of menu and comment nodes to search, sorted by prompt,
+    # with the menus first
+
+    if not cached_nodes:
+        def prompt_text(mc):
+            return mc.prompt[0]
+
+        cached_nodes += sorted(_kconf.menus, key=prompt_text)
+        cached_nodes += sorted(_kconf.comments, key=prompt_text)
+
+    return cached_nodes
+
+
+def _load_parents(node):
+    # Menus are lazily populated as they're opened in full-tree mode, but
+    # jumping to an item needs its parent menus to be populated. This function
+    # populates 'node's parents.
+
+    # Get all parents leading up to 'node', sorted with the root first
+    parents = []
+    cur = node.parent
+    while cur is not _kconf.top_node:
+        parents.append(cur)
+        cur = cur.parent
+    parents.reverse()
+
+    for i, parent in enumerate(parents):
+        if not _tree.item(id(parent), "open"):
+            # Found a closed menu. Populate it and all the remaining menus
+            # leading up to 'node'.
+            for parent in parents[i:]:
+                # We only need to populate "real" menus/choices. Implicit menus
+                # are populated when their parents menus are entered.
+                if not isinstance(parent.item, Symbol):
+                    _build_full_tree(parent)
+            return
+
+
+def _parent_menu(node):
+    # Returns the menu node of the menu that contains 'node'. In addition to
+    # proper 'menu's, this might also be a 'menuconfig' symbol or a 'choice'.
+    # "Menu" here means a menu in the interface.
+
+    menu = node.parent
+    while not menu.is_menuconfig:
+        menu = menu.parent
+    return menu
+
+
+def _trace_write(var, fn):
+    # Makes fn() be called whenever the Tkinter Variable 'var' changes value
+
+    # trace_variable() is deprecated according to the docstring,
+    # which recommends trace_add()
+    if hasattr(var, "trace_add"):
+        var.trace_add("write", fn)
+    else:
+        var.trace_variable("w", fn)
+
+
+def _info_str(node):
+    # Returns information about the menu node 'node' as a string.
+    #
+    # The helper functions are responsible for adding newlines. This allows
+    # them to return "" if they don't want to add any output.
+
+    if isinstance(node.item, Symbol):
+        sym = node.item
+
+        return (
+            _name_info(sym) +
+            _help_info(sym) +
+            _direct_dep_info(sym) +
+            _defaults_info(sym) +
+            _select_imply_info(sym) +
+            _kconfig_def_info(sym)
+        )
+
+    if isinstance(node.item, Choice):
+        choice = node.item
+
+        return (
+            _name_info(choice) +
+            _help_info(choice) +
+            'Mode: {}\n\n'.format(choice.str_value) +
+            _choice_syms_info(choice) +
+            _direct_dep_info(choice) +
+            _defaults_info(choice) +
+            _kconfig_def_info(choice)
+        )
+
+    # node.item in (MENU, COMMENT)
+    return _kconfig_def_info(node)
+
+
+def _name_info(sc):
+    # Returns a string with the name of the symbol/choice. Choices are shown as
+    # <choice (name if any)>.
+
+    return (sc.name if sc.name else standard_sc_expr_str(sc)) + "\n\n"
+
+
+def _value_info(sym):
+    # Returns a string showing 'sym's value
+
+    # Only put quotes around the value for string symbols
+    return "Value: {}\n".format(
+        '"{}"'.format(sym.str_value)
+        if sym.orig_type == STRING
+        else sym.str_value)
+
+
+def _choice_syms_info(choice):
+    # Returns a string listing the choice symbols in 'choice'. Adds
+    # "(selected)" next to the selected one.
+
+    s = "Choice symbols:\n"
+
+    for sym in choice.syms:
+        s += "  - " + sym.name
+        if sym is choice.selection:
+            s += " (selected)"
+        s += "\n"
+
+    return s + "\n"
+
+
+def _help_info(sc):
+    # Returns a string with the help text(s) of 'sc' (Symbol or Choice).
+    # Symbols and choices defined in multiple locations can have multiple help
+    # texts.
+
+    s = ""
+
+    for node in sc.nodes:
+        if node.help is not None:
+            s += node.help + "\n\n"
+
+    return s
+
+
+def _direct_dep_info(sc):
+    # Returns a string describing the direct dependencies of 'sc' (Symbol or
+    # Choice). The direct dependencies are the OR of the dependencies from each
+    # definition location. The dependencies at each definition location come
+    # from 'depends on' and dependencies inherited from parent items.
+
+    return "" if sc.direct_dep is _kconf.y else \
+        'Direct dependencies (={}):\n{}\n' \
+        .format(TRI_TO_STR[expr_value(sc.direct_dep)],
+                _split_expr_info(sc.direct_dep, 2))
+
+
+def _defaults_info(sc):
+    # Returns a string describing the defaults of 'sc' (Symbol or Choice)
+
+    if not sc.defaults:
+        return ""
+
+    s = "Default"
+    if len(sc.defaults) > 1:
+        s += "s"
+    s += ":\n"
+
+    for val, cond in sc.orig_defaults:
+        s += "  - "
+        if isinstance(sc, Symbol):
+            s += _expr_str(val)
+
+            # Skip the tristate value hint if the expression is just a single
+            # symbol. _expr_str() already shows its value as a string.
+            #
+            # This also avoids showing the tristate value for string/int/hex
+            # defaults, which wouldn't make any sense.
+            if isinstance(val, tuple):
+                s += '  (={})'.format(TRI_TO_STR[expr_value(val)])
+        else:
+            # Don't print the value next to the symbol name for choice
+            # defaults, as it looks a bit confusing
+            s += val.name
+        s += "\n"
+
+        if cond is not _kconf.y:
+            s += "    Condition (={}):\n{}" \
+                 .format(TRI_TO_STR[expr_value(cond)],
+                         _split_expr_info(cond, 4))
+
+    return s + "\n"
+
+
+def _split_expr_info(expr, indent):
+    # Returns a string with 'expr' split into its top-level && or || operands,
+    # with one operand per line, together with the operand's value. This is
+    # usually enough to get something readable for long expressions. A fancier
+    # recursive thingy would be possible too.
+    #
+    # indent:
+    #   Number of leading spaces to add before the split expression.
+
+    if len(split_expr(expr, AND)) > 1:
+        split_op = AND
+        op_str = "&&"
+    else:
+        split_op = OR
+        op_str = "||"
+
+    s = ""
+    for i, term in enumerate(split_expr(expr, split_op)):
+        s += "{}{} {}".format(indent*" ",
+                              "  " if i == 0 else op_str,
+                              _expr_str(term))
+
+        # Don't bother showing the value hint if the expression is just a
+        # single symbol. _expr_str() already shows its value.
+        if isinstance(term, tuple):
+            s += "  (={})".format(TRI_TO_STR[expr_value(term)])
+
+        s += "\n"
+
+    return s
+
+
+def _select_imply_info(sym):
+    # Returns a string with information about which symbols 'select' or 'imply'
+    # 'sym'. The selecting/implying symbols are grouped according to which
+    # value they select/imply 'sym' to (n/m/y).
+
+    def sis(expr, val, title):
+        # sis = selects/implies
+        sis = [si for si in split_expr(expr, OR) if expr_value(si) == val]
+        if not sis:
+            return ""
+
+        res = title
+        for si in sis:
+            res += "  - {}\n".format(split_expr(si, AND)[0].name)
+        return res + "\n"
+
+    s = ""
+
+    if sym.rev_dep is not _kconf.n:
+        s += sis(sym.rev_dep, 2,
+                 "Symbols currently y-selecting this symbol:\n")
+        s += sis(sym.rev_dep, 1,
+                 "Symbols currently m-selecting this symbol:\n")
+        s += sis(sym.rev_dep, 0,
+                 "Symbols currently n-selecting this symbol (no effect):\n")
+
+    if sym.weak_rev_dep is not _kconf.n:
+        s += sis(sym.weak_rev_dep, 2,
+                 "Symbols currently y-implying this symbol:\n")
+        s += sis(sym.weak_rev_dep, 1,
+                 "Symbols currently m-implying this symbol:\n")
+        s += sis(sym.weak_rev_dep, 0,
+                 "Symbols currently n-implying this symbol (no effect):\n")
+
+    return s
+
+
+def _kconfig_def_info(item):
+    # Returns a string with the definition of 'item' in Kconfig syntax,
+    # together with the definition location(s) and their include and menu paths
+
+    nodes = [item] if isinstance(item, MenuNode) else item.nodes
+
+    s = "Kconfig definition{}, with parent deps. propagated to 'depends on'\n" \
+        .format("s" if len(nodes) > 1 else "")
+    s += (len(s) - 1)*"="
+
+    for node in nodes:
+        s += "\n\n" \
+             "At {}:{}\n" \
+             "{}" \
+             "Menu path: {}\n\n" \
+             "{}" \
+             .format(node.filename, node.linenr,
+                     _include_path_info(node),
+                     _menu_path_info(node),
+                     node.custom_str(_name_and_val_str))
+
+    return s
+
+
+def _include_path_info(node):
+    if not node.include_path:
+        # In the top-level Kconfig file
+        return ""
+
+    return "Included via {}\n".format(
+        " -> ".join("{}:{}".format(filename, linenr)
+                    for filename, linenr in node.include_path))
+
+
+def _menu_path_info(node):
+    # Returns a string describing the menu path leading up to 'node'
+
+    path = ""
+
+    while node.parent is not _kconf.top_node:
+        node = node.parent
+
+        # Promptless choices might appear among the parents. Use
+        # standard_sc_expr_str() for them, so that they show up as
+        # '<choice (name if any)>'.
+        path = " -> " + (node.prompt[0] if node.prompt else
+                         standard_sc_expr_str(node.item)) + path
+
+    return "(Top)" + path
+
+
+def _name_and_val_str(sc):
+    # Custom symbol/choice printer that shows symbol values after symbols
+
+    # Show the values of non-constant (non-quoted) symbols that don't look like
+    # numbers. Things like 123 are actually symbol references, and only work as
+    # expected due to undefined symbols getting their name as their value.
+    # Showing the symbol value for those isn't helpful though.
+    if isinstance(sc, Symbol) and not sc.is_constant and not _is_num(sc.name):
+        if not sc.nodes:
+            # Undefined symbol reference
+            return "{}(undefined/n)".format(sc.name)
+
+        return '{}(={})'.format(sc.name, sc.str_value)
+
+    # For other items, use the standard format
+    return standard_sc_expr_str(sc)
+
+
+def _expr_str(expr):
+    # Custom expression printer that shows symbol values
+    return expr_str(expr, _name_and_val_str)
+
+
+def _is_num(name):
+    # Heuristic to see if a symbol name looks like a number, for nicer output
+    # when printing expressions. Things like 16 are actually symbol names, only
+    # they get their name as their value when the symbol is undefined.
+
+    try:
+        int(name)
+    except ValueError:
+        if not name.startswith(("0x", "0X")):
+            return False
+
+        try:
+            int(name, 16)
+        except ValueError:
+            return False
+
+    return True
+
+
+if __name__ == "__main__":
+    _main()
diff --git a/scripts/Kconfiglib/kconfiglib.py b/scripts/Kconfiglib/kconfiglib.py
new file mode 100644
index 0000000..c67895c
--- /dev/null
+++ b/scripts/Kconfiglib/kconfiglib.py
@@ -0,0 +1,7160 @@
+# Copyright (c) 2011-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Overview
+========
+
+Kconfiglib is a Python 2/3 library for scripting and extracting information
+from Kconfig (https://www.kernel.org/doc/Documentation/kbuild/kconfig-language.txt)
+configuration systems.
+
+See the homepage at https://github.com/ulfalizer/Kconfiglib for a longer
+overview.
+
+Since Kconfiglib 12.0.0, the library version is available in
+kconfiglib.VERSION, which is a (<major>, <minor>, <patch>) tuple, e.g.
+(12, 0, 0).
+
+
+Using Kconfiglib on the Linux kernel with the Makefile targets
+==============================================================
+
+For the Linux kernel, a handy interface is provided by the
+scripts/kconfig/Makefile patch, which can be applied with either 'git am' or
+the 'patch' utility:
+
+  $ wget -qO- https://raw.githubusercontent.com/ulfalizer/Kconfiglib/master/makefile.patch | git am
+  $ wget -qO- https://raw.githubusercontent.com/ulfalizer/Kconfiglib/master/makefile.patch | patch -p1
+
+Warning: Not passing -p1 to patch will cause the wrong file to be patched.
+
+Please tell me if the patch does not apply. It should be trivial to apply
+manually, as it's just a block of text that needs to be inserted near the other
+*conf: targets in scripts/kconfig/Makefile.
+
+Look further down for a motivation for the Makefile patch and for instructions
+on how you can use Kconfiglib without it.
+
+If you do not wish to install Kconfiglib via pip, the Makefile patch is set up
+so that you can also just clone Kconfiglib into the kernel root:
+
+  $ git clone git://github.com/ulfalizer/Kconfiglib.git
+  $ git am Kconfiglib/makefile.patch  (or 'patch -p1 < Kconfiglib/makefile.patch')
+
+Warning: The directory name Kconfiglib/ is significant in this case, because
+it's added to PYTHONPATH by the new targets in makefile.patch.
+
+The targets added by the Makefile patch are described in the following
+sections.
+
+
+make kmenuconfig
+----------------
+
+This target runs the curses menuconfig interface with Python 3. As of
+Kconfiglib 12.2.0, both Python 2 and Python 3 are supported (previously, only
+Python 3 was supported, so this was a backport).
+
+
+make guiconfig
+--------------
+
+This target runs the Tkinter menuconfig interface. Both Python 2 and Python 3
+are supported. To change the Python interpreter used, pass
+PYTHONCMD=<executable> to 'make'. The default is 'python'.
+
+
+make [ARCH=<arch>] iscriptconfig
+--------------------------------
+
+This target gives an interactive Python prompt where a Kconfig instance has
+been preloaded and is available in 'kconf'. To change the Python interpreter
+used, pass PYTHONCMD=<executable> to 'make'. The default is 'python'.
+
+To get a feel for the API, try evaluating and printing the symbols in
+kconf.defined_syms, and explore the MenuNode menu tree starting at
+kconf.top_node by following 'next' and 'list' pointers.
+
+The item contained in a menu node is found in MenuNode.item (note that this can
+be one of the constants kconfiglib.MENU and kconfiglib.COMMENT), and all
+symbols and choices have a 'nodes' attribute containing their menu nodes
+(usually only one). Printing a menu node will print its item, in Kconfig
+format.
+
+If you want to look up a symbol by name, use the kconf.syms dictionary.
+
+
+make scriptconfig SCRIPT=<script> [SCRIPT_ARG=<arg>]
+----------------------------------------------------
+
+This target runs the Python script given by the SCRIPT parameter on the
+configuration. sys.argv[1] holds the name of the top-level Kconfig file
+(currently always "Kconfig" in practice), and sys.argv[2] holds the SCRIPT_ARG
+argument, if given.
+
+See the examples/ subdirectory for example scripts.
+
+
+make dumpvarsconfig
+-------------------
+
+This target prints a list of all environment variables referenced from the
+Kconfig files, together with their values. See the
+Kconfiglib/examples/dumpvars.py script.
+
+Only environment variables that are referenced via the Kconfig preprocessor
+$(FOO) syntax are included. The preprocessor was added in Linux 4.18.
+
+
+Using Kconfiglib without the Makefile targets
+=============================================
+
+The make targets are only needed to pick up environment variables exported from
+the Kbuild makefiles and referenced inside Kconfig files, via e.g.
+'source "arch/$(SRCARCH)/Kconfig" and commands run via '$(shell,...)'.
+
+These variables are referenced as of writing (Linux 4.18), together with sample
+values:
+
+  srctree          (.)
+  ARCH             (x86)
+  SRCARCH          (x86)
+  KERNELVERSION    (4.18.0)
+  CC               (gcc)
+  HOSTCC           (gcc)
+  HOSTCXX          (g++)
+  CC_VERSION_TEXT  (gcc (Ubuntu 7.3.0-16ubuntu3) 7.3.0)
+
+Older kernels only reference ARCH, SRCARCH, and KERNELVERSION.
+
+If your kernel is recent enough (4.18+), you can get a list of referenced
+environment variables via 'make dumpvarsconfig' (see above). Note that this
+command is added by the Makefile patch.
+
+To run Kconfiglib without the Makefile patch, set the environment variables
+manually:
+
+  $ srctree=. ARCH=x86 SRCARCH=x86 KERNELVERSION=`make kernelversion` ... python(3)
+  >>> import kconfiglib
+  >>> kconf = kconfiglib.Kconfig()  # filename defaults to "Kconfig"
+
+Search the top-level Makefile for "Additional ARCH settings" to see other
+possibilities for ARCH and SRCARCH.
+
+
+Intro to symbol values
+======================
+
+Kconfiglib has the same assignment semantics as the C implementation.
+
+Any symbol can be assigned a value by the user (via Kconfig.load_config() or
+Symbol.set_value()), but this user value is only respected if the symbol is
+visible, which corresponds to it (currently) being visible in the menuconfig
+interface.
+
+For symbols with prompts, the visibility of the symbol is determined by the
+condition on the prompt. Symbols without prompts are never visible, so setting
+a user value on them is pointless. A warning will be printed by default if
+Symbol.set_value() is called on a promptless symbol. Assignments to promptless
+symbols are normal within a .config file, so no similar warning will be printed
+by load_config().
+
+Dependencies from parents and 'if'/'depends on' are propagated to properties,
+including prompts, so these two configurations are logically equivalent:
+
+(1)
+
+  menu "menu"
+      depends on A
+
+  if B
+
+  config FOO
+      tristate "foo" if D
+      default y
+      depends on C
+
+  endif
+
+  endmenu
+
+(2)
+
+  menu "menu"
+      depends on A
+
+  config FOO
+      tristate "foo" if A && B && C && D
+      default y if A && B && C
+
+  endmenu
+
+In this example, A && B && C && D (the prompt condition) needs to be non-n for
+FOO to be visible (assignable). If its value is m, the symbol can only be
+assigned the value m: The visibility sets an upper bound on the value that can
+be assigned by the user, and any higher user value will be truncated down.
+
+'default' properties are independent of the visibility, though a 'default' will
+often get the same condition as the prompt due to dependency propagation.
+'default' properties are used if the symbol is not visible or has no user
+value.
+
+Symbols with no user value (or that have a user value but are not visible) and
+no (active) 'default' default to n for bool/tristate symbols, and to the empty
+string for other symbol types.
+
+'select' works similarly to symbol visibility, but sets a lower bound on the
+value of the symbol. The lower bound is determined by the value of the
+select*ing* symbol. 'select' does not respect visibility, so non-visible
+symbols can be forced to a particular (minimum) value by a select as well.
+
+For non-bool/tristate symbols, it only matters whether the visibility is n or
+non-n: m visibility acts the same as y visibility.
+
+Conditions on 'default' and 'select' work in mostly intuitive ways. If the
+condition is n, the 'default' or 'select' is disabled. If it is m, the
+'default' or 'select' value (the value of the selecting symbol) is truncated
+down to m.
+
+When writing a configuration with Kconfig.write_config(), only symbols that are
+visible, have an (active) default, or are selected will get written out (note
+that this includes all symbols that would accept user values). Kconfiglib
+matches the .config format produced by the C implementations down to the
+character. This eases testing.
+
+For a visible bool/tristate symbol FOO with value n, this line is written to
+.config:
+
+    # CONFIG_FOO is not set
+
+The point is to remember the user n selection (which might differ from the
+default value the symbol would get), while at the same sticking to the rule
+that undefined corresponds to n (.config uses Makefile format, making the line
+above a comment). When the .config file is read back in, this line will be
+treated the same as the following assignment:
+
+    CONFIG_FOO=n
+
+In Kconfiglib, the set of (currently) assignable values for a bool/tristate
+symbol appear in Symbol.assignable. For other symbol types, just check if
+sym.visibility is non-0 (non-n) to see whether the user value will have an
+effect.
+
+
+Intro to the menu tree
+======================
+
+The menu structure, as seen in e.g. menuconfig, is represented by a tree of
+MenuNode objects. The top node of the configuration corresponds to an implicit
+top-level menu, the title of which is shown at the top in the standard
+menuconfig interface. (The title is also available in Kconfig.mainmenu_text in
+Kconfiglib.)
+
+The top node is found in Kconfig.top_node. From there, you can visit child menu
+nodes by following the 'list' pointer, and any following menu nodes by
+following the 'next' pointer. Usually, a non-None 'list' pointer indicates a
+menu or Choice, but menu nodes for symbols can sometimes have a non-None 'list'
+pointer too due to submenus created implicitly from dependencies.
+
+MenuNode.item is either a Symbol or a Choice object, or one of the constants
+MENU and COMMENT. The prompt of the menu node can be found in MenuNode.prompt,
+which also holds the title for menus and comments. For Symbol and Choice,
+MenuNode.help holds the help text (if any, otherwise None).
+
+Most symbols will only have a single menu node. A symbol defined in multiple
+locations will have one menu node for each location. The list of menu nodes for
+a Symbol or Choice can be found in the Symbol/Choice.nodes attribute.
+
+Note that prompts and help texts for symbols and choices are stored in their
+menu node(s) rather than in the Symbol or Choice objects themselves. This makes
+it possible to define a symbol in multiple locations with a different prompt or
+help text in each location. To get the help text or prompt for a symbol with a
+single menu node, do sym.nodes[0].help and sym.nodes[0].prompt, respectively.
+The prompt is a (text, condition) tuple, where condition determines the
+visibility (see 'Intro to expressions' below).
+
+This organization mirrors the C implementation. MenuNode is called
+'struct menu' there, but I thought "menu" was a confusing name.
+
+It is possible to give a Choice a name and define it in multiple locations,
+hence why Choice.nodes is also a list.
+
+As a convenience, the properties added at a particular definition location are
+available on the MenuNode itself, in e.g. MenuNode.defaults. This is helpful
+when generating documentation, so that symbols/choices defined in multiple
+locations can be shown with the correct properties at each location.
+
+
+Intro to expressions
+====================
+
+Expressions can be evaluated with the expr_value() function and printed with
+the expr_str() function (these are used internally as well). Evaluating an
+expression always yields a tristate value, where n, m, and y are represented as
+0, 1, and 2, respectively.
+
+The following table should help you figure out how expressions are represented.
+A, B, C, ... are symbols (Symbol instances), NOT is the kconfiglib.NOT
+constant, etc.
+
+Expression            Representation
+----------            --------------
+A                     A
+"A"                   A (constant symbol)
+!A                    (NOT, A)
+A && B                (AND, A, B)
+A && B && C           (AND, A, (AND, B, C))
+A || B                (OR, A, B)
+A || (B && C && D)    (OR, A, (AND, B, (AND, C, D)))
+A = B                 (EQUAL, A, B)
+A != "foo"            (UNEQUAL, A, foo (constant symbol))
+A && B = C && D       (AND, A, (AND, (EQUAL, B, C), D))
+n                     Kconfig.n (constant symbol)
+m                     Kconfig.m (constant symbol)
+y                     Kconfig.y (constant symbol)
+"y"                   Kconfig.y (constant symbol)
+
+Strings like "foo" in 'default "foo"' or 'depends on SYM = "foo"' are
+represented as constant symbols, so the only values that appear in expressions
+are symbols***. This mirrors the C implementation.
+
+***For choice symbols, the parent Choice will appear in expressions as well,
+but it's usually invisible as the value interfaces of Symbol and Choice are
+identical. This mirrors the C implementation and makes different choice modes
+"just work".
+
+Manual evaluation examples:
+
+  - The value of A && B is min(A.tri_value, B.tri_value)
+
+  - The value of A || B is max(A.tri_value, B.tri_value)
+
+  - The value of !A is 2 - A.tri_value
+
+  - The value of A = B is 2 (y) if A.str_value == B.str_value, and 0 (n)
+    otherwise. Note that str_value is used here instead of tri_value.
+
+    For constant (as well as undefined) symbols, str_value matches the name of
+    the symbol. This mirrors the C implementation and explains why
+    'depends on SYM = "foo"' above works as expected.
+
+n/m/y are automatically converted to the corresponding constant symbols
+"n"/"m"/"y" (Kconfig.n/m/y) during parsing.
+
+Kconfig.const_syms is a dictionary like Kconfig.syms but for constant symbols.
+
+If a condition is missing (e.g., <cond> when the 'if <cond>' is removed from
+'default A if <cond>'), it is actually Kconfig.y. The standard __str__()
+functions just avoid printing 'if y' conditions to give cleaner output.
+
+
+Kconfig extensions
+==================
+
+Kconfiglib includes a couple of Kconfig extensions:
+
+'source' with relative path
+---------------------------
+
+The 'rsource' statement sources Kconfig files with a path relative to directory
+of the Kconfig file containing the 'rsource' statement, instead of relative to
+the project root.
+
+Consider following directory tree:
+
+  Project
+  +--Kconfig
+  |
+  +--src
+     +--Kconfig
+     |
+     +--SubSystem1
+        +--Kconfig
+        |
+        +--ModuleA
+           +--Kconfig
+
+In this example, assume that src/SubSystem1/Kconfig wants to source
+src/SubSystem1/ModuleA/Kconfig.
+
+With 'source', this statement would be used:
+
+  source "src/SubSystem1/ModuleA/Kconfig"
+
+With 'rsource', this turns into
+
+  rsource "ModuleA/Kconfig"
+
+If an absolute path is given to 'rsource', it acts the same as 'source'.
+
+'rsource' can be used to create "position-independent" Kconfig trees that can
+be moved around freely.
+
+
+Globbing 'source'
+-----------------
+
+'source' and 'rsource' accept glob patterns, sourcing all matching Kconfig
+files. They require at least one matching file, raising a KconfigError
+otherwise.
+
+For example, the following statement might source sub1/foofoofoo and
+sub2/foobarfoo:
+
+  source "sub[12]/foo*foo"
+
+The glob patterns accepted are the same as for the standard glob.glob()
+function.
+
+Two additional statements are provided for cases where it's acceptable for a
+pattern to match no files: 'osource' and 'orsource' (the o is for "optional").
+
+For example, the following statements will be no-ops if neither "foo" nor any
+files matching "bar*" exist:
+
+  osource "foo"
+  osource "bar*"
+
+'orsource' does a relative optional source.
+
+'source' and 'osource' are analogous to 'include' and '-include' in Make.
+
+
+Generalized def_* keywords
+--------------------------
+
+def_int, def_hex, and def_string are available in addition to def_bool and
+def_tristate, allowing int, hex, and string symbols to be given a type and a
+default at the same time.
+
+
+Extra optional warnings
+-----------------------
+
+Some optional warnings can be controlled via environment variables:
+
+  - KCONFIG_WARN_UNDEF: If set to 'y', warnings will be generated for all
+    references to undefined symbols within Kconfig files. The only gotcha is
+    that all hex literals must be prefixed with "0x" or "0X", to make it
+    possible to distinguish them from symbol references.
+
+    Some projects (e.g. the Linux kernel) use multiple Kconfig trees with many
+    shared Kconfig files, leading to some safe undefined symbol references.
+    KCONFIG_WARN_UNDEF is useful in projects that only have a single Kconfig
+    tree though.
+
+    KCONFIG_STRICT is an older alias for this environment variable, supported
+    for backwards compatibility.
+
+  - KCONFIG_WARN_UNDEF_ASSIGN: If set to 'y', warnings will be generated for
+    all assignments to undefined symbols within .config files. By default, no
+    such warnings are generated.
+
+    This warning can also be enabled/disabled via the Kconfig.warn_assign_undef
+    variable.
+
+
+Preprocessor user functions defined in Python
+---------------------------------------------
+
+Preprocessor functions can be defined in Python, which makes it simple to
+integrate information from existing Python tools into Kconfig (e.g. to have
+Kconfig symbols depend on hardware information stored in some other format).
+
+Putting a Python module named kconfigfunctions(.py) anywhere in sys.path will
+cause it to be imported by Kconfiglib (in Kconfig.__init__()). Note that
+sys.path can be customized via PYTHONPATH, and includes the directory of the
+module being run by default, as well as installation directories.
+
+If the KCONFIG_FUNCTIONS environment variable is set, it gives a different
+module name to use instead of 'kconfigfunctions'.
+
+The imported module is expected to define a global dictionary named 'functions'
+that maps function names to Python functions, as follows:
+
+  def my_fn(kconf, name, arg_1, arg_2, ...):
+      # kconf:
+      #   Kconfig instance
+      #
+      # name:
+      #   Name of the user-defined function ("my-fn"). Think argv[0].
+      #
+      # arg_1, arg_2, ...:
+      #   Arguments passed to the function from Kconfig (strings)
+      #
+      # Returns a string to be substituted as the result of calling the
+      # function
+      ...
+
+  def my_other_fn(kconf, name, arg_1, arg_2, ...):
+      ...
+
+  functions = {
+      "my-fn":       (my_fn,       <min.args>, <max.args>/None),
+      "my-other-fn": (my_other_fn, <min.args>, <max.args>/None),
+      ...
+  }
+
+  ...
+
+<min.args> and <max.args> are the minimum and maximum number of arguments
+expected by the function (excluding the implicit 'name' argument). If
+<max.args> is None, there is no upper limit to the number of arguments. Passing
+an invalid number of arguments will generate a KconfigError exception.
+
+Functions can access the current parsing location as kconf.filename/linenr.
+Accessing other fields of the Kconfig object is not safe. See the warning
+below.
+
+Keep in mind that for a variable defined like 'foo = $(fn)', 'fn' will be
+called only when 'foo' is expanded. If 'fn' uses the parsing location and the
+intent is to use the location of the assignment, you want 'foo := $(fn)'
+instead, which calls the function immediately.
+
+Once defined, user functions can be called from Kconfig in the same way as
+other preprocessor functions:
+
+    config FOO
+        ...
+        depends on $(my-fn,arg1,arg2)
+
+If my_fn() returns "n", this will result in
+
+    config FOO
+        ...
+        depends on n
+
+Warning
+*******
+
+User-defined preprocessor functions are called as they're encountered at parse
+time, before all Kconfig files have been processed, and before the menu tree
+has been finalized. There are no guarantees that accessing Kconfig symbols or
+the menu tree via the 'kconf' parameter will work, and it could potentially
+lead to a crash.
+
+Preferably, user-defined functions should be stateless.
+
+
+Feedback
+========
+
+Send bug reports, suggestions, and questions to ulfalizer a.t Google's email
+service, or open a ticket on the GitHub page.
+"""
+import errno
+import importlib
+import os
+import re
+import sys
+
+# Get rid of some attribute lookups. These are obvious in context.
+from glob import iglob
+from os.path import dirname, exists, expandvars, islink, join, realpath
+
+
+VERSION = (14, 1, 0)
+
+
+# File layout:
+#
+# Public classes
+# Public functions
+# Internal functions
+# Global constants
+
+# Line length: 79 columns
+
+
+#
+# Public classes
+#
+
+
+class Kconfig(object):
+    """
+    Represents a Kconfig configuration, e.g. for x86 or ARM. This is the set of
+    symbols, choices, and menu nodes appearing in the configuration. Creating
+    any number of Kconfig objects (including for different architectures) is
+    safe. Kconfiglib doesn't keep any global state.
+
+    The following attributes are available. They should be treated as
+    read-only, and some are implemented through @property magic.
+
+    syms:
+      A dictionary with all symbols in the configuration, indexed by name. Also
+      includes all symbols that are referenced in expressions but never
+      defined, except for constant (quoted) symbols.
+
+      Undefined symbols can be recognized by Symbol.nodes being empty -- see
+      the 'Intro to the menu tree' section in the module docstring.
+
+    const_syms:
+      A dictionary like 'syms' for constant (quoted) symbols
+
+    named_choices:
+      A dictionary like 'syms' for named choices (choice FOO)
+
+    defined_syms:
+      A list with all defined symbols, in the same order as they appear in the
+      Kconfig files. Symbols defined in multiple locations appear multiple
+      times.
+
+      Note: You probably want to use 'unique_defined_syms' instead. This
+      attribute is mostly maintained for backwards compatibility.
+
+    unique_defined_syms:
+      A list like 'defined_syms', but with duplicates removed. Just the first
+      instance is kept for symbols defined in multiple locations. Kconfig order
+      is preserved otherwise.
+
+      Using this attribute instead of 'defined_syms' can save work, and
+      automatically gives reasonable behavior when writing configuration output
+      (symbols defined in multiple locations only generate output once, while
+      still preserving Kconfig order for readability).
+
+    choices:
+      A list with all choices, in the same order as they appear in the Kconfig
+      files.
+
+      Note: You probably want to use 'unique_choices' instead. This attribute
+      is mostly maintained for backwards compatibility.
+
+    unique_choices:
+      Analogous to 'unique_defined_syms', for choices. Named choices can have
+      multiple definition locations.
+
+    menus:
+      A list with all menus, in the same order as they appear in the Kconfig
+      files
+
+    comments:
+      A list with all comments, in the same order as they appear in the Kconfig
+      files
+
+    kconfig_filenames:
+      A list with the filenames of all Kconfig files included in the
+      configuration, relative to $srctree (or relative to the current directory
+      if $srctree isn't set), except absolute paths (e.g.
+      'source "/foo/Kconfig"') are kept as-is.
+
+      The files are listed in the order they are source'd, starting with the
+      top-level Kconfig file. If a file is source'd multiple times, it will
+      appear multiple times. Use set() to get unique filenames.
+
+      Note that Kconfig.sync_deps() already indirectly catches any file
+      modifications that change configuration output.
+
+    env_vars:
+      A set() with the names of all environment variables referenced in the
+      Kconfig files.
+
+      Only environment variables referenced with the preprocessor $(FOO) syntax
+      will be registered. The older $FOO syntax is only supported for backwards
+      compatibility.
+
+      Also note that $(FOO) won't be registered unless the environment variable
+      $FOO is actually set. If it isn't, $(FOO) is an expansion of an unset
+      preprocessor variable (which gives the empty string).
+
+      Another gotcha is that environment variables referenced in the values of
+      recursively expanded preprocessor variables (those defined with =) will
+      only be registered if the variable is actually used (expanded) somewhere.
+
+      The note from the 'kconfig_filenames' documentation applies here too.
+
+    n/m/y:
+      The predefined constant symbols n/m/y. Also available in const_syms.
+
+    modules:
+      The Symbol instance for the modules symbol. Currently hardcoded to
+      MODULES, which is backwards compatible. Kconfiglib will warn if
+      'option modules' is set on some other symbol. Tell me if you need proper
+      'option modules' support.
+
+      'modules' is never None. If the MODULES symbol is not explicitly defined,
+      its tri_value will be 0 (n), as expected.
+
+      A simple way to enable modules is to do 'kconf.modules.set_value(2)'
+      (provided the MODULES symbol is defined and visible). Modules are
+      disabled by default in the kernel Kconfig files as of writing, though
+      nearly all defconfig files enable them (with 'CONFIG_MODULES=y').
+
+    defconfig_list:
+      The Symbol instance for the 'option defconfig_list' symbol, or None if no
+      defconfig_list symbol exists. The defconfig filename derived from this
+      symbol can be found in Kconfig.defconfig_filename.
+
+    defconfig_filename:
+      The filename given by the defconfig_list symbol. This is taken from the
+      first 'default' with a satisfied condition where the specified file
+      exists (can be opened for reading). If a defconfig file foo/defconfig is
+      not found and $srctree was set when the Kconfig was created,
+      $srctree/foo/defconfig is looked up as well.
+
+      'defconfig_filename' is None if either no defconfig_list symbol exists,
+      or if the defconfig_list symbol has no 'default' with a satisfied
+      condition that specifies a file that exists.
+
+      Gotcha: scripts/kconfig/Makefile might pass --defconfig=<defconfig> to
+      scripts/kconfig/conf when running e.g. 'make defconfig'. This option
+      overrides the defconfig_list symbol, meaning defconfig_filename might not
+      always match what 'make defconfig' would use.
+
+    top_node:
+      The menu node (see the MenuNode class) of the implicit top-level menu.
+      Acts as the root of the menu tree.
+
+    mainmenu_text:
+      The prompt (title) of the top menu (top_node). Defaults to "Main menu".
+      Can be changed with the 'mainmenu' statement (see kconfig-language.txt).
+
+    variables:
+      A dictionary with all preprocessor variables, indexed by name. See the
+      Variable class.
+
+    warn:
+      Set this variable to True/False to enable/disable warnings. See
+      Kconfig.__init__().
+
+      When 'warn' is False, the values of the other warning-related variables
+      are ignored.
+
+      This variable as well as the other warn* variables can be read to check
+      the current warning settings.
+
+    warn_to_stderr:
+      Set this variable to True/False to enable/disable warnings on stderr. See
+      Kconfig.__init__().
+
+    warn_assign_undef:
+      Set this variable to True to generate warnings for assignments to
+      undefined symbols in configuration files.
+
+      This variable is False by default unless the KCONFIG_WARN_UNDEF_ASSIGN
+      environment variable was set to 'y' when the Kconfig instance was
+      created.
+
+    warn_assign_override:
+      Set this variable to True to generate warnings for multiple assignments
+      to the same symbol in configuration files, where the assignments set
+      different values (e.g. CONFIG_FOO=m followed by CONFIG_FOO=y, where the
+      last value would get used).
+
+      This variable is True by default. Disabling it might be useful when
+      merging configurations.
+
+    warn_assign_redun:
+      Like warn_assign_override, but for multiple assignments setting a symbol
+      to the same value.
+
+      This variable is True by default. Disabling it might be useful when
+      merging configurations.
+
+    warnings:
+      A list of strings containing all warnings that have been generated, for
+      cases where more flexibility is needed.
+
+      See the 'warn_to_stderr' parameter to Kconfig.__init__() and the
+      Kconfig.warn_to_stderr variable as well. Note that warnings still get
+      added to Kconfig.warnings when 'warn_to_stderr' is True.
+
+      Just as for warnings printed to stderr, only warnings that are enabled
+      will get added to Kconfig.warnings. See the various Kconfig.warn*
+      variables.
+
+    missing_syms:
+      A list with (name, value) tuples for all assignments to undefined symbols
+      within the most recently loaded .config file(s). 'name' is the symbol
+      name without the 'CONFIG_' prefix. 'value' is a string that gives the
+      right-hand side of the assignment verbatim.
+
+      See Kconfig.load_config() as well.
+
+    srctree:
+      The value the $srctree environment variable had when the Kconfig instance
+      was created, or the empty string if $srctree wasn't set. This gives nice
+      behavior with os.path.join(), which treats "" as the current directory,
+      without adding "./".
+
+      Kconfig files are looked up relative to $srctree (unless absolute paths
+      are used), and .config files are looked up relative to $srctree if they
+      are not found in the current directory. This is used to support
+      out-of-tree builds. The C tools use this environment variable in the same
+      way.
+
+      Changing $srctree after creating the Kconfig instance has no effect. Only
+      the value when the configuration is loaded matters. This avoids surprises
+      if multiple configurations are loaded with different values for $srctree.
+
+    config_prefix:
+      The value the CONFIG_ environment variable had when the Kconfig instance
+      was created, or "CONFIG_" if CONFIG_ wasn't set. This is the prefix used
+      (and expected) on symbol names in .config files and C headers. Used in
+      the same way in the C tools.
+
+    config_header:
+      The value the KCONFIG_CONFIG_HEADER environment variable had when the
+      Kconfig instance was created, or the empty string if
+      KCONFIG_CONFIG_HEADER wasn't set. This string is inserted verbatim at the
+      beginning of configuration files. See write_config().
+
+    header_header:
+      The value the KCONFIG_AUTOHEADER_HEADER environment variable had when the
+      Kconfig instance was created, or the empty string if
+      KCONFIG_AUTOHEADER_HEADER wasn't set. This string is inserted verbatim at
+      the beginning of header files. See write_autoconf().
+
+    filename/linenr:
+      The current parsing location, for use in Python preprocessor functions.
+      See the module docstring.
+    """
+    __slots__ = (
+        "_encoding",
+        "_functions",
+        "_set_match",
+        "_srctree_prefix",
+        "_unset_match",
+        "_warn_assign_no_prompt",
+        "choices",
+        "comments",
+        "config_header",
+        "config_prefix",
+        "const_syms",
+        "defconfig_list",
+        "defined_syms",
+        "env_vars",
+        "header_header",
+        "kconfig_filenames",
+        "m",
+        "menus",
+        "missing_syms",
+        "modules",
+        "n",
+        "named_choices",
+        "srctree",
+        "syms",
+        "top_node",
+        "unique_choices",
+        "unique_defined_syms",
+        "variables",
+        "warn",
+        "warn_assign_override",
+        "warn_assign_redun",
+        "warn_assign_undef",
+        "warn_to_stderr",
+        "warnings",
+        "y",
+
+        # Parsing-related
+        "_parsing_kconfigs",
+        "_readline",
+        "filename",
+        "linenr",
+        "_include_path",
+        "_filestack",
+        "_line",
+        "_tokens",
+        "_tokens_i",
+        "_reuse_tokens",
+    )
+
+    #
+    # Public interface
+    #
+
+    def __init__(self, filename="Kconfig", warn=True, warn_to_stderr=True,
+                 encoding="utf-8", suppress_traceback=False):
+        """
+        Creates a new Kconfig object by parsing Kconfig files.
+        Note that Kconfig files are not the same as .config files (which store
+        configuration symbol values).
+
+        See the module docstring for some environment variables that influence
+        default warning settings (KCONFIG_WARN_UNDEF and
+        KCONFIG_WARN_UNDEF_ASSIGN).
+
+        Raises KconfigError on syntax/semantic errors, and OSError or (possibly
+        a subclass of) IOError on IO errors ('errno', 'strerror', and
+        'filename' are available). Note that IOError is an alias for OSError on
+        Python 3, so it's enough to catch OSError there. If you need Python 2/3
+        compatibility, it's easiest to catch EnvironmentError, which is a
+        common base class of OSError/IOError on Python 2 and an alias for
+        OSError on Python 3.
+
+        filename (default: "Kconfig"):
+          The Kconfig file to load. For the Linux kernel, you'll want "Kconfig"
+          from the top-level directory, as environment variables will make sure
+          the right Kconfig is included from there (arch/$SRCARCH/Kconfig as of
+          writing).
+
+          If $srctree is set, 'filename' will be looked up relative to it.
+          $srctree is also used to look up source'd files within Kconfig files.
+          See the class documentation.
+
+          If you are using Kconfiglib via 'make scriptconfig', the filename of
+          the base base Kconfig file will be in sys.argv[1]. It's currently
+          always "Kconfig" in practice.
+
+        warn (default: True):
+          True if warnings related to this configuration should be generated.
+          This can be changed later by setting Kconfig.warn to True/False. It
+          is provided as a constructor argument since warnings might be
+          generated during parsing.
+
+          See the other Kconfig.warn_* variables as well, which enable or
+          suppress certain warnings when warnings are enabled.
+
+          All generated warnings are added to the Kconfig.warnings list. See
+          the class documentation.
+
+        warn_to_stderr (default: True):
+          True if warnings should be printed to stderr in addition to being
+          added to Kconfig.warnings.
+
+          This can be changed later by setting Kconfig.warn_to_stderr to
+          True/False.
+
+        encoding (default: "utf-8"):
+          The encoding to use when reading and writing files, and when decoding
+          output from commands run via $(shell). If None, the encoding
+          specified in the current locale will be used.
+
+          The "utf-8" default avoids exceptions on systems that are configured
+          to use the C locale, which implies an ASCII encoding.
+
+          This parameter has no effect on Python 2, due to implementation
+          issues (regular strings turning into Unicode strings, which are
+          distinct in Python 2). Python 2 doesn't decode regular strings
+          anyway.
+
+          Related PEP: https://www.python.org/dev/peps/pep-0538/
+
+        suppress_traceback (default: False):
+          Helper for tools. When True, any EnvironmentError or KconfigError
+          generated during parsing is caught, the exception message is printed
+          to stderr together with the command name, and sys.exit(1) is called
+          (which generates SystemExit).
+
+          This hides the Python traceback for "expected" errors like syntax
+          errors in Kconfig files.
+
+          Other exceptions besides EnvironmentError and KconfigError are still
+          propagated when suppress_traceback is True.
+        """
+        try:
+            self._init(filename, warn, warn_to_stderr, encoding)
+        except (EnvironmentError, KconfigError) as e:
+            if suppress_traceback:
+                cmd = sys.argv[0]  # Empty string if missing
+                if cmd:
+                    cmd += ": "
+                # Some long exception messages have extra newlines for better
+                # formatting when reported as an unhandled exception. Strip
+                # them here.
+                sys.exit(cmd + str(e).strip())
+            raise
+
+    def _init(self, filename, warn, warn_to_stderr, encoding):
+        # See __init__()
+
+        self._encoding = encoding
+
+        self.srctree = os.getenv("srctree", "")
+        # A prefix we can reliably strip from glob() results to get a filename
+        # relative to $srctree. relpath() can cause issues for symlinks,
+        # because it assumes symlink/../foo is the same as foo/.
+        self._srctree_prefix = realpath(self.srctree) + os.sep
+
+        self.warn = warn
+        self.warn_to_stderr = warn_to_stderr
+        self.warn_assign_undef = os.getenv("KCONFIG_WARN_UNDEF_ASSIGN") == "y"
+        self.warn_assign_override = True
+        self.warn_assign_redun = True
+        self._warn_assign_no_prompt = True
+
+        self.warnings = []
+
+        self.config_prefix = os.getenv("CONFIG_", "CONFIG_")
+        # Regular expressions for parsing .config files
+        self._set_match = _re_match(self.config_prefix + r"([^=]+)=(.*)")
+        self._unset_match = _re_match(r"# {}([^ ]+) is not set".format(
+            self.config_prefix))
+
+        self.config_header = os.getenv("KCONFIG_CONFIG_HEADER", "")
+        self.header_header = os.getenv("KCONFIG_AUTOHEADER_HEADER", "")
+
+        self.syms = {}
+        self.const_syms = {}
+        self.defined_syms = []
+        self.missing_syms = []
+        self.named_choices = {}
+        self.choices = []
+        self.menus = []
+        self.comments = []
+
+        for nmy in "n", "m", "y":
+            sym = Symbol()
+            sym.kconfig = self
+            sym.name = nmy
+            sym.is_constant = True
+            sym.orig_type = TRISTATE
+            sym._cached_tri_val = STR_TO_TRI[nmy]
+
+            self.const_syms[nmy] = sym
+
+        self.n = self.const_syms["n"]
+        self.m = self.const_syms["m"]
+        self.y = self.const_syms["y"]
+
+        # Make n/m/y well-formed symbols
+        for nmy in "n", "m", "y":
+            sym = self.const_syms[nmy]
+            sym.rev_dep = sym.weak_rev_dep = sym.direct_dep = self.n
+
+        # Maps preprocessor variables names to Variable instances
+        self.variables = {}
+
+        # Predefined preprocessor functions, with min/max number of arguments
+        self._functions = {
+            "info":       (_info_fn,       1, 1),
+            "error-if":   (_error_if_fn,   2, 2),
+            "filename":   (_filename_fn,   0, 0),
+            "lineno":     (_lineno_fn,     0, 0),
+            "shell":      (_shell_fn,      1, 1),
+            "warning-if": (_warning_if_fn, 2, 2),
+        }
+
+        # Add any user-defined preprocessor functions
+        try:
+            self._functions.update(
+                importlib.import_module(
+                    os.getenv("KCONFIG_FUNCTIONS", "kconfigfunctions")
+                ).functions)
+        except ImportError:
+            pass
+
+        # This determines whether previously unseen symbols are registered.
+        # They shouldn't be if we parse expressions after parsing, as part of
+        # Kconfig.eval_string().
+        self._parsing_kconfigs = True
+
+        self.modules = self._lookup_sym("MODULES")
+        self.defconfig_list = None
+
+        self.top_node = MenuNode()
+        self.top_node.kconfig = self
+        self.top_node.item = MENU
+        self.top_node.is_menuconfig = True
+        self.top_node.visibility = self.y
+        self.top_node.prompt = ("Main menu", self.y)
+        self.top_node.parent = None
+        self.top_node.dep = self.y
+        self.top_node.filename = filename
+        self.top_node.linenr = 1
+        self.top_node.include_path = ()
+
+        # Parse the Kconfig files
+
+        # Not used internally. Provided as a convenience.
+        self.kconfig_filenames = [filename]
+        self.env_vars = set()
+
+        # Keeps track of the location in the parent Kconfig files. Kconfig
+        # files usually source other Kconfig files. See _enter_file().
+        self._filestack = []
+        self._include_path = ()
+
+        # The current parsing location
+        self.filename = filename
+        self.linenr = 0
+
+        # Used to avoid retokenizing lines when we discover that they're not
+        # part of the construct currently being parsed. This is kinda like an
+        # unget operation.
+        self._reuse_tokens = False
+
+        # Open the top-level Kconfig file. Store the readline() method directly
+        # as a small optimization.
+        self._readline = self._open(join(self.srctree, filename), "r").readline
+
+        try:
+            # Parse the Kconfig files. Returns the last node, which we
+            # terminate with '.next = None'.
+            self._parse_block(None, self.top_node, self.top_node).next = None
+            self.top_node.list = self.top_node.next
+            self.top_node.next = None
+        except UnicodeDecodeError as e:
+            _decoding_error(e, self.filename)
+
+        # Close the top-level Kconfig file. __self__ fetches the 'file' object
+        # for the method.
+        self._readline.__self__.close()
+
+        self._parsing_kconfigs = False
+
+        # Do various menu tree post-processing
+        self._finalize_node(self.top_node, self.y)
+
+        self.unique_defined_syms = _ordered_unique(self.defined_syms)
+        self.unique_choices = _ordered_unique(self.choices)
+
+        # Do sanity checks. Some of these depend on everything being finalized.
+        self._check_sym_sanity()
+        self._check_choice_sanity()
+
+        # KCONFIG_STRICT is an older alias for KCONFIG_WARN_UNDEF, supported
+        # for backwards compatibility
+        if os.getenv("KCONFIG_WARN_UNDEF") == "y" or \
+           os.getenv("KCONFIG_STRICT") == "y":
+
+            self._check_undef_syms()
+
+        # Build Symbol._dependents for all symbols and choices
+        self._build_dep()
+
+        # Check for dependency loops
+        check_dep_loop_sym = _check_dep_loop_sym  # Micro-optimization
+        for sym in self.unique_defined_syms:
+            check_dep_loop_sym(sym, False)
+
+        # Add extra dependencies from choices to choice symbols that get
+        # awkward during dependency loop detection
+        self._add_choice_deps()
+
+    @property
+    def mainmenu_text(self):
+        """
+        See the class documentation.
+        """
+        return self.top_node.prompt[0]
+
+    @property
+    def defconfig_filename(self):
+        """
+        See the class documentation.
+        """
+        if self.defconfig_list:
+            for filename, cond in self.defconfig_list.defaults:
+                if expr_value(cond):
+                    try:
+                        with self._open_config(filename.str_value) as f:
+                            return f.name
+                    except EnvironmentError:
+                        continue
+
+        return None
+
+    def load_config(self, filename=None, replace=True, verbose=None):
+        """
+        Loads symbol values from a file in the .config format. Equivalent to
+        calling Symbol.set_value() to set each of the values.
+
+        "# CONFIG_FOO is not set" within a .config file sets the user value of
+        FOO to n. The C tools work the same way.
+
+        For each symbol, the Symbol.user_value attribute holds the value the
+        symbol was assigned in the .config file (if any). The user value might
+        differ from Symbol.str/tri_value if there are unsatisfied dependencies.
+
+        Calling this function also updates the Kconfig.missing_syms attribute
+        with a list of all assignments to undefined symbols within the
+        configuration file. Kconfig.missing_syms is cleared if 'replace' is
+        True, and appended to otherwise. See the documentation for
+        Kconfig.missing_syms as well.
+
+        See the Kconfig.__init__() docstring for raised exceptions
+        (OSError/IOError). KconfigError is never raised here.
+
+        filename (default: None):
+          Path to load configuration from (a string). Respects $srctree if set
+          (see the class documentation).
+
+          If 'filename' is None (the default), the configuration file to load
+          (if any) is calculated automatically, giving the behavior you'd
+          usually want:
+
+            1. If the KCONFIG_CONFIG environment variable is set, it gives the
+               path to the configuration file to load. Otherwise, ".config" is
+               used. See standard_config_filename().
+
+            2. If the path from (1.) doesn't exist, the configuration file
+               given by kconf.defconfig_filename is loaded instead, which is
+               derived from the 'option defconfig_list' symbol.
+
+            3. If (1.) and (2.) fail to find a configuration file to load, no
+               configuration file is loaded, and symbols retain their current
+               values (e.g., their default values). This is not an error.
+
+           See the return value as well.
+
+        replace (default: True):
+          If True, all existing user values will be cleared before loading the
+          .config. Pass False to merge configurations.
+
+        verbose (default: None):
+          Limited backwards compatibility to prevent crashes. A warning is
+          printed if anything but None is passed.
+
+          Prior to Kconfiglib 12.0.0, this option enabled printing of messages
+          to stdout when 'filename' was None. A message is (always) returned
+          now instead, which is more flexible.
+
+          Will probably be removed in some future version.
+
+        Returns a string with a message saying which file got loaded (or
+        possibly that no file got loaded, when 'filename' is None). This is
+        meant to reduce boilerplate in tools, which can do e.g.
+        print(kconf.load_config()). The returned message distinguishes between
+        loading (replace == True) and merging (replace == False).
+        """
+        if verbose is not None:
+            _warn_verbose_deprecated("load_config")
+
+        msg = None
+        if filename is None:
+            filename = standard_config_filename()
+            if not exists(filename) and \
+               not exists(join(self.srctree, filename)):
+                defconfig = self.defconfig_filename
+                if defconfig is None:
+                    return "Using default symbol values (no '{}')" \
+                           .format(filename)
+
+                msg = " default configuration '{}' (no '{}')" \
+                      .format(defconfig, filename)
+                filename = defconfig
+
+        if not msg:
+            msg = " configuration '{}'".format(filename)
+
+        # Disable the warning about assigning to symbols without prompts. This
+        # is normal and expected within a .config file.
+        self._warn_assign_no_prompt = False
+
+        # This stub only exists to make sure _warn_assign_no_prompt gets
+        # reenabled
+        try:
+            self._load_config(filename, replace)
+        except UnicodeDecodeError as e:
+            _decoding_error(e, filename)
+        finally:
+            self._warn_assign_no_prompt = True
+
+        return ("Loaded" if replace else "Merged") + msg
+
+    def _load_config(self, filename, replace):
+        with self._open_config(filename) as f:
+            if replace:
+                self.missing_syms = []
+
+                # If we're replacing the configuration, keep track of which
+                # symbols and choices got set so that we can unset the rest
+                # later. This avoids invalidating everything and is faster.
+                # Another benefit is that invalidation must be rock solid for
+                # it to work, making it a good test.
+
+                for sym in self.unique_defined_syms:
+                    sym._was_set = False
+
+                for choice in self.unique_choices:
+                    choice._was_set = False
+
+            # Small optimizations
+            set_match = self._set_match
+            unset_match = self._unset_match
+            get_sym = self.syms.get
+
+            for linenr, line in enumerate(f, 1):
+                # The C tools ignore trailing whitespace
+                line = line.rstrip()
+
+                match = set_match(line)
+                if match:
+                    name, val = match.groups()
+                    sym = get_sym(name)
+                    if not sym or not sym.nodes:
+                        self._undef_assign(name, val, filename, linenr)
+                        continue
+
+                    if sym.orig_type in _BOOL_TRISTATE:
+                        # The C implementation only checks the first character
+                        # to the right of '=', for whatever reason
+                        if not (sym.orig_type is BOOL
+                                and val.startswith(("y", "n")) or
+                                sym.orig_type is TRISTATE
+                                and val.startswith(("y", "m", "n"))):
+                            self._warn("'{}' is not a valid value for the {} "
+                                       "symbol {}. Assignment ignored."
+                                       .format(val, TYPE_TO_STR[sym.orig_type],
+                                               sym.name_and_loc),
+                                       filename, linenr)
+                            continue
+
+                        val = val[0]
+
+                        if sym.choice and val != "n":
+                            # During .config loading, we infer the mode of the
+                            # choice from the kind of values that are assigned
+                            # to the choice symbols
+
+                            prev_mode = sym.choice.user_value
+                            if prev_mode is not None and \
+                               TRI_TO_STR[prev_mode] != val:
+
+                                self._warn("both m and y assigned to symbols "
+                                           "within the same choice",
+                                           filename, linenr)
+
+                            # Set the choice's mode
+                            sym.choice.set_value(val)
+
+                    elif sym.orig_type is STRING:
+                        match = _conf_string_match(val)
+                        if not match:
+                            self._warn("malformed string literal in "
+                                       "assignment to {}. Assignment ignored."
+                                       .format(sym.name_and_loc),
+                                       filename, linenr)
+                            continue
+
+                        val = unescape(match.group(1))
+
+                else:
+                    match = unset_match(line)
+                    if not match:
+                        # Print a warning for lines that match neither
+                        # set_match() nor unset_match() and that are not blank
+                        # lines or comments. 'line' has already been
+                        # rstrip()'d, so blank lines show up as "" here.
+                        if line and not line.lstrip().startswith("#"):
+                            self._warn("ignoring malformed line '{}'"
+                                       .format(line),
+                                       filename, linenr)
+
+                        continue
+
+                    name = match.group(1)
+                    sym = get_sym(name)
+                    if not sym or not sym.nodes:
+                        self._undef_assign(name, "n", filename, linenr)
+                        continue
+
+                    if sym.orig_type not in _BOOL_TRISTATE:
+                        continue
+
+                    val = "n"
+
+                # Done parsing the assignment. Set the value.
+
+                if sym._was_set:
+                    self._assigned_twice(sym, val, filename, linenr)
+
+                sym.set_value(val)
+
+        if replace:
+            # If we're replacing the configuration, unset the symbols that
+            # didn't get set
+
+            for sym in self.unique_defined_syms:
+                if not sym._was_set:
+                    sym.unset_value()
+
+            for choice in self.unique_choices:
+                if not choice._was_set:
+                    choice.unset_value()
+
+    def _undef_assign(self, name, val, filename, linenr):
+        # Called for assignments to undefined symbols during .config loading
+
+        self.missing_syms.append((name, val))
+        if self.warn_assign_undef:
+            self._warn(
+                "attempt to assign the value '{}' to the undefined symbol {}"
+                .format(val, name), filename, linenr)
+
+    def _assigned_twice(self, sym, new_val, filename, linenr):
+        # Called when a symbol is assigned more than once in a .config file
+
+        # Use strings for bool/tristate user values in the warning
+        if sym.orig_type in _BOOL_TRISTATE:
+            user_val = TRI_TO_STR[sym.user_value]
+        else:
+            user_val = sym.user_value
+
+        msg = '{} set more than once. Old value "{}", new value "{}".'.format(
+            sym.name_and_loc, user_val, new_val)
+
+        if user_val == new_val:
+            if self.warn_assign_redun:
+                self._warn(msg, filename, linenr)
+        elif self.warn_assign_override:
+            self._warn(msg, filename, linenr)
+
+    def load_allconfig(self, filename):
+        """
+        Helper for all*config. Loads (merges) the configuration file specified
+        by KCONFIG_ALLCONFIG, if any. See Documentation/kbuild/kconfig.txt in
+        the Linux kernel.
+
+        Disables warnings for duplicated assignments within configuration files
+        for the duration of the call
+        (kconf.warn_assign_override/warn_assign_redun = False), and restores
+        the previous warning settings at the end. The KCONFIG_ALLCONFIG
+        configuration file is expected to override symbols.
+
+        Exits with sys.exit() (which raises a SystemExit exception) and prints
+        an error to stderr if KCONFIG_ALLCONFIG is set but the configuration
+        file can't be opened.
+
+        filename:
+          Command-specific configuration filename - "allyes.config",
+          "allno.config", etc.
+        """
+        load_allconfig(self, filename)
+
+    def write_autoconf(self, filename=None, header=None):
+        r"""
+        Writes out symbol values as a C header file, matching the format used
+        by include/generated/autoconf.h in the kernel.
+
+        The ordering of the #defines matches the one generated by
+        write_config(). The order in the C implementation depends on the hash
+        table implementation as of writing, and so won't match.
+
+        If 'filename' exists and its contents is identical to what would get
+        written out, it is left untouched. This avoids updating file metadata
+        like the modification time and possibly triggering redundant work in
+        build tools.
+
+        filename (default: None):
+          Path to write header to.
+
+          If None (the default), the path in the environment variable
+          KCONFIG_AUTOHEADER is used if set, and "include/generated/autoconf.h"
+          otherwise. This is compatible with the C tools.
+
+        header (default: None):
+          Text inserted verbatim at the beginning of the file. You would
+          usually want it enclosed in '/* */' to make it a C comment, and
+          include a trailing newline.
+
+          If None (the default), the value of the environment variable
+          KCONFIG_AUTOHEADER_HEADER had when the Kconfig instance was created
+          will be used if it was set, and no header otherwise. See the
+          Kconfig.header_header attribute.
+
+        Returns a string with a message saying that the header got saved, or
+        that there were no changes to it. This is meant to reduce boilerplate
+        in tools, which can do e.g. print(kconf.write_autoconf()).
+        """
+        if filename is None:
+            filename = os.getenv("KCONFIG_AUTOHEADER",
+                                 "include/generated/autoconf.h")
+
+        if self._write_if_changed(filename, self._autoconf_contents(header)):
+            return "Kconfig header saved to '{}'".format(filename)
+        return "No change to Kconfig header in '{}'".format(filename)
+
+    def _autoconf_contents(self, header):
+        # write_autoconf() helper. Returns the contents to write as a string,
+        # with 'header' or KCONFIG_AUTOHEADER_HEADER at the beginning.
+
+        if header is None:
+            header = self.header_header
+
+        chunks = [header]  # "".join()ed later
+        add = chunks.append
+
+        for sym in self.unique_defined_syms:
+            # _write_to_conf is determined when the value is calculated. This
+            # is a hidden function call due to property magic.
+            #
+            # Note: In client code, you can check if sym.config_string is empty
+            # instead, to avoid accessing the internal _write_to_conf variable
+            # (though it's likely to keep working).
+            val = sym.str_value
+            if not sym._write_to_conf:
+                continue
+
+            if sym.orig_type in _BOOL_TRISTATE:
+                if val == "y":
+                    add("#define {}{} 1\n"
+                        .format(self.config_prefix, sym.name))
+                elif val == "m":
+                    add("#define {}{}_MODULE 1\n"
+                        .format(self.config_prefix, sym.name))
+
+            elif sym.orig_type is STRING:
+                add('#define {}{} "{}"\n'
+                    .format(self.config_prefix, sym.name, escape(val)))
+
+            else:  # sym.orig_type in _INT_HEX:
+                if sym.orig_type is HEX and \
+                   not val.startswith(("0x", "0X")):
+                    val = "0x" + val
+
+                add("#define {}{} {}\n"
+                    .format(self.config_prefix, sym.name, val))
+
+        return "".join(chunks)
+
+    def write_config(self, filename=None, header=None, save_old=True,
+                     verbose=None):
+        r"""
+        Writes out symbol values in the .config format. The format matches the
+        C implementation, including ordering.
+
+        Symbols appear in the same order in generated .config files as they do
+        in the Kconfig files. For symbols defined in multiple locations, a
+        single assignment is written out corresponding to the first location
+        where the symbol is defined.
+
+        See the 'Intro to symbol values' section in the module docstring to
+        understand which symbols get written out.
+
+        If 'filename' exists and its contents is identical to what would get
+        written out, it is left untouched. This avoids updating file metadata
+        like the modification time and possibly triggering redundant work in
+        build tools.
+
+        See the Kconfig.__init__() docstring for raised exceptions
+        (OSError/IOError). KconfigError is never raised here.
+
+        filename (default: None):
+          Path to write configuration to (a string).
+
+          If None (the default), the path in the environment variable
+          KCONFIG_CONFIG is used if set, and ".config" otherwise. See
+          standard_config_filename().
+
+        header (default: None):
+          Text inserted verbatim at the beginning of the file. You would
+          usually want each line to start with '#' to make it a comment, and
+          include a trailing newline.
+
+          if None (the default), the value of the environment variable
+          KCONFIG_CONFIG_HEADER had when the Kconfig instance was created will
+          be used if it was set, and no header otherwise. See the
+          Kconfig.config_header attribute.
+
+        save_old (default: True):
+          If True and <filename> already exists, a copy of it will be saved to
+          <filename>.old in the same directory before the new configuration is
+          written.
+
+          Errors are silently ignored if <filename>.old cannot be written (e.g.
+          due to being a directory, or <filename> being something like
+          /dev/null).
+
+        verbose (default: None):
+          Limited backwards compatibility to prevent crashes. A warning is
+          printed if anything but None is passed.
+
+          Prior to Kconfiglib 12.0.0, this option enabled printing of messages
+          to stdout when 'filename' was None. A message is (always) returned
+          now instead, which is more flexible.
+
+          Will probably be removed in some future version.
+
+        Returns a string with a message saying which file got saved. This is
+        meant to reduce boilerplate in tools, which can do e.g.
+        print(kconf.write_config()).
+        """
+        if verbose is not None:
+            _warn_verbose_deprecated("write_config")
+
+        if filename is None:
+            filename = standard_config_filename()
+
+        contents = self._config_contents(header)
+        if self._contents_eq(filename, contents):
+            return "No change to configuration in '{}'".format(filename)
+
+        if save_old:
+            _save_old(filename)
+
+        with self._open(filename, "w") as f:
+            f.write(contents)
+
+        return "Configuration saved to '{}'".format(filename)
+
+    def _config_contents(self, header):
+        # write_config() helper. Returns the contents to write as a string,
+        # with 'header' or KCONFIG_CONFIG_HEADER at the beginning.
+        #
+        # More memory friendly would be to 'yield' the strings and
+        # "".join(_config_contents()), but it was a bit slower on my system.
+
+        # node_iter() was used here before commit 3aea9f7 ("Add '# end of
+        # <menu>' after menus in .config"). Those comments get tricky to
+        # implement with it.
+
+        for sym in self.unique_defined_syms:
+            sym._visited = False
+
+        if header is None:
+            header = self.config_header
+
+        chunks = [header]  # "".join()ed later
+        add = chunks.append
+
+        # Did we just print an '# end of ...' comment?
+        after_end_comment = False
+
+        node = self.top_node
+        while 1:
+            # Jump to the next node with an iterative tree walk
+            if node.list:
+                node = node.list
+            elif node.next:
+                node = node.next
+            else:
+                while node.parent:
+                    node = node.parent
+
+                    # Add a comment when leaving visible menus
+                    if node.item is MENU and expr_value(node.dep) and \
+                       expr_value(node.visibility) and \
+                       node is not self.top_node:
+                        add("# end of {}\n".format(node.prompt[0]))
+                        after_end_comment = True
+
+                    if node.next:
+                        node = node.next
+                        break
+                else:
+                    # No more nodes
+                    return "".join(chunks)
+
+            # Generate configuration output for the node
+
+            item = node.item
+
+            if item.__class__ is Symbol:
+                if item._visited:
+                    continue
+                item._visited = True
+
+                conf_string = item.config_string
+                if not conf_string:
+                    continue
+
+                if after_end_comment:
+                    # Add a blank line before the first symbol printed after an
+                    # '# end of ...' comment
+                    after_end_comment = False
+                    add("\n")
+                add(conf_string)
+
+            elif expr_value(node.dep) and \
+                 ((item is MENU and expr_value(node.visibility)) or
+                  item is COMMENT):
+
+                add("\n#\n# {}\n#\n".format(node.prompt[0]))
+                after_end_comment = False
+
+    def write_min_config(self, filename, header=None):
+        """
+        Writes out a "minimal" configuration file, omitting symbols whose value
+        matches their default value. The format matches the one produced by
+        'make savedefconfig'.
+
+        The resulting configuration file is incomplete, but a complete
+        configuration can be derived from it by loading it. Minimal
+        configuration files can serve as a more manageable configuration format
+        compared to a "full" .config file, especially when configurations files
+        are merged or edited by hand.
+
+        See the Kconfig.__init__() docstring for raised exceptions
+        (OSError/IOError). KconfigError is never raised here.
+
+        filename:
+          Path to write minimal configuration to.
+
+        header (default: None):
+          Text inserted verbatim at the beginning of the file. You would
+          usually want each line to start with '#' to make it a comment, and
+          include a final terminating newline.
+
+          if None (the default), the value of the environment variable
+          KCONFIG_CONFIG_HEADER had when the Kconfig instance was created will
+          be used if it was set, and no header otherwise. See the
+          Kconfig.config_header attribute.
+
+        Returns a string with a message saying the minimal configuration got
+        saved, or that there were no changes to it. This is meant to reduce
+        boilerplate in tools, which can do e.g.
+        print(kconf.write_min_config()).
+        """
+        if self._write_if_changed(filename, self._min_config_contents(header)):
+            return "Minimal configuration saved to '{}'".format(filename)
+        return "No change to minimal configuration in '{}'".format(filename)
+
+    def _min_config_contents(self, header):
+        # write_min_config() helper. Returns the contents to write as a string,
+        # with 'header' or KCONFIG_CONFIG_HEADER at the beginning.
+
+        if header is None:
+            header = self.config_header
+
+        chunks = [header]  # "".join()ed later
+        add = chunks.append
+
+        for sym in self.unique_defined_syms:
+            # Skip symbols that cannot be changed. Only check
+            # non-choice symbols, as selects don't affect choice
+            # symbols.
+            if not sym.choice and \
+               sym.visibility <= expr_value(sym.rev_dep):
+                continue
+
+            # Skip symbols whose value matches their default
+            if sym.str_value == sym._str_default():
+                continue
+
+            # Skip symbols that would be selected by default in a
+            # choice, unless the choice is optional or the symbol type
+            # isn't bool (it might be possible to set the choice mode
+            # to n or the symbol to m in those cases).
+            if sym.choice and \
+               not sym.choice.is_optional and \
+               sym.choice._selection_from_defaults() is sym and \
+               sym.orig_type is BOOL and \
+               sym.tri_value == 2:
+                continue
+
+            add(sym.config_string)
+
+        return "".join(chunks)
+
+    def sync_deps(self, path):
+        """
+        Creates or updates a directory structure that can be used to avoid
+        doing a full rebuild whenever the configuration is changed, mirroring
+        include/config/ in the kernel.
+
+        This function is intended to be called during each build, before
+        compiling source files that depend on configuration symbols.
+
+        See the Kconfig.__init__() docstring for raised exceptions
+        (OSError/IOError). KconfigError is never raised here.
+
+        path:
+          Path to directory
+
+        sync_deps(path) does the following:
+
+          1. If the directory <path> does not exist, it is created.
+
+          2. If <path>/auto.conf exists, old symbol values are loaded from it,
+             which are then compared against the current symbol values. If a
+             symbol has changed value (would generate different output in
+             autoconf.h compared to before), the change is signaled by
+             touch'ing a file corresponding to the symbol.
+
+             The first time sync_deps() is run on a directory, <path>/auto.conf
+             won't exist, and no old symbol values will be available. This
+             logically has the same effect as updating the entire
+             configuration.
+
+             The path to a symbol's file is calculated from the symbol's name
+             by replacing all '_' with '/' and appending '.h'. For example, the
+             symbol FOO_BAR_BAZ gets the file <path>/foo/bar/baz.h, and FOO
+             gets the file <path>/foo.h.
+
+             This scheme matches the C tools. The point is to avoid having a
+             single directory with a huge number of files, which the underlying
+             filesystem might not handle well.
+
+          3. A new auto.conf with the current symbol values is written, to keep
+             track of them for the next build.
+
+             If auto.conf exists and its contents is identical to what would
+             get written out, it is left untouched. This avoids updating file
+             metadata like the modification time and possibly triggering
+             redundant work in build tools.
+
+
+        The last piece of the puzzle is knowing what symbols each source file
+        depends on. Knowing that, dependencies can be added from source files
+        to the files corresponding to the symbols they depends on. The source
+        file will then get recompiled (only) when the symbol value changes
+        (provided sync_deps() is run first during each build).
+
+        The tool in the kernel that extracts symbol dependencies from source
+        files is scripts/basic/fixdep.c. Missing symbol files also correspond
+        to "not changed", which fixdep deals with by using the $(wildcard) Make
+        function when adding symbol prerequisites to source files.
+
+        In case you need a different scheme for your project, the sync_deps()
+        implementation can be used as a template.
+        """
+        if not exists(path):
+            os.mkdir(path, 0o755)
+
+        # Load old values from auto.conf, if any
+        self._load_old_vals(path)
+
+        for sym in self.unique_defined_syms:
+            # _write_to_conf is determined when the value is calculated. This
+            # is a hidden function call due to property magic.
+            #
+            # Note: In client code, you can check if sym.config_string is empty
+            # instead, to avoid accessing the internal _write_to_conf variable
+            # (though it's likely to keep working).
+            val = sym.str_value
+
+            # n tristate values do not get written to auto.conf and autoconf.h,
+            # making a missing symbol logically equivalent to n
+
+            if sym._write_to_conf:
+                if sym._old_val is None and \
+                   sym.orig_type in _BOOL_TRISTATE and \
+                   val == "n":
+                    # No old value (the symbol was missing or n), new value n.
+                    # No change.
+                    continue
+
+                if val == sym._old_val:
+                    # New value matches old. No change.
+                    continue
+
+            elif sym._old_val is None:
+                # The symbol wouldn't appear in autoconf.h (because
+                # _write_to_conf is false), and it wouldn't have appeared in
+                # autoconf.h previously either (because it didn't appear in
+                # auto.conf). No change.
+                continue
+
+            # 'sym' has a new value. Flag it.
+            _touch_dep_file(path, sym.name)
+
+        # Remember the current values as the "new old" values.
+        #
+        # This call could go anywhere after the call to _load_old_vals(), but
+        # putting it last means _sync_deps() can be safely rerun if it fails
+        # before this point.
+        self._write_old_vals(path)
+
+    def _load_old_vals(self, path):
+        # Loads old symbol values from auto.conf into a dedicated
+        # Symbol._old_val field. Mirrors load_config().
+        #
+        # The extra field could be avoided with some trickery involving dumping
+        # symbol values and restoring them later, but this is simpler and
+        # faster. The C tools also use a dedicated field for this purpose.
+
+        for sym in self.unique_defined_syms:
+            sym._old_val = None
+
+        try:
+            auto_conf = self._open(join(path, "auto.conf"), "r")
+        except EnvironmentError as e:
+            if e.errno == errno.ENOENT:
+                # No old values
+                return
+            raise
+
+        with auto_conf as f:
+            for line in f:
+                match = self._set_match(line)
+                if not match:
+                    # We only expect CONFIG_FOO=... (and possibly a header
+                    # comment) in auto.conf
+                    continue
+
+                name, val = match.groups()
+                if name in self.syms:
+                    sym = self.syms[name]
+
+                    if sym.orig_type is STRING:
+                        match = _conf_string_match(val)
+                        if not match:
+                            continue
+                        val = unescape(match.group(1))
+
+                    self.syms[name]._old_val = val
+                else:
+                    # Flag that the symbol no longer exists, in
+                    # case something still depends on it
+                    _touch_dep_file(path, name)
+
+    def _write_old_vals(self, path):
+        # Helper for writing auto.conf. Basically just a simplified
+        # write_config() that doesn't write any comments (including
+        # '# CONFIG_FOO is not set' comments). The format matches the C
+        # implementation, though the ordering is arbitrary there (depends on
+        # the hash table implementation).
+        #
+        # A separate helper function is neater than complicating write_config()
+        # by passing a flag to it, plus we only need to look at symbols here.
+
+        self._write_if_changed(
+            os.path.join(path, "auto.conf"),
+            self._old_vals_contents())
+
+    def _old_vals_contents(self):
+        # _write_old_vals() helper. Returns the contents to write as a string.
+
+        # Temporary list instead of generator makes this a bit faster
+        return "".join([
+            sym.config_string for sym in self.unique_defined_syms
+                if not (sym.orig_type in _BOOL_TRISTATE and not sym.tri_value)
+        ])
+
+    def node_iter(self, unique_syms=False):
+        """
+        Returns a generator for iterating through all MenuNode's in the Kconfig
+        tree. The iteration is done in Kconfig definition order (each node is
+        visited before its children, and the children of a node are visited
+        before the next node).
+
+        The Kconfig.top_node menu node is skipped. It contains an implicit menu
+        that holds the top-level items.
+
+        As an example, the following code will produce a list equal to
+        Kconfig.defined_syms:
+
+          defined_syms = [node.item for node in kconf.node_iter()
+                          if isinstance(node.item, Symbol)]
+
+        unique_syms (default: False):
+          If True, only the first MenuNode will be included for symbols defined
+          in multiple locations.
+
+          Using kconf.node_iter(True) in the example above would give a list
+          equal to unique_defined_syms.
+        """
+        if unique_syms:
+            for sym in self.unique_defined_syms:
+                sym._visited = False
+
+        node = self.top_node
+        while 1:
+            # Jump to the next node with an iterative tree walk
+            if node.list:
+                node = node.list
+            elif node.next:
+                node = node.next
+            else:
+                while node.parent:
+                    node = node.parent
+                    if node.next:
+                        node = node.next
+                        break
+                else:
+                    # No more nodes
+                    return
+
+            if unique_syms and node.item.__class__ is Symbol:
+                if node.item._visited:
+                    continue
+                node.item._visited = True
+
+            yield node
+
+    def eval_string(self, s):
+        """
+        Returns the tristate value of the expression 's', represented as 0, 1,
+        and 2 for n, m, and y, respectively. Raises KconfigError on syntax
+        errors. Warns if undefined symbols are referenced.
+
+        As an example, if FOO and BAR are tristate symbols at least one of
+        which has the value y, then eval_string("y && (FOO || BAR)") returns
+        2 (y).
+
+        To get the string value of non-bool/tristate symbols, use
+        Symbol.str_value. eval_string() always returns a tristate value, and
+        all non-bool/tristate symbols have the tristate value 0 (n).
+
+        The expression parsing is consistent with how parsing works for
+        conditional ('if ...') expressions in the configuration, and matches
+        the C implementation. m is rewritten to 'm && MODULES', so
+        eval_string("m") will return 0 (n) unless modules are enabled.
+        """
+        # The parser is optimized to be fast when parsing Kconfig files (where
+        # an expression can never appear at the beginning of a line). We have
+        # to monkey-patch things a bit here to reuse it.
+
+        self.filename = None
+
+        self._tokens = self._tokenize("if " + s)
+        # Strip "if " to avoid giving confusing error messages
+        self._line = s
+        self._tokens_i = 1  # Skip the 'if' token
+
+        return expr_value(self._expect_expr_and_eol())
+
+    def unset_values(self):
+        """
+        Removes any user values from all symbols, as if Kconfig.load_config()
+        or Symbol.set_value() had never been called.
+        """
+        self._warn_assign_no_prompt = False
+        try:
+            # set_value() already rejects undefined symbols, and they don't
+            # need to be invalidated (because their value never changes), so we
+            # can just iterate over defined symbols
+            for sym in self.unique_defined_syms:
+                sym.unset_value()
+
+            for choice in self.unique_choices:
+                choice.unset_value()
+        finally:
+            self._warn_assign_no_prompt = True
+
+    def enable_warnings(self):
+        """
+        Do 'Kconfig.warn = True' instead. Maintained for backwards
+        compatibility.
+        """
+        self.warn = True
+
+    def disable_warnings(self):
+        """
+        Do 'Kconfig.warn = False' instead. Maintained for backwards
+        compatibility.
+        """
+        self.warn = False
+
+    def enable_stderr_warnings(self):
+        """
+        Do 'Kconfig.warn_to_stderr = True' instead. Maintained for backwards
+        compatibility.
+        """
+        self.warn_to_stderr = True
+
+    def disable_stderr_warnings(self):
+        """
+        Do 'Kconfig.warn_to_stderr = False' instead. Maintained for backwards
+        compatibility.
+        """
+        self.warn_to_stderr = False
+
+    def enable_undef_warnings(self):
+        """
+        Do 'Kconfig.warn_assign_undef = True' instead. Maintained for backwards
+        compatibility.
+        """
+        self.warn_assign_undef = True
+
+    def disable_undef_warnings(self):
+        """
+        Do 'Kconfig.warn_assign_undef = False' instead. Maintained for
+        backwards compatibility.
+        """
+        self.warn_assign_undef = False
+
+    def enable_override_warnings(self):
+        """
+        Do 'Kconfig.warn_assign_override = True' instead. Maintained for
+        backwards compatibility.
+        """
+        self.warn_assign_override = True
+
+    def disable_override_warnings(self):
+        """
+        Do 'Kconfig.warn_assign_override = False' instead. Maintained for
+        backwards compatibility.
+        """
+        self.warn_assign_override = False
+
+    def enable_redun_warnings(self):
+        """
+        Do 'Kconfig.warn_assign_redun = True' instead. Maintained for backwards
+        compatibility.
+        """
+        self.warn_assign_redun = True
+
+    def disable_redun_warnings(self):
+        """
+        Do 'Kconfig.warn_assign_redun = False' instead. Maintained for
+        backwards compatibility.
+        """
+        self.warn_assign_redun = False
+
+    def __repr__(self):
+        """
+        Returns a string with information about the Kconfig object when it is
+        evaluated on e.g. the interactive Python prompt.
+        """
+        def status(flag):
+            return "enabled" if flag else "disabled"
+
+        return "<{}>".format(", ".join((
+            "configuration with {} symbols".format(len(self.syms)),
+            'main menu prompt "{}"'.format(self.mainmenu_text),
+            "srctree is current directory" if not self.srctree else
+                'srctree "{}"'.format(self.srctree),
+            'config symbol prefix "{}"'.format(self.config_prefix),
+            "warnings " + status(self.warn),
+            "printing of warnings to stderr " + status(self.warn_to_stderr),
+            "undef. symbol assignment warnings " +
+                status(self.warn_assign_undef),
+            "overriding symbol assignment warnings " +
+                status(self.warn_assign_override),
+            "redundant symbol assignment warnings " +
+                status(self.warn_assign_redun)
+        )))
+
+    #
+    # Private methods
+    #
+
+
+    #
+    # File reading
+    #
+
+    def _open_config(self, filename):
+        # Opens a .config file. First tries to open 'filename', then
+        # '$srctree/filename' if $srctree was set when the configuration was
+        # loaded.
+
+        try:
+            return self._open(filename, "r")
+        except EnvironmentError as e:
+            # This will try opening the same file twice if $srctree is unset,
+            # but it's not a big deal
+            try:
+                return self._open(join(self.srctree, filename), "r")
+            except EnvironmentError as e2:
+                # This is needed for Python 3, because e2 is deleted after
+                # the try block:
+                #
+                # https://docs.python.org/3/reference/compound_stmts.html#the-try-statement
+                e = e2
+
+            raise _KconfigIOError(
+                e, "Could not open '{}' ({}: {}). Check that the $srctree "
+                   "environment variable ({}) is set correctly."
+                   .format(filename, errno.errorcode[e.errno], e.strerror,
+                           "set to '{}'".format(self.srctree) if self.srctree
+                               else "unset or blank"))
+
+    def _enter_file(self, filename):
+        # Jumps to the beginning of a sourced Kconfig file, saving the previous
+        # position and file object.
+        #
+        # filename:
+        #   Absolute path to file
+
+        # Path relative to $srctree, stored in e.g. self.filename (which makes
+        # it indirectly show up in MenuNode.filename). Equals 'filename' for
+        # absolute paths passed to 'source'.
+        if filename.startswith(self._srctree_prefix):
+            # Relative path (or a redundant absolute path to within $srctree,
+            # but it's probably fine to reduce those too)
+            rel_filename = filename[len(self._srctree_prefix):]
+        else:
+            # Absolute path
+            rel_filename = filename
+
+        self.kconfig_filenames.append(rel_filename)
+
+        # The parent Kconfig files are represented as a list of
+        # (<include path>, <Python 'file' object for Kconfig file>) tuples.
+        #
+        # <include path> is immutable and holds a *tuple* of
+        # (<filename>, <linenr>) tuples, giving the locations of the 'source'
+        # statements in the parent Kconfig files. The current include path is
+        # also available in Kconfig._include_path.
+        #
+        # The point of this redundant setup is to allow Kconfig._include_path
+        # to be assigned directly to MenuNode.include_path without having to
+        # copy it, sharing it wherever possible.
+
+        # Save include path and 'file' object (via its 'readline' function)
+        # before entering the file
+        self._filestack.append((self._include_path, self._readline))
+
+        # _include_path is a tuple, so this rebinds the variable instead of
+        # doing in-place modification
+        self._include_path += ((self.filename, self.linenr),)
+
+        # Check for recursive 'source'
+        for name, _ in self._include_path:
+            if name == rel_filename:
+                raise KconfigError(
+                    "\n{}:{}: recursive 'source' of '{}' detected. Check that "
+                    "environment variables are set correctly.\n"
+                    "Include path:\n{}"
+                    .format(self.filename, self.linenr, rel_filename,
+                            "\n".join("{}:{}".format(name, linenr)
+                                      for name, linenr in self._include_path)))
+
+        try:
+            self._readline = self._open(filename, "r").readline
+        except EnvironmentError as e:
+            # We already know that the file exists
+            raise _KconfigIOError(
+                e, "{}:{}: Could not open '{}' (in '{}') ({}: {})"
+                   .format(self.filename, self.linenr, filename,
+                           self._line.strip(),
+                           errno.errorcode[e.errno], e.strerror))
+
+        self.filename = rel_filename
+        self.linenr = 0
+
+    def _leave_file(self):
+        # Returns from a Kconfig file to the file that sourced it. See
+        # _enter_file().
+
+        # Restore location from parent Kconfig file
+        self.filename, self.linenr = self._include_path[-1]
+        # Restore include path and 'file' object
+        self._readline.__self__.close()  # __self__ fetches the 'file' object
+        self._include_path, self._readline = self._filestack.pop()
+
+    def _next_line(self):
+        # Fetches and tokenizes the next line from the current Kconfig file.
+        # Returns False at EOF and True otherwise.
+
+        # We might already have tokens from parsing a line and discovering that
+        # it's part of a different construct
+        if self._reuse_tokens:
+            self._reuse_tokens = False
+            # self._tokens_i is known to be 1 here, because _parse_props()
+            # leaves it like that when it can't recognize a line (or parses a
+            # help text)
+            return True
+
+        # readline() returns '' over and over at EOF, which we rely on for help
+        # texts at the end of files (see _line_after_help())
+        line = self._readline()
+        if not line:
+            return False
+        self.linenr += 1
+
+        # Handle line joining
+        while line.endswith("\\\n"):
+            line = line[:-2] + self._readline()
+            self.linenr += 1
+
+        self._tokens = self._tokenize(line)
+        # Initialize to 1 instead of 0 to factor out code from _parse_block()
+        # and _parse_props(). They immediately fetch self._tokens[0].
+        self._tokens_i = 1
+
+        return True
+
+    def _line_after_help(self, line):
+        # Tokenizes a line after a help text. This case is special in that the
+        # line has already been fetched (to discover that it isn't part of the
+        # help text).
+        #
+        # An earlier version used a _saved_line variable instead that was
+        # checked in _next_line(). This special-casing gets rid of it and makes
+        # _reuse_tokens alone sufficient to handle unget.
+
+        # Handle line joining
+        while line.endswith("\\\n"):
+            line = line[:-2] + self._readline()
+            self.linenr += 1
+
+        self._tokens = self._tokenize(line)
+        self._reuse_tokens = True
+
+    def _write_if_changed(self, filename, contents):
+        # Writes 'contents' into 'filename', but only if it differs from the
+        # current contents of the file.
+        #
+        # Another variant would be write a temporary file on the same
+        # filesystem, compare the files, and rename() the temporary file if it
+        # differs, but it breaks stuff like write_config("/dev/null"), which is
+        # used out there to force evaluation-related warnings to be generated.
+        # This simple version is pretty failsafe and portable.
+        #
+        # Returns True if the file has changed and is updated, and False
+        # otherwise.
+
+        if self._contents_eq(filename, contents):
+            return False
+        with self._open(filename, "w") as f:
+            f.write(contents)
+        return True
+
+    def _contents_eq(self, filename, contents):
+        # Returns True if the contents of 'filename' is 'contents' (a string),
+        # and False otherwise (including if 'filename' can't be opened/read)
+
+        try:
+            with self._open(filename, "r") as f:
+                # Robust re. things like encoding and line endings (mmap()
+                # trickery isn't)
+                return f.read(len(contents) + 1) == contents
+        except EnvironmentError:
+            # If the error here would prevent writing the file as well, we'll
+            # notice it later
+            return False
+
+    #
+    # Tokenization
+    #
+
+    def _lookup_sym(self, name):
+        # Fetches the symbol 'name' from the symbol table, creating and
+        # registering it if it does not exist. If '_parsing_kconfigs' is False,
+        # it means we're in eval_string(), and new symbols won't be registered.
+
+        if name in self.syms:
+            return self.syms[name]
+
+        sym = Symbol()
+        sym.kconfig = self
+        sym.name = name
+        sym.is_constant = False
+        sym.rev_dep = sym.weak_rev_dep = sym.direct_dep = self.n
+
+        if self._parsing_kconfigs:
+            self.syms[name] = sym
+        else:
+            self._warn("no symbol {} in configuration".format(name))
+
+        return sym
+
+    def _lookup_const_sym(self, name):
+        # Like _lookup_sym(), for constant (quoted) symbols
+
+        if name in self.const_syms:
+            return self.const_syms[name]
+
+        sym = Symbol()
+        sym.kconfig = self
+        sym.name = name
+        sym.is_constant = True
+        sym.rev_dep = sym.weak_rev_dep = sym.direct_dep = self.n
+
+        if self._parsing_kconfigs:
+            self.const_syms[name] = sym
+
+        return sym
+
+    def _tokenize(self, s):
+        # Parses 's', returning a None-terminated list of tokens. Registers any
+        # new symbols encountered with _lookup(_const)_sym().
+        #
+        # Tries to be reasonably speedy by processing chunks of text via
+        # regexes and string operations where possible. This is the biggest
+        # hotspot during parsing.
+        #
+        # It might be possible to rewrite this to 'yield' tokens instead,
+        # working across multiple lines. Lookback and compatibility with old
+        # janky versions of the C tools complicate things though.
+
+        self._line = s  # Used for error reporting
+
+        # Initial token on the line
+        match = _command_match(s)
+        if not match:
+            if s.isspace() or s.lstrip().startswith("#"):
+                return (None,)
+            self._parse_error("unknown token at start of line")
+
+        # Tricky implementation detail: While parsing a token, 'token' refers
+        # to the previous token. See _STRING_LEX for why this is needed.
+        token = _get_keyword(match.group(1))
+        if not token:
+            # Backwards compatibility with old versions of the C tools, which
+            # (accidentally) accepted stuff like "--help--" and "-help---".
+            # This was fixed in the C tools by commit c2264564 ("kconfig: warn
+            # of unhandled characters in Kconfig commands"), committed in July
+            # 2015, but it seems people still run Kconfiglib on older kernels.
+            if s.strip(" \t\n-") == "help":
+                return (_T_HELP, None)
+
+            # If the first token is not a keyword (and not a weird help token),
+            # we have a preprocessor variable assignment (or a bare macro on a
+            # line)
+            self._parse_assignment(s)
+            return (None,)
+
+        tokens = [token]
+        # The current index in the string being tokenized
+        i = match.end()
+
+        # Main tokenization loop (for tokens past the first one)
+        while i < len(s):
+            # Test for an identifier/keyword first. This is the most common
+            # case.
+            match = _id_keyword_match(s, i)
+            if match:
+                # We have an identifier or keyword
+
+                # Check what it is. lookup_sym() will take care of allocating
+                # new symbols for us the first time we see them. Note that
+                # 'token' still refers to the previous token.
+
+                name = match.group(1)
+                keyword = _get_keyword(name)
+                if keyword:
+                    # It's a keyword
+                    token = keyword
+                    # Jump past it
+                    i = match.end()
+
+                elif token not in _STRING_LEX:
+                    # It's a non-const symbol, except we translate n, m, and y
+                    # into the corresponding constant symbols, like the C
+                    # implementation
+
+                    if "$" in name:
+                        # Macro expansion within symbol name
+                        name, s, i = self._expand_name(s, i)
+                    else:
+                        i = match.end()
+
+                    token = self.const_syms[name] if name in STR_TO_TRI else \
+                        self._lookup_sym(name)
+
+                else:
+                    # It's a case of missing quotes. For example, the
+                    # following is accepted:
+                    #
+                    #   menu unquoted_title
+                    #
+                    #   config A
+                    #       tristate unquoted_prompt
+                    #
+                    #   endmenu
+                    #
+                    # Named choices ('choice FOO') also end up here.
+
+                    if token is not _T_CHOICE:
+                        self._warn("style: quotes recommended around '{}' in '{}'"
+                                   .format(name, self._line.strip()),
+                                   self.filename, self.linenr)
+
+                    token = name
+                    i = match.end()
+
+            else:
+                # Neither a keyword nor a non-const symbol
+
+                # We always strip whitespace after tokens, so it is safe to
+                # assume that s[i] is the start of a token here.
+                c = s[i]
+
+                if c in "\"'":
+                    if "$" not in s and "\\" not in s:
+                        # Fast path for lines without $ and \. Find the
+                        # matching quote.
+                        end_i = s.find(c, i + 1) + 1
+                        if not end_i:
+                            self._parse_error("unterminated string")
+                        val = s[i + 1:end_i - 1]
+                        i = end_i
+                    else:
+                        # Slow path
+                        s, end_i = self._expand_str(s, i)
+
+                        # os.path.expandvars() and the $UNAME_RELEASE replace()
+                        # is a backwards compatibility hack, which should be
+                        # reasonably safe as expandvars() leaves references to
+                        # undefined env. vars. as is.
+                        #
+                        # The preprocessor functionality changed how
+                        # environment variables are referenced, to $(FOO).
+                        val = expandvars(s[i + 1:end_i - 1]
+                                         .replace("$UNAME_RELEASE",
+                                                  _UNAME_RELEASE))
+
+                        i = end_i
+
+                    # This is the only place where we don't survive with a
+                    # single token of lookback: 'option env="FOO"' does not
+                    # refer to a constant symbol named "FOO".
+                    token = \
+                        val if token in _STRING_LEX or tokens[0] is _T_OPTION \
+                        else self._lookup_const_sym(val)
+
+                elif s.startswith("&&", i):
+                    token = _T_AND
+                    i += 2
+
+                elif s.startswith("||", i):
+                    token = _T_OR
+                    i += 2
+
+                elif c == "=":
+                    token = _T_EQUAL
+                    i += 1
+
+                elif s.startswith("!=", i):
+                    token = _T_UNEQUAL
+                    i += 2
+
+                elif c == "!":
+                    token = _T_NOT
+                    i += 1
+
+                elif c == "(":
+                    token = _T_OPEN_PAREN
+                    i += 1
+
+                elif c == ")":
+                    token = _T_CLOSE_PAREN
+                    i += 1
+
+                elif c == "#":
+                    break
+
+
+                # Very rare
+
+                elif s.startswith("<=", i):
+                    token = _T_LESS_EQUAL
+                    i += 2
+
+                elif c == "<":
+                    token = _T_LESS
+                    i += 1
+
+                elif s.startswith(">=", i):
+                    token = _T_GREATER_EQUAL
+                    i += 2
+
+                elif c == ">":
+                    token = _T_GREATER
+                    i += 1
+
+
+                else:
+                    self._parse_error("unknown tokens in line")
+
+
+                # Skip trailing whitespace
+                while i < len(s) and s[i].isspace():
+                    i += 1
+
+
+            # Add the token
+            tokens.append(token)
+
+        # None-terminating the token list makes token fetching simpler/faster
+        tokens.append(None)
+
+        return tokens
+
+    # Helpers for syntax checking and token fetching. See the
+    # 'Intro to expressions' section for what a constant symbol is.
+    #
+    # More of these could be added, but the single-use cases are inlined as an
+    # optimization.
+
+    def _expect_sym(self):
+        token = self._tokens[self._tokens_i]
+        self._tokens_i += 1
+
+        if token.__class__ is not Symbol:
+            self._parse_error("expected symbol")
+
+        return token
+
+    def _expect_nonconst_sym(self):
+        # Used for 'select' and 'imply' only. We know the token indices.
+
+        token = self._tokens[1]
+        self._tokens_i = 2
+
+        if token.__class__ is not Symbol or token.is_constant:
+            self._parse_error("expected nonconstant symbol")
+
+        return token
+
+    def _expect_str_and_eol(self):
+        token = self._tokens[self._tokens_i]
+        self._tokens_i += 1
+
+        if token.__class__ is not str:
+            self._parse_error("expected string")
+
+        if self._tokens[self._tokens_i] is not None:
+            self._trailing_tokens_error()
+
+        return token
+
+    def _expect_expr_and_eol(self):
+        expr = self._parse_expr(True)
+
+        if self._tokens[self._tokens_i] is not None:
+            self._trailing_tokens_error()
+
+        return expr
+
+    def _check_token(self, token):
+        # If the next token is 'token', removes it and returns True
+
+        if self._tokens[self._tokens_i] is token:
+            self._tokens_i += 1
+            return True
+        return False
+
+    #
+    # Preprocessor logic
+    #
+
+    def _parse_assignment(self, s):
+        # Parses a preprocessor variable assignment, registering the variable
+        # if it doesn't already exist. Also takes care of bare macros on lines
+        # (which are allowed, and can be useful for their side effects).
+
+        # Expand any macros in the left-hand side of the assignment (the
+        # variable name)
+        s = s.lstrip()
+        i = 0
+        while 1:
+            i = _assignment_lhs_fragment_match(s, i).end()
+            if s.startswith("$(", i):
+                s, i = self._expand_macro(s, i, ())
+            else:
+                break
+
+        if s.isspace():
+            # We also accept a bare macro on a line (e.g.
+            # $(warning-if,$(foo),ops)), provided it expands to a blank string
+            return
+
+        # Assigned variable
+        name = s[:i]
+
+
+        # Extract assignment operator (=, :=, or +=) and value
+        rhs_match = _assignment_rhs_match(s, i)
+        if not rhs_match:
+            self._parse_error("syntax error")
+
+        op, val = rhs_match.groups()
+
+
+        if name in self.variables:
+            # Already seen variable
+            var = self.variables[name]
+        else:
+            # New variable
+            var = Variable()
+            var.kconfig = self
+            var.name = name
+            var._n_expansions = 0
+            self.variables[name] = var
+
+            # += acts like = on undefined variables (defines a recursive
+            # variable)
+            if op == "+=":
+                op = "="
+
+        if op == "=":
+            var.is_recursive = True
+            var.value = val
+        elif op == ":=":
+            var.is_recursive = False
+            var.value = self._expand_whole(val, ())
+        else:  # op == "+="
+            # += does immediate expansion if the variable was last set
+            # with :=
+            var.value += " " + (val if var.is_recursive else
+                                self._expand_whole(val, ()))
+
+    def _expand_whole(self, s, args):
+        # Expands preprocessor macros in all of 's'. Used whenever we don't
+        # have to worry about delimiters. See _expand_macro() re. the 'args'
+        # parameter.
+        #
+        # Returns the expanded string.
+
+        i = 0
+        while 1:
+            i = s.find("$(", i)
+            if i == -1:
+                break
+            s, i = self._expand_macro(s, i, args)
+        return s
+
+    def _expand_name(self, s, i):
+        # Expands a symbol name starting at index 'i' in 's'.
+        #
+        # Returns the expanded name, the expanded 's' (including the part
+        # before the name), and the index of the first character in the next
+        # token after the name.
+
+        s, end_i = self._expand_name_iter(s, i)
+        name = s[i:end_i]
+        # isspace() is False for empty strings
+        if not name.strip():
+            # Avoid creating a Kconfig symbol with a blank name. It's almost
+            # guaranteed to be an error.
+            self._parse_error("macro expanded to blank string")
+
+        # Skip trailing whitespace
+        while end_i < len(s) and s[end_i].isspace():
+            end_i += 1
+
+        return name, s, end_i
+
+    def _expand_name_iter(self, s, i):
+        # Expands a symbol name starting at index 'i' in 's'.
+        #
+        # Returns the expanded 's' (including the part before the name) and the
+        # index of the first character after the expanded name in 's'.
+
+        while 1:
+            match = _name_special_search(s, i)
+
+            if match.group() != "$(":
+                return (s, match.start())
+            s, i = self._expand_macro(s, match.start(), ())
+
+    def _expand_str(self, s, i):
+        # Expands a quoted string starting at index 'i' in 's'. Handles both
+        # backslash escapes and macro expansion.
+        #
+        # Returns the expanded 's' (including the part before the string) and
+        # the index of the first character after the expanded string in 's'.
+
+        quote = s[i]
+        i += 1  # Skip over initial "/'
+        while 1:
+            match = _string_special_search(s, i)
+            if not match:
+                self._parse_error("unterminated string")
+
+
+            if match.group() == quote:
+                # Found the end of the string
+                return (s, match.end())
+
+            elif match.group() == "\\":
+                # Replace '\x' with 'x'. 'i' ends up pointing to the character
+                # after 'x', which allows macros to be canceled with '\$(foo)'.
+                i = match.end()
+                s = s[:match.start()] + s[i:]
+
+            elif match.group() == "$(":
+                # A macro call within the string
+                s, i = self._expand_macro(s, match.start(), ())
+
+            else:
+                # A ' quote within " quotes or vice versa
+                i += 1
+
+    def _expand_macro(self, s, i, args):
+        # Expands a macro starting at index 'i' in 's'. If this macro resulted
+        # from the expansion of another macro, 'args' holds the arguments
+        # passed to that macro.
+        #
+        # Returns the expanded 's' (including the part before the macro) and
+        # the index of the first character after the expanded macro in 's'.
+
+        res = s[:i]
+        i += 2  # Skip over "$("
+
+        arg_start = i  # Start of current macro argument
+        new_args = []  # Arguments of this macro call
+        nesting = 0  # Current parentheses nesting level
+
+        while 1:
+            match = _macro_special_search(s, i)
+            if not match:
+                self._parse_error("missing end parenthesis in macro expansion")
+
+
+            if match.group() == "(":
+                nesting += 1
+                i = match.end()
+
+            elif match.group() == ")":
+                if nesting:
+                    nesting -= 1
+                    i = match.end()
+                    continue
+
+                # Found the end of the macro
+
+                new_args.append(s[arg_start:match.start()])
+
+                # $(1) is replaced by the first argument to the function, etc.,
+                # provided at least that many arguments were passed
+
+                try:
+                    # Does the macro look like an integer, with a corresponding
+                    # argument? If so, expand it to the value of the argument.
+                    res += args[int(new_args[0])]
+                except (ValueError, IndexError):
+                    # Regular variables are just functions without arguments,
+                    # and also go through the function value path
+                    res += self._fn_val(new_args)
+
+                return (res + s[match.end():], len(res))
+
+            elif match.group() == ",":
+                i = match.end()
+                if nesting:
+                    continue
+
+                # Found the end of a macro argument
+                new_args.append(s[arg_start:match.start()])
+                arg_start = i
+
+            else:  # match.group() == "$("
+                # A nested macro call within the macro
+                s, i = self._expand_macro(s, match.start(), args)
+
+    def _fn_val(self, args):
+        # Returns the result of calling the function args[0] with the arguments
+        # args[1..len(args)-1]. Plain variables are treated as functions
+        # without arguments.
+
+        fn = args[0]
+
+        if fn in self.variables:
+            var = self.variables[fn]
+
+            if len(args) == 1:
+                # Plain variable
+                if var._n_expansions:
+                    self._parse_error("Preprocessor variable {} recursively "
+                                      "references itself".format(var.name))
+            elif var._n_expansions > 100:
+                # Allow functions to call themselves, but guess that functions
+                # that are overly recursive are stuck
+                self._parse_error("Preprocessor function {} seems stuck "
+                                  "in infinite recursion".format(var.name))
+
+            var._n_expansions += 1
+            res = self._expand_whole(self.variables[fn].value, args)
+            var._n_expansions -= 1
+            return res
+
+        if fn in self._functions:
+            # Built-in or user-defined function
+
+            py_fn, min_arg, max_arg = self._functions[fn]
+
+            if len(args) - 1 < min_arg or \
+               (max_arg is not None and len(args) - 1 > max_arg):
+
+                if min_arg == max_arg:
+                    expected_args = min_arg
+                elif max_arg is None:
+                    expected_args = "{} or more".format(min_arg)
+                else:
+                    expected_args = "{}-{}".format(min_arg, max_arg)
+
+                raise KconfigError("{}:{}: bad number of arguments in call "
+                                   "to {}, expected {}, got {}"
+                                   .format(self.filename, self.linenr, fn,
+                                           expected_args, len(args) - 1))
+
+            return py_fn(self, *args)
+
+        # Environment variables are tried last
+        if fn in os.environ:
+            self.env_vars.add(fn)
+            return os.environ[fn]
+
+        return ""
+
+    #
+    # Parsing
+    #
+
+    def _make_and(self, e1, e2):
+        # Constructs an AND (&&) expression. Performs trivial simplification.
+
+        if e1 is self.y:
+            return e2
+
+        if e2 is self.y:
+            return e1
+
+        if e1 is self.n or e2 is self.n:
+            return self.n
+
+        return (AND, e1, e2)
+
+    def _make_or(self, e1, e2):
+        # Constructs an OR (||) expression. Performs trivial simplification.
+
+        if e1 is self.n:
+            return e2
+
+        if e2 is self.n:
+            return e1
+
+        if e1 is self.y or e2 is self.y:
+            return self.y
+
+        return (OR, e1, e2)
+
+    def _parse_block(self, end_token, parent, prev):
+        # Parses a block, which is the contents of either a file or an if,
+        # menu, or choice statement.
+        #
+        # end_token:
+        #   The token that ends the block, e.g. _T_ENDIF ("endif") for ifs.
+        #   None for files.
+        #
+        # parent:
+        #   The parent menu node, corresponding to a menu, Choice, or 'if'.
+        #   'if's are flattened after parsing.
+        #
+        # prev:
+        #   The previous menu node. New nodes will be added after this one (by
+        #   modifying 'next' pointers).
+        #
+        #   'prev' is reused to parse a list of child menu nodes (for a menu or
+        #   Choice): After parsing the children, the 'next' pointer is assigned
+        #   to the 'list' pointer to "tilt up" the children above the node.
+        #
+        # Returns the final menu node in the block (or 'prev' if the block is
+        # empty). This allows chaining.
+
+        while self._next_line():
+            t0 = self._tokens[0]
+
+            if t0 is _T_CONFIG or t0 is _T_MENUCONFIG:
+                # The tokenizer allocates Symbol objects for us
+                sym = self._tokens[1]
+
+                if sym.__class__ is not Symbol or sym.is_constant:
+                    self._parse_error("missing or bad symbol name")
+
+                if self._tokens[2] is not None:
+                    self._trailing_tokens_error()
+
+                self.defined_syms.append(sym)
+
+                node = MenuNode()
+                node.kconfig = self
+                node.item = sym
+                node.is_menuconfig = (t0 is _T_MENUCONFIG)
+                node.prompt = node.help = node.list = None
+                node.parent = parent
+                node.filename = self.filename
+                node.linenr = self.linenr
+                node.include_path = self._include_path
+
+                sym.nodes.append(node)
+
+                self._parse_props(node)
+
+                if node.is_menuconfig and not node.prompt:
+                    self._warn("the menuconfig symbol {} has no prompt"
+                               .format(sym.name_and_loc))
+
+                # Equivalent to
+                #
+                #   prev.next = node
+                #   prev = node
+                #
+                # due to tricky Python semantics. The order matters.
+                prev.next = prev = node
+
+            elif t0 is None:
+                # Blank line
+                continue
+
+            elif t0 in _SOURCE_TOKENS:
+                pattern = self._expect_str_and_eol()
+
+                if t0 in _REL_SOURCE_TOKENS:
+                    # Relative source
+                    pattern = join(dirname(self.filename), pattern)
+
+                # - glob() doesn't support globbing relative to a directory, so
+                #   we need to prepend $srctree to 'pattern'. Use join()
+                #   instead of '+' so that an absolute path in 'pattern' is
+                #   preserved.
+                #
+                # - Sort the glob results to ensure a consistent ordering of
+                #   Kconfig symbols, which indirectly ensures a consistent
+                #   ordering in e.g. .config files
+                filenames = sorted(iglob(join(self._srctree_prefix, pattern)))
+
+                if not filenames and t0 in _OBL_SOURCE_TOKENS:
+                    raise KconfigError(
+                        "{}:{}: '{}' not found (in '{}'). Check that "
+                        "environment variables are set correctly (e.g. "
+                        "$srctree, which is {}). Also note that unset "
+                        "environment variables expand to the empty string."
+                        .format(self.filename, self.linenr, pattern,
+                                self._line.strip(),
+                                "set to '{}'".format(self.srctree)
+                                    if self.srctree else "unset or blank"))
+
+                for filename in filenames:
+                    self._enter_file(filename)
+                    prev = self._parse_block(None, parent, prev)
+                    self._leave_file()
+
+            elif t0 is end_token:
+                # Reached the end of the block. Terminate the final node and
+                # return it.
+
+                if self._tokens[1] is not None:
+                    self._trailing_tokens_error()
+
+                prev.next = None
+                return prev
+
+            elif t0 is _T_IF:
+                node = MenuNode()
+                node.item = node.prompt = None
+                node.parent = parent
+                node.dep = self._expect_expr_and_eol()
+
+                self._parse_block(_T_ENDIF, node, node)
+                node.list = node.next
+
+                prev.next = prev = node
+
+            elif t0 is _T_MENU:
+                node = MenuNode()
+                node.kconfig = self
+                node.item = t0  # _T_MENU == MENU
+                node.is_menuconfig = True
+                node.prompt = (self._expect_str_and_eol(), self.y)
+                node.visibility = self.y
+                node.parent = parent
+                node.filename = self.filename
+                node.linenr = self.linenr
+                node.include_path = self._include_path
+
+                self.menus.append(node)
+
+                self._parse_props(node)
+                self._parse_block(_T_ENDMENU, node, node)
+                node.list = node.next
+
+                prev.next = prev = node
+
+            elif t0 is _T_COMMENT:
+                node = MenuNode()
+                node.kconfig = self
+                node.item = t0  # _T_COMMENT == COMMENT
+                node.is_menuconfig = False
+                node.prompt = (self._expect_str_and_eol(), self.y)
+                node.list = None
+                node.parent = parent
+                node.filename = self.filename
+                node.linenr = self.linenr
+                node.include_path = self._include_path
+
+                self.comments.append(node)
+
+                self._parse_props(node)
+
+                prev.next = prev = node
+
+            elif t0 is _T_CHOICE:
+                if self._tokens[1] is None:
+                    choice = Choice()
+                    choice.direct_dep = self.n
+                else:
+                    # Named choice
+                    name = self._expect_str_and_eol()
+                    choice = self.named_choices.get(name)
+                    if not choice:
+                        choice = Choice()
+                        choice.name = name
+                        choice.direct_dep = self.n
+                        self.named_choices[name] = choice
+
+                self.choices.append(choice)
+
+                node = MenuNode()
+                node.kconfig = choice.kconfig = self
+                node.item = choice
+                node.is_menuconfig = True
+                node.prompt = node.help = None
+                node.parent = parent
+                node.filename = self.filename
+                node.linenr = self.linenr
+                node.include_path = self._include_path
+
+                choice.nodes.append(node)
+
+                self._parse_props(node)
+                self._parse_block(_T_ENDCHOICE, node, node)
+                node.list = node.next
+
+                prev.next = prev = node
+
+            elif t0 is _T_MAINMENU:
+                self.top_node.prompt = (self._expect_str_and_eol(), self.y)
+
+            else:
+                # A valid endchoice/endif/endmenu is caught by the 'end_token'
+                # check above
+                self._parse_error(
+                    "no corresponding 'choice'" if t0 is _T_ENDCHOICE else
+                    "no corresponding 'if'"     if t0 is _T_ENDIF else
+                    "no corresponding 'menu'"   if t0 is _T_ENDMENU else
+                    "unrecognized construct")
+
+        # End of file reached. Return the last node.
+
+        if end_token:
+            raise KconfigError(
+                "error: expected '{}' at end of '{}'"
+                .format("endchoice" if end_token is _T_ENDCHOICE else
+                        "endif"     if end_token is _T_ENDIF else
+                        "endmenu",
+                        self.filename))
+
+        return prev
+
+    def _parse_cond(self):
+        # Parses an optional 'if <expr>' construct and returns the parsed
+        # <expr>, or self.y if the next token is not _T_IF
+
+        expr = self._parse_expr(True) if self._check_token(_T_IF) else self.y
+
+        if self._tokens[self._tokens_i] is not None:
+            self._trailing_tokens_error()
+
+        return expr
+
+    def _parse_props(self, node):
+        # Parses and adds properties to the MenuNode 'node' (type, 'prompt',
+        # 'default's, etc.) Properties are later copied up to symbols and
+        # choices in a separate pass after parsing, in e.g.
+        # _add_props_to_sym().
+        #
+        # An older version of this code added properties directly to symbols
+        # and choices instead of to their menu nodes (and handled dependency
+        # propagation simultaneously), but that loses information on where a
+        # property is added when a symbol or choice is defined in multiple
+        # locations. Some Kconfig configuration systems rely heavily on such
+        # symbols, and better docs can be generated by keeping track of where
+        # properties are added.
+        #
+        # node:
+        #   The menu node we're parsing properties on
+
+        # Dependencies from 'depends on'. Will get propagated to the properties
+        # below.
+        node.dep = self.y
+
+        while self._next_line():
+            t0 = self._tokens[0]
+
+            if t0 in _TYPE_TOKENS:
+                # Relies on '_T_BOOL is BOOL', etc., to save a conversion
+                self._set_type(node.item, t0)
+                if self._tokens[1] is not None:
+                    self._parse_prompt(node)
+
+            elif t0 is _T_DEPENDS:
+                if not self._check_token(_T_ON):
+                    self._parse_error("expected 'on' after 'depends'")
+
+                node.dep = self._make_and(node.dep,
+                                          self._expect_expr_and_eol())
+
+            elif t0 is _T_HELP:
+                self._parse_help(node)
+
+            elif t0 is _T_SELECT:
+                if node.item.__class__ is not Symbol:
+                    self._parse_error("only symbols can select")
+
+                node.selects.append((self._expect_nonconst_sym(),
+                                     self._parse_cond()))
+
+            elif t0 is None:
+                # Blank line
+                continue
+
+            elif t0 is _T_DEFAULT:
+                node.defaults.append((self._parse_expr(False),
+                                      self._parse_cond()))
+
+            elif t0 in _DEF_TOKEN_TO_TYPE:
+                self._set_type(node.item, _DEF_TOKEN_TO_TYPE[t0])
+                node.defaults.append((self._parse_expr(False),
+                                      self._parse_cond()))
+
+            elif t0 is _T_PROMPT:
+                self._parse_prompt(node)
+
+            elif t0 is _T_RANGE:
+                node.ranges.append((self._expect_sym(), self._expect_sym(),
+                                    self._parse_cond()))
+
+            elif t0 is _T_IMPLY:
+                if node.item.__class__ is not Symbol:
+                    self._parse_error("only symbols can imply")
+
+                node.implies.append((self._expect_nonconst_sym(),
+                                     self._parse_cond()))
+
+            elif t0 is _T_VISIBLE:
+                if not self._check_token(_T_IF):
+                    self._parse_error("expected 'if' after 'visible'")
+
+                node.visibility = self._make_and(node.visibility,
+                                                 self._expect_expr_and_eol())
+
+            elif t0 is _T_OPTION:
+                if self._check_token(_T_ENV):
+                    if not self._check_token(_T_EQUAL):
+                        self._parse_error("expected '=' after 'env'")
+
+                    env_var = self._expect_str_and_eol()
+                    node.item.env_var = env_var
+
+                    if env_var in os.environ:
+                        node.defaults.append(
+                            (self._lookup_const_sym(os.environ[env_var]),
+                             self.y))
+                    else:
+                        self._warn("{1} has 'option env=\"{0}\"', "
+                                   "but the environment variable {0} is not "
+                                   "set".format(node.item.name, env_var),
+                                   self.filename, self.linenr)
+
+                    if env_var != node.item.name:
+                        self._warn("Kconfiglib expands environment variables "
+                                   "in strings directly, meaning you do not "
+                                   "need 'option env=...' \"bounce\" symbols. "
+                                   "For compatibility with the C tools, "
+                                   "rename {} to {} (so that the symbol name "
+                                   "matches the environment variable name)."
+                                   .format(node.item.name, env_var),
+                                   self.filename, self.linenr)
+
+                elif self._check_token(_T_DEFCONFIG_LIST):
+                    if not self.defconfig_list:
+                        self.defconfig_list = node.item
+                    else:
+                        self._warn("'option defconfig_list' set on multiple "
+                                   "symbols ({0} and {1}). Only {0} will be "
+                                   "used.".format(self.defconfig_list.name,
+                                                  node.item.name),
+                                   self.filename, self.linenr)
+
+                elif self._check_token(_T_MODULES):
+                    # To reduce warning spam, only warn if 'option modules' is
+                    # set on some symbol that isn't MODULES, which should be
+                    # safe. I haven't run into any projects that make use
+                    # modules besides the kernel yet, and there it's likely to
+                    # keep being called "MODULES".
+                    if node.item is not self.modules:
+                        self._warn("the 'modules' option is not supported. "
+                                   "Let me know if this is a problem for you, "
+                                   "as it wouldn't be that hard to implement. "
+                                   "Note that modules are supported -- "
+                                   "Kconfiglib just assumes the symbol name "
+                                   "MODULES, like older versions of the C "
+                                   "implementation did when 'option modules' "
+                                   "wasn't used.",
+                                   self.filename, self.linenr)
+
+                elif self._check_token(_T_ALLNOCONFIG_Y):
+                    if node.item.__class__ is not Symbol:
+                        self._parse_error("the 'allnoconfig_y' option is only "
+                                          "valid for symbols")
+
+                    node.item.is_allnoconfig_y = True
+
+                else:
+                    self._parse_error("unrecognized option")
+
+            elif t0 is _T_OPTIONAL:
+                if node.item.__class__ is not Choice:
+                    self._parse_error('"optional" is only valid for choices')
+
+                node.item.is_optional = True
+
+            else:
+                # Reuse the tokens for the non-property line later
+                self._reuse_tokens = True
+                return
+
+    def _set_type(self, sc, new_type):
+        # Sets the type of 'sc' (symbol or choice) to 'new_type'
+
+        # UNKNOWN is falsy
+        if sc.orig_type and sc.orig_type is not new_type:
+            self._warn("{} defined with multiple types, {} will be used"
+                       .format(sc.name_and_loc, TYPE_TO_STR[new_type]))
+
+        sc.orig_type = new_type
+
+    def _parse_prompt(self, node):
+        # 'prompt' properties override each other within a single definition of
+        # a symbol, but additional prompts can be added by defining the symbol
+        # multiple times
+
+        if node.prompt:
+            self._warn(node.item.name_and_loc +
+                       " defined with multiple prompts in single location")
+
+        prompt = self._tokens[1]
+        self._tokens_i = 2
+
+        if prompt.__class__ is not str:
+            self._parse_error("expected prompt string")
+
+        if prompt != prompt.strip():
+            self._warn(node.item.name_and_loc +
+                       " has leading or trailing whitespace in its prompt")
+
+            # This avoid issues for e.g. reStructuredText documentation, where
+            # '*prompt *' is invalid
+            prompt = prompt.strip()
+
+        node.prompt = (prompt, self._parse_cond())
+
+    def _parse_help(self, node):
+        if node.help is not None:
+            self._warn(node.item.name_and_loc + " defined with more than "
+                       "one help text -- only the last one will be used")
+
+        # Micro-optimization. This code is pretty hot.
+        readline = self._readline
+
+        # Find first non-blank (not all-space) line and get its
+        # indentation
+
+        while 1:
+            line = readline()
+            self.linenr += 1
+            if not line:
+                self._empty_help(node, line)
+                return
+            if not line.isspace():
+                break
+
+        len_ = len  # Micro-optimization
+
+        # Use a separate 'expline' variable here and below to avoid stomping on
+        # any tabs people might've put deliberately into the first line after
+        # the help text
+        expline = line.expandtabs()
+        indent = len_(expline) - len_(expline.lstrip())
+        if not indent:
+            self._empty_help(node, line)
+            return
+
+        # The help text goes on till the first non-blank line with less indent
+        # than the first line
+
+        # Add the first line
+        lines = [expline[indent:]]
+        add_line = lines.append  # Micro-optimization
+
+        while 1:
+            line = readline()
+            if line.isspace():
+                # No need to preserve the exact whitespace in these
+                add_line("\n")
+            elif not line:
+                # End of file
+                break
+            else:
+                expline = line.expandtabs()
+                if len_(expline) - len_(expline.lstrip()) < indent:
+                    break
+                add_line(expline[indent:])
+
+        self.linenr += len_(lines)
+        node.help = "".join(lines).rstrip()
+        if line:
+            self._line_after_help(line)
+
+    def _empty_help(self, node, line):
+        self._warn(node.item.name_and_loc +
+                   " has 'help' but empty help text")
+        node.help = ""
+        if line:
+            self._line_after_help(line)
+
+    def _parse_expr(self, transform_m):
+        # Parses an expression from the tokens in Kconfig._tokens using a
+        # simple top-down approach. See the module docstring for the expression
+        # format.
+        #
+        # transform_m:
+        #   True if m should be rewritten to m && MODULES. See the
+        #   Kconfig.eval_string() documentation.
+
+        # Grammar:
+        #
+        #   expr:     and_expr ['||' expr]
+        #   and_expr: factor ['&&' and_expr]
+        #   factor:   <symbol> ['='/'!='/'<'/... <symbol>]
+        #             '!' factor
+        #             '(' expr ')'
+        #
+        # It helps to think of the 'expr: and_expr' case as a single-operand OR
+        # (no ||), and of the 'and_expr: factor' case as a single-operand AND
+        # (no &&). Parsing code is always a bit tricky.
+
+        # Mind dump: parse_factor() and two nested loops for OR and AND would
+        # work as well. The straightforward implementation there gives a
+        # (op, (op, (op, A, B), C), D) parse for A op B op C op D. Representing
+        # expressions as (op, [list of operands]) instead goes nicely with that
+        # version, but is wasteful for short expressions and complicates
+        # expression evaluation and other code that works on expressions (more
+        # complicated code likely offsets any performance gain from less
+        # recursion too). If we also try to optimize the list representation by
+        # merging lists when possible (e.g. when ANDing two AND expressions),
+        # we end up allocating a ton of lists instead of reusing expressions,
+        # which is bad.
+
+        and_expr = self._parse_and_expr(transform_m)
+
+        # Return 'and_expr' directly if we have a "single-operand" OR.
+        # Otherwise, parse the expression on the right and make an OR node.
+        # This turns A || B || C || D into (OR, A, (OR, B, (OR, C, D))).
+        return and_expr if not self._check_token(_T_OR) else \
+            (OR, and_expr, self._parse_expr(transform_m))
+
+    def _parse_and_expr(self, transform_m):
+        factor = self._parse_factor(transform_m)
+
+        # Return 'factor' directly if we have a "single-operand" AND.
+        # Otherwise, parse the right operand and make an AND node. This turns
+        # A && B && C && D into (AND, A, (AND, B, (AND, C, D))).
+        return factor if not self._check_token(_T_AND) else \
+            (AND, factor, self._parse_and_expr(transform_m))
+
+    def _parse_factor(self, transform_m):
+        token = self._tokens[self._tokens_i]
+        self._tokens_i += 1
+
+        if token.__class__ is Symbol:
+            # Plain symbol or relation
+
+            if self._tokens[self._tokens_i] not in _RELATIONS:
+                # Plain symbol
+
+                # For conditional expressions ('depends on <expr>',
+                # '... if <expr>', etc.), m is rewritten to m && MODULES.
+                if transform_m and token is self.m:
+                    return (AND, self.m, self.modules)
+
+                return token
+
+            # Relation
+            #
+            # _T_EQUAL, _T_UNEQUAL, etc., deliberately have the same values as
+            # EQUAL, UNEQUAL, etc., so we can just use the token directly
+            self._tokens_i += 1
+            return (self._tokens[self._tokens_i - 1], token,
+                    self._expect_sym())
+
+        if token is _T_NOT:
+            # token == _T_NOT == NOT
+            return (token, self._parse_factor(transform_m))
+
+        if token is _T_OPEN_PAREN:
+            expr_parse = self._parse_expr(transform_m)
+            if self._check_token(_T_CLOSE_PAREN):
+                return expr_parse
+
+        self._parse_error("malformed expression")
+
+    #
+    # Caching and invalidation
+    #
+
+    def _build_dep(self):
+        # Populates the Symbol/Choice._dependents sets, which contain all other
+        # items (symbols and choices) that immediately depend on the item in
+        # the sense that changing the value of the item might affect the value
+        # of the dependent items. This is used for caching/invalidation.
+        #
+        # The calculated sets might be larger than necessary as we don't do any
+        # complex analysis of the expressions.
+
+        depend_on = _depend_on  # Micro-optimization
+
+        # Only calculate _dependents for defined symbols. Constant and
+        # undefined symbols could theoretically be selected/implied, but it
+        # wouldn't change their value, so it's not a true dependency.
+        for sym in self.unique_defined_syms:
+            # Symbols depend on the following:
+
+            # The prompt conditions
+            for node in sym.nodes:
+                if node.prompt:
+                    depend_on(sym, node.prompt[1])
+
+            # The default values and their conditions
+            for value, cond in sym.defaults:
+                depend_on(sym, value)
+                depend_on(sym, cond)
+
+            # The reverse and weak reverse dependencies
+            depend_on(sym, sym.rev_dep)
+            depend_on(sym, sym.weak_rev_dep)
+
+            # The ranges along with their conditions
+            for low, high, cond in sym.ranges:
+                depend_on(sym, low)
+                depend_on(sym, high)
+                depend_on(sym, cond)
+
+            # The direct dependencies. This is usually redundant, as the direct
+            # dependencies get propagated to properties, but it's needed to get
+            # invalidation solid for 'imply', which only checks the direct
+            # dependencies (even if there are no properties to propagate it
+            # to).
+            depend_on(sym, sym.direct_dep)
+
+            # In addition to the above, choice symbols depend on the choice
+            # they're in, but that's handled automatically since the Choice is
+            # propagated to the conditions of the properties before
+            # _build_dep() runs.
+
+        for choice in self.unique_choices:
+            # Choices depend on the following:
+
+            # The prompt conditions
+            for node in choice.nodes:
+                if node.prompt:
+                    depend_on(choice, node.prompt[1])
+
+            # The default symbol conditions
+            for _, cond in choice.defaults:
+                depend_on(choice, cond)
+
+    def _add_choice_deps(self):
+        # Choices also depend on the choice symbols themselves, because the
+        # y-mode selection of the choice might change if a choice symbol's
+        # visibility changes.
+        #
+        # We add these dependencies separately after dependency loop detection.
+        # The invalidation algorithm can handle the resulting
+        # <choice symbol> <-> <choice> dependency loops, but they make loop
+        # detection awkward.
+
+        for choice in self.unique_choices:
+            for sym in choice.syms:
+                sym._dependents.add(choice)
+
+    def _invalidate_all(self):
+        # Undefined symbols never change value and don't need to be
+        # invalidated, so we can just iterate over defined symbols.
+        # Invalidating constant symbols would break things horribly.
+        for sym in self.unique_defined_syms:
+            sym._invalidate()
+
+        for choice in self.unique_choices:
+            choice._invalidate()
+
+    #
+    # Post-parsing menu tree processing, including dependency propagation and
+    # implicit submenu creation
+    #
+
+    def _finalize_node(self, node, visible_if):
+        # Finalizes a menu node and its children:
+        #
+        #  - Copies properties from menu nodes up to their contained
+        #    symbols/choices
+        #
+        #  - Propagates dependencies from parent to child nodes
+        #
+        #  - Creates implicit menus (see kconfig-language.txt)
+        #
+        #  - Removes 'if' nodes
+        #
+        #  - Sets 'choice' types and registers choice symbols
+        #
+        # menu_finalize() in the C implementation is similar.
+        #
+        # node:
+        #   The menu node to finalize. This node and its children will have
+        #   been finalized when the function returns, and any implicit menus
+        #   will have been created.
+        #
+        # visible_if:
+        #   Dependencies from 'visible if' on parent menus. These are added to
+        #   the prompts of symbols and choices.
+
+        if node.item.__class__ is Symbol:
+            # Copy defaults, ranges, selects, and implies to the Symbol
+            self._add_props_to_sym(node)
+
+            # Find any items that should go in an implicit menu rooted at the
+            # symbol
+            cur = node
+            while cur.next and _auto_menu_dep(node, cur.next):
+                # This makes implicit submenu creation work recursively, with
+                # implicit menus inside implicit menus
+                self._finalize_node(cur.next, visible_if)
+                cur = cur.next
+                cur.parent = node
+
+            if cur is not node:
+                # Found symbols that should go in an implicit submenu. Tilt
+                # them up above us.
+                node.list = node.next
+                node.next = cur.next
+                cur.next = None
+
+        elif node.list:
+            # The menu node is a choice, menu, or if. Finalize each child node.
+
+            if node.item is MENU:
+                visible_if = self._make_and(visible_if, node.visibility)
+
+            # Propagate the menu node's dependencies to each child menu node.
+            #
+            # This needs to go before the recursive _finalize_node() call so
+            # that implicit submenu creation can look ahead at dependencies.
+            self._propagate_deps(node, visible_if)
+
+            # Finalize the children
+            cur = node.list
+            while cur:
+                self._finalize_node(cur, visible_if)
+                cur = cur.next
+
+        if node.list:
+            # node's children have been individually finalized. Do final steps
+            # to finalize this "level" in the menu tree.
+            _flatten(node.list)
+            _remove_ifs(node)
+
+        # Empty choices (node.list None) are possible, so this needs to go
+        # outside
+        if node.item.__class__ is Choice:
+            # Add the node's non-node-specific properties to the choice, like
+            # _add_props_to_sym() does
+            choice = node.item
+            choice.direct_dep = self._make_or(choice.direct_dep, node.dep)
+            choice.defaults += node.defaults
+
+            _finalize_choice(node)
+
+    def _propagate_deps(self, node, visible_if):
+        # Propagates 'node's dependencies to its child menu nodes
+
+        # If the parent node holds a Choice, we use the Choice itself as the
+        # parent dependency. This makes sense as the value (mode) of the choice
+        # limits the visibility of the contained choice symbols. The C
+        # implementation works the same way.
+        #
+        # Due to the similar interface, Choice works as a drop-in replacement
+        # for Symbol here.
+        basedep = node.item if node.item.__class__ is Choice else node.dep
+
+        cur = node.list
+        while cur:
+            dep = cur.dep = self._make_and(cur.dep, basedep)
+
+            if cur.item.__class__ in _SYMBOL_CHOICE:
+                # Propagate 'visible if' and dependencies to the prompt
+                if cur.prompt:
+                    cur.prompt = (cur.prompt[0],
+                                  self._make_and(
+                                      cur.prompt[1],
+                                      self._make_and(visible_if, dep)))
+
+                # Propagate dependencies to defaults
+                if cur.defaults:
+                    cur.defaults = [(default, self._make_and(cond, dep))
+                                    for default, cond in cur.defaults]
+
+                # Propagate dependencies to ranges
+                if cur.ranges:
+                    cur.ranges = [(low, high, self._make_and(cond, dep))
+                                  for low, high, cond in cur.ranges]
+
+                # Propagate dependencies to selects
+                if cur.selects:
+                    cur.selects = [(target, self._make_and(cond, dep))
+                                   for target, cond in cur.selects]
+
+                # Propagate dependencies to implies
+                if cur.implies:
+                    cur.implies = [(target, self._make_and(cond, dep))
+                                   for target, cond in cur.implies]
+
+            elif cur.prompt:  # Not a symbol/choice
+                # Propagate dependencies to the prompt. 'visible if' is only
+                # propagated to symbols/choices.
+                cur.prompt = (cur.prompt[0],
+                              self._make_and(cur.prompt[1], dep))
+
+            cur = cur.next
+
+    def _add_props_to_sym(self, node):
+        # Copies properties from the menu node 'node' up to its contained
+        # symbol, and adds (weak) reverse dependencies to selected/implied
+        # symbols.
+        #
+        # This can't be rolled into _propagate_deps(), because that function
+        # traverses the menu tree roughly breadth-first, meaning properties on
+        # symbols defined in multiple locations could end up in the wrong
+        # order.
+
+        sym = node.item
+
+        # See the Symbol class docstring
+        sym.direct_dep = self._make_or(sym.direct_dep, node.dep)
+
+        sym.defaults += node.defaults
+        sym.ranges += node.ranges
+        sym.selects += node.selects
+        sym.implies += node.implies
+
+        # Modify the reverse dependencies of the selected symbol
+        for target, cond in node.selects:
+            target.rev_dep = self._make_or(
+                target.rev_dep,
+                self._make_and(sym, cond))
+
+        # Modify the weak reverse dependencies of the implied
+        # symbol
+        for target, cond in node.implies:
+            target.weak_rev_dep = self._make_or(
+                target.weak_rev_dep,
+                self._make_and(sym, cond))
+
+    #
+    # Misc.
+    #
+
+    def _check_sym_sanity(self):
+        # Checks various symbol properties that are handiest to check after
+        # parsing. Only generates errors and warnings.
+
+        def num_ok(sym, type_):
+            # Returns True if the (possibly constant) symbol 'sym' is valid as a value
+            # for a symbol of type type_ (INT or HEX)
+
+            # 'not sym.nodes' implies a constant or undefined symbol, e.g. a plain
+            # "123"
+            if not sym.nodes:
+                return _is_base_n(sym.name, _TYPE_TO_BASE[type_])
+
+            return sym.orig_type is type_
+
+        for sym in self.unique_defined_syms:
+            if sym.orig_type in _BOOL_TRISTATE:
+                # A helper function could be factored out here, but keep it
+                # speedy/straightforward
+
+                for target_sym, _ in sym.selects:
+                    if target_sym.orig_type not in _BOOL_TRISTATE_UNKNOWN:
+                        self._warn("{} selects the {} symbol {}, which is not "
+                                   "bool or tristate"
+                                   .format(sym.name_and_loc,
+                                           TYPE_TO_STR[target_sym.orig_type],
+                                           target_sym.name_and_loc))
+
+                for target_sym, _ in sym.implies:
+                    if target_sym.orig_type not in _BOOL_TRISTATE_UNKNOWN:
+                        self._warn("{} implies the {} symbol {}, which is not "
+                                   "bool or tristate"
+                                   .format(sym.name_and_loc,
+                                           TYPE_TO_STR[target_sym.orig_type],
+                                           target_sym.name_and_loc))
+
+            elif sym.orig_type:  # STRING/INT/HEX
+                for default, _ in sym.defaults:
+                    if default.__class__ is not Symbol:
+                        raise KconfigError(
+                            "the {} symbol {} has a malformed default {} -- "
+                            "expected a single symbol"
+                            .format(TYPE_TO_STR[sym.orig_type],
+                                    sym.name_and_loc, expr_str(default)))
+
+                    if sym.orig_type is STRING:
+                        if not default.is_constant and not default.nodes and \
+                           not default.name.isupper():
+                            # 'default foo' on a string symbol could be either a symbol
+                            # reference or someone leaving out the quotes. Guess that
+                            # the quotes were left out if 'foo' isn't all-uppercase
+                            # (and no symbol named 'foo' exists).
+                            self._warn("style: quotes recommended around "
+                                       "default value for string symbol "
+                                       + sym.name_and_loc)
+
+                    elif not num_ok(default, sym.orig_type):  # INT/HEX
+                        self._warn("the {0} symbol {1} has a non-{0} default {2}"
+                                   .format(TYPE_TO_STR[sym.orig_type],
+                                           sym.name_and_loc,
+                                           default.name_and_loc))
+
+                if sym.selects or sym.implies:
+                    self._warn("the {} symbol {} has selects or implies"
+                               .format(TYPE_TO_STR[sym.orig_type],
+                                       sym.name_and_loc))
+
+            else:  # UNKNOWN
+                self._warn("{} defined without a type"
+                           .format(sym.name_and_loc))
+
+
+            if sym.ranges:
+                if sym.orig_type not in _INT_HEX:
+                    self._warn(
+                        "the {} symbol {} has ranges, but is not int or hex"
+                        .format(TYPE_TO_STR[sym.orig_type],
+                                sym.name_and_loc))
+                else:
+                    for low, high, _ in sym.ranges:
+                        if not num_ok(low, sym.orig_type) or \
+                           not num_ok(high, sym.orig_type):
+
+                            self._warn("the {0} symbol {1} has a non-{0} "
+                                       "range [{2}, {3}]"
+                                       .format(TYPE_TO_STR[sym.orig_type],
+                                               sym.name_and_loc,
+                                               low.name_and_loc,
+                                               high.name_and_loc))
+
+    def _check_choice_sanity(self):
+        # Checks various choice properties that are handiest to check after
+        # parsing. Only generates errors and warnings.
+
+        def warn_select_imply(sym, expr, expr_type):
+            msg = "the choice symbol {} is {} by the following symbols, but " \
+                  "select/imply has no effect on choice symbols" \
+                  .format(sym.name_and_loc, expr_type)
+
+            # si = select/imply
+            for si in split_expr(expr, OR):
+                msg += "\n - " + split_expr(si, AND)[0].name_and_loc
+
+            self._warn(msg)
+
+        for choice in self.unique_choices:
+            if choice.orig_type not in _BOOL_TRISTATE:
+                self._warn("{} defined with type {}"
+                           .format(choice.name_and_loc,
+                                   TYPE_TO_STR[choice.orig_type]))
+
+            for node in choice.nodes:
+                if node.prompt:
+                    break
+            else:
+                self._warn(choice.name_and_loc + " defined without a prompt")
+
+            for default, _ in choice.defaults:
+                if default.__class__ is not Symbol:
+                    raise KconfigError(
+                        "{} has a malformed default {}"
+                        .format(choice.name_and_loc, expr_str(default)))
+
+                if default.choice is not choice:
+                    self._warn("the default selection {} of {} is not "
+                               "contained in the choice"
+                               .format(default.name_and_loc,
+                                       choice.name_and_loc))
+
+            for sym in choice.syms:
+                if sym.defaults:
+                    self._warn("default on the choice symbol {} will have "
+                               "no effect, as defaults do not affect choice "
+                               "symbols".format(sym.name_and_loc))
+
+                if sym.rev_dep is not sym.kconfig.n:
+                    warn_select_imply(sym, sym.rev_dep, "selected")
+
+                if sym.weak_rev_dep is not sym.kconfig.n:
+                    warn_select_imply(sym, sym.weak_rev_dep, "implied")
+
+                for node in sym.nodes:
+                    if node.parent.item is choice:
+                        if not node.prompt:
+                            self._warn("the choice symbol {} has no prompt"
+                                       .format(sym.name_and_loc))
+
+                    elif node.prompt:
+                        self._warn("the choice symbol {} is defined with a "
+                                   "prompt outside the choice"
+                                   .format(sym.name_and_loc))
+
+    def _parse_error(self, msg):
+        raise KconfigError("{}error: couldn't parse '{}': {}".format(
+            "" if self.filename is None else
+                "{}:{}: ".format(self.filename, self.linenr),
+            self._line.strip(), msg))
+
+    def _trailing_tokens_error(self):
+        self._parse_error("extra tokens at end of line")
+
+    def _open(self, filename, mode):
+        # open() wrapper:
+        #
+        # - Enable universal newlines mode on Python 2 to ease
+        #   interoperability between Linux and Windows. It's already the
+        #   default on Python 3.
+        #
+        #   The "U" flag would currently work for both Python 2 and 3, but it's
+        #   deprecated on Python 3, so play it future-safe.
+        #
+        #   io.open() defaults to universal newlines on Python 2 (and is an
+        #   alias for open() on Python 3), but it returns 'unicode' strings and
+        #   slows things down:
+        #
+        #     Parsing x86 Kconfigs on Python 2
+        #
+        #     with open(..., "rU"):
+        #
+        #       real  0m0.930s
+        #       user  0m0.905s
+        #       sys   0m0.025s
+        #
+        #     with io.open():
+        #
+        #       real  0m1.069s
+        #       user  0m1.040s
+        #       sys   0m0.029s
+        #
+        #   There's no appreciable performance difference between "r" and
+        #   "rU" for parsing performance on Python 2.
+        #
+        # - For Python 3, force the encoding. Forcing the encoding on Python 2
+        #   turns strings into Unicode strings, which gets messy. Python 2
+        #   doesn't decode regular strings anyway.
+        return open(filename, "rU" if mode == "r" else mode) if _IS_PY2 else \
+               open(filename, mode, encoding=self._encoding)
+
+    def _check_undef_syms(self):
+        # Prints warnings for all references to undefined symbols within the
+        # Kconfig files
+
+        def is_num(s):
+            # Returns True if the string 's' looks like a number.
+            #
+            # Internally, all operands in Kconfig are symbols, only undefined symbols
+            # (which numbers usually are) get their name as their value.
+            #
+            # Only hex numbers that start with 0x/0X are classified as numbers.
+            # Otherwise, symbols whose names happen to contain only the letters A-F
+            # would trigger false positives.
+
+            try:
+                int(s)
+            except ValueError:
+                if not s.startswith(("0x", "0X")):
+                    return False
+
+                try:
+                    int(s, 16)
+                except ValueError:
+                    return False
+
+            return True
+
+        for sym in (self.syms.viewvalues if _IS_PY2 else self.syms.values)():
+            # - sym.nodes empty means the symbol is undefined (has no
+            #   definition locations)
+            #
+            # - Due to Kconfig internals, numbers show up as undefined Kconfig
+            #   symbols, but shouldn't be flagged
+            #
+            # - The MODULES symbol always exists
+            if not sym.nodes and not is_num(sym.name) and \
+               sym.name != "MODULES":
+
+                msg = "undefined symbol {}:".format(sym.name)
+                for node in self.node_iter():
+                    if sym in node.referenced:
+                        msg += "\n\n- Referenced at {}:{}:\n\n{}" \
+                               .format(node.filename, node.linenr, node)
+                self._warn(msg)
+
+    def _warn(self, msg, filename=None, linenr=None):
+        # For printing general warnings
+
+        if not self.warn:
+            return
+
+        msg = "warning: " + msg
+        if filename is not None:
+            msg = "{}:{}: {}".format(filename, linenr, msg)
+
+        self.warnings.append(msg)
+        if self.warn_to_stderr:
+            sys.stderr.write(msg + "\n")
+
+
+class Symbol(object):
+    """
+    Represents a configuration symbol:
+
+      (menu)config FOO
+          ...
+
+    The following attributes are available. They should be viewed as read-only,
+    and some are implemented through @property magic (but are still efficient
+    to access due to internal caching).
+
+    Note: Prompts, help texts, and locations are stored in the Symbol's
+    MenuNode(s) rather than in the Symbol itself. Check the MenuNode class and
+    the Symbol.nodes attribute. This organization matches the C tools.
+
+    name:
+      The name of the symbol, e.g. "FOO" for 'config FOO'.
+
+    type:
+      The type of the symbol. One of BOOL, TRISTATE, STRING, INT, HEX, UNKNOWN.
+      UNKNOWN is for undefined symbols, (non-special) constant symbols, and
+      symbols defined without a type.
+
+      When running without modules (MODULES having the value n), TRISTATE
+      symbols magically change type to BOOL. This also happens for symbols
+      within choices in "y" mode. This matches the C tools, and makes sense for
+      menuconfig-like functionality.
+
+    orig_type:
+      The type as given in the Kconfig file, without any magic applied. Used
+      when printing the symbol.
+
+    tri_value:
+      The tristate value of the symbol as an integer. One of 0, 1, 2,
+      representing n, m, y. Always 0 (n) for non-bool/tristate symbols.
+
+      This is the symbol value that's used outside of relation expressions
+      (A, !A, A && B, A || B).
+
+    str_value:
+      The value of the symbol as a string. Gives the value for string/int/hex
+      symbols. For bool/tristate symbols, gives "n", "m", or "y".
+
+      This is the symbol value that's used in relational expressions
+      (A = B, A != B, etc.)
+
+      Gotcha: For int/hex symbols, the exact format of the value is often
+      preserved (e.g. when writing a .config file), hence why you can't get it
+      directly as an int. Do int(int_sym.str_value) or
+      int(hex_sym.str_value, 16) to get the integer value.
+
+    user_value:
+      The user value of the symbol. None if no user value has been assigned
+      (via Kconfig.load_config() or Symbol.set_value()).
+
+      Holds 0, 1, or 2 for bool/tristate symbols, and a string for the other
+      symbol types.
+
+      WARNING: Do not assign directly to this. It will break things. Use
+      Symbol.set_value().
+
+    assignable:
+      A tuple containing the tristate user values that can currently be
+      assigned to the symbol (that would be respected), ordered from lowest (0,
+      representing n) to highest (2, representing y). This corresponds to the
+      selections available in the menuconfig interface. The set of assignable
+      values is calculated from the symbol's visibility and selects/implies.
+
+      Returns the empty set for non-bool/tristate symbols and for symbols with
+      visibility n. The other possible values are (0, 2), (0, 1, 2), (1, 2),
+      (1,), and (2,). A (1,) or (2,) result means the symbol is visible but
+      "locked" to m or y through a select, perhaps in combination with the
+      visibility. menuconfig represents this as -M- and -*-, respectively.
+
+      For string/hex/int symbols, check if Symbol.visibility is non-0 (non-n)
+      instead to determine if the value can be changed.
+
+      Some handy 'assignable' idioms:
+
+        # Is 'sym' an assignable (visible) bool/tristate symbol?
+        if sym.assignable:
+            # What's the highest value it can be assigned? [-1] in Python
+            # gives the last element.
+            sym_high = sym.assignable[-1]
+
+            # The lowest?
+            sym_low = sym.assignable[0]
+
+            # Can the symbol be set to at least m?
+            if sym.assignable[-1] >= 1:
+                ...
+
+        # Can the symbol be set to m?
+        if 1 in sym.assignable:
+            ...
+
+    visibility:
+      The visibility of the symbol. One of 0, 1, 2, representing n, m, y. See
+      the module documentation for an overview of symbol values and visibility.
+
+    config_string:
+      The .config assignment string that would get written out for the symbol
+      by Kconfig.write_config(). Returns the empty string if no .config
+      assignment would get written out.
+
+      In general, visible symbols, symbols with (active) defaults, and selected
+      symbols get written out. This includes all non-n-valued bool/tristate
+      symbols, and all visible string/int/hex symbols.
+
+      Symbols with the (no longer needed) 'option env=...' option generate no
+      configuration output, and neither does the special
+      'option defconfig_list' symbol.
+
+      Tip: This field is useful when generating custom configuration output,
+      even for non-.config-like formats. To write just the symbols that would
+      get written out to .config files, do this:
+
+        if sym.config_string:
+            *Write symbol, e.g. by looking sym.str_value*
+
+      This is a superset of the symbols written out by write_autoconf().
+      That function skips all n-valued symbols.
+
+      There usually won't be any great harm in just writing all symbols either,
+      though you might get some special symbols and possibly some "redundant"
+      n-valued symbol entries in there.
+
+    name_and_loc:
+      Holds a string like
+
+        "MY_SYMBOL (defined at foo/Kconfig:12, bar/Kconfig:14)"
+
+      , giving the name of the symbol and its definition location(s).
+
+      If the symbol is undefined, the location is given as "(undefined)".
+
+    nodes:
+      A list of MenuNodes for this symbol. Will contain a single MenuNode for
+      most symbols. Undefined and constant symbols have an empty nodes list.
+      Symbols defined in multiple locations get one node for each location.
+
+    choice:
+      Holds the parent Choice for choice symbols, and None for non-choice
+      symbols. Doubles as a flag for whether a symbol is a choice symbol.
+
+    defaults:
+      List of (default, cond) tuples for the symbol's 'default' properties. For
+      example, 'default A && B if C || D' is represented as
+      ((AND, A, B), (OR, C, D)). If no condition was given, 'cond' is
+      self.kconfig.y.
+
+      Note that 'depends on' and parent dependencies are propagated to
+      'default' conditions.
+
+    selects:
+      List of (symbol, cond) tuples for the symbol's 'select' properties. For
+      example, 'select A if B && C' is represented as (A, (AND, B, C)). If no
+      condition was given, 'cond' is self.kconfig.y.
+
+      Note that 'depends on' and parent dependencies are propagated to 'select'
+      conditions.
+
+    implies:
+      Like 'selects', for imply.
+
+    ranges:
+      List of (low, high, cond) tuples for the symbol's 'range' properties. For
+      example, 'range 1 2 if A' is represented as (1, 2, A). If there is no
+      condition, 'cond' is self.kconfig.y.
+
+      Note that 'depends on' and parent dependencies are propagated to 'range'
+      conditions.
+
+      Gotcha: 1 and 2 above will be represented as (undefined) Symbols rather
+      than plain integers. Undefined symbols get their name as their string
+      value, so this works out. The C tools work the same way.
+
+    orig_defaults:
+    orig_selects:
+    orig_implies:
+    orig_ranges:
+      See the corresponding attributes on the MenuNode class.
+
+    rev_dep:
+      Reverse dependency expression from other symbols selecting this symbol.
+      Multiple selections get ORed together. A condition on a select is ANDed
+      with the selecting symbol.
+
+      For example, if A has 'select FOO' and B has 'select FOO if C', then
+      FOO's rev_dep will be (OR, A, (AND, B, C)).
+
+    weak_rev_dep:
+      Like rev_dep, for imply.
+
+    direct_dep:
+      The direct ('depends on') dependencies for the symbol, or self.kconfig.y
+      if there are no direct dependencies.
+
+      This attribute includes any dependencies from surrounding menus and ifs.
+      Those get propagated to the direct dependencies, and the resulting direct
+      dependencies in turn get propagated to the conditions of all properties.
+
+      If the symbol is defined in multiple locations, the dependencies from the
+      different locations get ORed together.
+
+    referenced:
+      A set() with all symbols and choices referenced in the properties and
+      property conditions of the symbol.
+
+      Also includes dependencies from surrounding menus and ifs, because those
+      get propagated to the symbol (see the 'Intro to symbol values' section in
+      the module docstring).
+
+      Choices appear in the dependencies of choice symbols.
+
+      For the following definitions, only B and not C appears in A's
+      'referenced'. To get transitive references, you'll have to recursively
+      expand 'references' until no new items appear.
+
+        config A
+                bool
+                depends on B
+
+        config B
+                bool
+                depends on C
+
+        config C
+                bool
+
+      See the Symbol.direct_dep attribute if you're only interested in the
+      direct dependencies of the symbol (its 'depends on'). You can extract the
+      symbols in it with the global expr_items() function.
+
+    env_var:
+      If the Symbol has an 'option env="FOO"' option, this contains the name
+      ("FOO") of the environment variable. None for symbols without no
+      'option env'.
+
+      'option env="FOO"' acts like a 'default' property whose value is the
+      value of $FOO.
+
+      Symbols with 'option env' are never written out to .config files, even if
+      they are visible. env_var corresponds to a flag called SYMBOL_AUTO in the
+      C implementation.
+
+    is_allnoconfig_y:
+      True if the symbol has 'option allnoconfig_y' set on it. This has no
+      effect internally (except when printing symbols), but can be checked by
+      scripts.
+
+    is_constant:
+      True if the symbol is a constant (quoted) symbol.
+
+    kconfig:
+      The Kconfig instance this symbol is from.
+    """
+    __slots__ = (
+        "_cached_assignable",
+        "_cached_str_val",
+        "_cached_tri_val",
+        "_cached_vis",
+        "_dependents",
+        "_old_val",
+        "_visited",
+        "_was_set",
+        "_write_to_conf",
+        "choice",
+        "defaults",
+        "direct_dep",
+        "env_var",
+        "implies",
+        "is_allnoconfig_y",
+        "is_constant",
+        "kconfig",
+        "name",
+        "nodes",
+        "orig_type",
+        "ranges",
+        "rev_dep",
+        "selects",
+        "user_value",
+        "weak_rev_dep",
+    )
+
+    #
+    # Public interface
+    #
+
+    @property
+    def type(self):
+        """
+        See the class documentation.
+        """
+        if self.orig_type is TRISTATE and \
+           (self.choice and self.choice.tri_value == 2 or
+            not self.kconfig.modules.tri_value):
+
+            return BOOL
+
+        return self.orig_type
+
+    @property
+    def str_value(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_str_val is not None:
+            return self._cached_str_val
+
+        if self.orig_type in _BOOL_TRISTATE:
+            # Also calculates the visibility, so invalidation safe
+            self._cached_str_val = TRI_TO_STR[self.tri_value]
+            return self._cached_str_val
+
+        # As a quirk of Kconfig, undefined symbols get their name as their
+        # string value. This is why things like "FOO = bar" work for seeing if
+        # FOO has the value "bar".
+        if not self.orig_type:  # UNKNOWN
+            self._cached_str_val = self.name
+            return self.name
+
+        val = ""
+        # Warning: See Symbol._rec_invalidate(), and note that this is a hidden
+        # function call (property magic)
+        vis = self.visibility
+
+        self._write_to_conf = (vis != 0)
+
+        if self.orig_type in _INT_HEX:
+            # The C implementation checks the user value against the range in a
+            # separate code path (post-processing after loading a .config).
+            # Checking all values here instead makes more sense for us. It
+            # requires that we check for a range first.
+
+            base = _TYPE_TO_BASE[self.orig_type]
+
+            # Check if a range is in effect
+            for low_expr, high_expr, cond in self.ranges:
+                if expr_value(cond):
+                    has_active_range = True
+
+                    # The zeros are from the C implementation running strtoll()
+                    # on empty strings
+                    low = int(low_expr.str_value, base) if \
+                      _is_base_n(low_expr.str_value, base) else 0
+                    high = int(high_expr.str_value, base) if \
+                      _is_base_n(high_expr.str_value, base) else 0
+
+                    break
+            else:
+                has_active_range = False
+
+            # Defaults are used if the symbol is invisible, lacks a user value,
+            # or has an out-of-range user value
+            use_defaults = True
+
+            if vis and self.user_value:
+                user_val = int(self.user_value, base)
+                if has_active_range and not low <= user_val <= high:
+                    num2str = str if base == 10 else hex
+                    self.kconfig._warn(
+                        "user value {} on the {} symbol {} ignored due to "
+                        "being outside the active range ([{}, {}]) -- falling "
+                        "back on defaults"
+                        .format(num2str(user_val), TYPE_TO_STR[self.orig_type],
+                                self.name_and_loc,
+                                num2str(low), num2str(high)))
+                else:
+                    # If the user value is well-formed and satisfies range
+                    # contraints, it is stored in exactly the same form as
+                    # specified in the assignment (with or without "0x", etc.)
+                    val = self.user_value
+                    use_defaults = False
+
+            if use_defaults:
+                # No user value or invalid user value. Look at defaults.
+
+                # Used to implement the warning below
+                has_default = False
+
+                for sym, cond in self.defaults:
+                    if expr_value(cond):
+                        has_default = self._write_to_conf = True
+
+                        val = sym.str_value
+
+                        if _is_base_n(val, base):
+                            val_num = int(val, base)
+                        else:
+                            val_num = 0  # strtoll() on empty string
+
+                        break
+                else:
+                    val_num = 0  # strtoll() on empty string
+
+                # This clamping procedure runs even if there's no default
+                if has_active_range:
+                    clamp = None
+                    if val_num < low:
+                        clamp = low
+                    elif val_num > high:
+                        clamp = high
+
+                    if clamp is not None:
+                        # The value is rewritten to a standard form if it is
+                        # clamped
+                        val = str(clamp) \
+                              if self.orig_type is INT else \
+                              hex(clamp)
+
+                        if has_default:
+                            num2str = str if base == 10 else hex
+                            self.kconfig._warn(
+                                "default value {} on {} clamped to {} due to "
+                                "being outside the active range ([{}, {}])"
+                                .format(val_num, self.name_and_loc,
+                                        num2str(clamp), num2str(low),
+                                        num2str(high)))
+
+        elif self.orig_type is STRING:
+            if vis and self.user_value is not None:
+                # If the symbol is visible and has a user value, use that
+                val = self.user_value
+            else:
+                # Otherwise, look at defaults
+                for sym, cond in self.defaults:
+                    if expr_value(cond):
+                        val = sym.str_value
+                        self._write_to_conf = True
+                        break
+
+        # env_var corresponds to SYMBOL_AUTO in the C implementation, and is
+        # also set on the defconfig_list symbol there. Test for the
+        # defconfig_list symbol explicitly instead here, to avoid a nonsensical
+        # env_var setting and the defconfig_list symbol being printed
+        # incorrectly. This code is pretty cold anyway.
+        if self.env_var is not None or self is self.kconfig.defconfig_list:
+            self._write_to_conf = False
+
+        self._cached_str_val = val
+        return val
+
+    @property
+    def tri_value(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_tri_val is not None:
+            return self._cached_tri_val
+
+        if self.orig_type not in _BOOL_TRISTATE:
+            if self.orig_type:  # != UNKNOWN
+                # Would take some work to give the location here
+                self.kconfig._warn(
+                    "The {} symbol {} is being evaluated in a logical context "
+                    "somewhere. It will always evaluate to n."
+                    .format(TYPE_TO_STR[self.orig_type], self.name_and_loc))
+
+            self._cached_tri_val = 0
+            return 0
+
+        # Warning: See Symbol._rec_invalidate(), and note that this is a hidden
+        # function call (property magic)
+        vis = self.visibility
+        self._write_to_conf = (vis != 0)
+
+        val = 0
+
+        if not self.choice:
+            # Non-choice symbol
+
+            if vis and self.user_value is not None:
+                # If the symbol is visible and has a user value, use that
+                val = min(self.user_value, vis)
+
+            else:
+                # Otherwise, look at defaults and weak reverse dependencies
+                # (implies)
+
+                for default, cond in self.defaults:
+                    dep_val = expr_value(cond)
+                    if dep_val:
+                        val = min(expr_value(default), dep_val)
+                        if val:
+                            self._write_to_conf = True
+                        break
+
+                # Weak reverse dependencies are only considered if our
+                # direct dependencies are met
+                dep_val = expr_value(self.weak_rev_dep)
+                if dep_val and expr_value(self.direct_dep):
+                    val = max(dep_val, val)
+                    self._write_to_conf = True
+
+            # Reverse (select-related) dependencies take precedence
+            dep_val = expr_value(self.rev_dep)
+            if dep_val:
+                if expr_value(self.direct_dep) < dep_val:
+                    self._warn_select_unsatisfied_deps()
+
+                val = max(dep_val, val)
+                self._write_to_conf = True
+
+            # m is promoted to y for (1) bool symbols and (2) symbols with a
+            # weak_rev_dep (from imply) of y
+            if val == 1 and \
+               (self.type is BOOL or expr_value(self.weak_rev_dep) == 2):
+                val = 2
+
+        elif vis == 2:
+            # Visible choice symbol in y-mode choice. The choice mode limits
+            # the visibility of choice symbols, so it's sufficient to just
+            # check the visibility of the choice symbols themselves.
+            val = 2 if self.choice.selection is self else 0
+
+        elif vis and self.user_value:
+            # Visible choice symbol in m-mode choice, with set non-0 user value
+            val = 1
+
+        self._cached_tri_val = val
+        return val
+
+    @property
+    def assignable(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_assignable is None:
+            self._cached_assignable = self._assignable()
+        return self._cached_assignable
+
+    @property
+    def visibility(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_vis is None:
+            self._cached_vis = _visibility(self)
+        return self._cached_vis
+
+    @property
+    def config_string(self):
+        """
+        See the class documentation.
+        """
+        # _write_to_conf is determined when the value is calculated. This is a
+        # hidden function call due to property magic.
+        val = self.str_value
+        if not self._write_to_conf:
+            return ""
+
+        if self.orig_type in _BOOL_TRISTATE:
+            return "{}{}={}\n" \
+                   .format(self.kconfig.config_prefix, self.name, val) \
+                   if val != "n" else \
+                   "# {}{} is not set\n" \
+                   .format(self.kconfig.config_prefix, self.name)
+
+        if self.orig_type in _INT_HEX:
+            return "{}{}={}\n" \
+                   .format(self.kconfig.config_prefix, self.name, val)
+
+        # sym.orig_type is STRING
+        return '{}{}="{}"\n' \
+               .format(self.kconfig.config_prefix, self.name, escape(val))
+
+    @property
+    def name_and_loc(self):
+        """
+        See the class documentation.
+        """
+        return self.name + " " + _locs(self)
+
+    def set_value(self, value):
+        """
+        Sets the user value of the symbol.
+
+        Equal in effect to assigning the value to the symbol within a .config
+        file. For bool and tristate symbols, use the 'assignable' attribute to
+        check which values can currently be assigned. Setting values outside
+        'assignable' will cause Symbol.user_value to differ from
+        Symbol.str/tri_value (be truncated down or up).
+
+        Setting a choice symbol to 2 (y) sets Choice.user_selection to the
+        choice symbol in addition to setting Symbol.user_value.
+        Choice.user_selection is considered when the choice is in y mode (the
+        "normal" mode).
+
+        Other symbols that depend (possibly indirectly) on this symbol are
+        automatically recalculated to reflect the assigned value.
+
+        value:
+          The user value to give to the symbol. For bool and tristate symbols,
+          n/m/y can be specified either as 0/1/2 (the usual format for tristate
+          values in Kconfiglib) or as one of the strings "n", "m", or "y". For
+          other symbol types, pass a string.
+
+          Note that the value for an int/hex symbol is passed as a string, e.g.
+          "123" or "0x0123". The format of this string is preserved in the
+          output.
+
+          Values that are invalid for the type (such as "foo" or 1 (m) for a
+          BOOL or "0x123" for an INT) are ignored and won't be stored in
+          Symbol.user_value. Kconfiglib will print a warning by default for
+          invalid assignments, and set_value() will return False.
+
+        Returns True if the value is valid for the type of the symbol, and
+        False otherwise. This only looks at the form of the value. For BOOL and
+        TRISTATE symbols, check the Symbol.assignable attribute to see what
+        values are currently in range and would actually be reflected in the
+        value of the symbol. For other symbol types, check whether the
+        visibility is non-n.
+        """
+        if self.orig_type in _BOOL_TRISTATE and value in STR_TO_TRI:
+            value = STR_TO_TRI[value]
+
+        # If the new user value matches the old, nothing changes, and we can
+        # avoid invalidating cached values.
+        #
+        # This optimization is skipped for choice symbols: Setting a choice
+        # symbol's user value to y might change the state of the choice, so it
+        # wouldn't be safe (symbol user values always match the values set in a
+        # .config file or via set_value(), and are never implicitly updated).
+        if value == self.user_value and not self.choice:
+            self._was_set = True
+            return True
+
+        # Check if the value is valid for our type
+        if not (self.orig_type is BOOL     and value in (2, 0)     or
+                self.orig_type is TRISTATE and value in TRI_TO_STR or
+                value.__class__ is str and
+                (self.orig_type is STRING                        or
+                 self.orig_type is INT and _is_base_n(value, 10) or
+                 self.orig_type is HEX and _is_base_n(value, 16)
+                                       and int(value, 16) >= 0)):
+
+            # Display tristate values as n, m, y in the warning
+            self.kconfig._warn(
+                "the value {} is invalid for {}, which has type {} -- "
+                "assignment ignored"
+                .format(TRI_TO_STR[value] if value in TRI_TO_STR else
+                            "'{}'".format(value),
+                        self.name_and_loc, TYPE_TO_STR[self.orig_type]))
+
+            return False
+
+        self.user_value = value
+        self._was_set = True
+
+        if self.choice and value == 2:
+            # Setting a choice symbol to y makes it the user selection of the
+            # choice. Like for symbol user values, the user selection is not
+            # guaranteed to match the actual selection of the choice, as
+            # dependencies come into play.
+            self.choice.user_selection = self
+            self.choice._was_set = True
+            self.choice._rec_invalidate()
+        else:
+            self._rec_invalidate_if_has_prompt()
+
+        return True
+
+    def unset_value(self):
+        """
+        Removes any user value from the symbol, as if the symbol had never
+        gotten a user value via Kconfig.load_config() or Symbol.set_value().
+        """
+        if self.user_value is not None:
+            self.user_value = None
+            self._rec_invalidate_if_has_prompt()
+
+    @property
+    def referenced(self):
+        """
+        See the class documentation.
+        """
+        return {item for node in self.nodes for item in node.referenced}
+
+    @property
+    def orig_defaults(self):
+        """
+        See the class documentation.
+        """
+        return [d for node in self.nodes for d in node.orig_defaults]
+
+    @property
+    def orig_selects(self):
+        """
+        See the class documentation.
+        """
+        return [s for node in self.nodes for s in node.orig_selects]
+
+    @property
+    def orig_implies(self):
+        """
+        See the class documentation.
+        """
+        return [i for node in self.nodes for i in node.orig_implies]
+
+    @property
+    def orig_ranges(self):
+        """
+        See the class documentation.
+        """
+        return [r for node in self.nodes for r in node.orig_ranges]
+
+    def __repr__(self):
+        """
+        Returns a string with information about the symbol (including its name,
+        value, visibility, and location(s)) when it is evaluated on e.g. the
+        interactive Python prompt.
+        """
+        fields = ["symbol " + self.name, TYPE_TO_STR[self.type]]
+        add = fields.append
+
+        for node in self.nodes:
+            if node.prompt:
+                add('"{}"'.format(node.prompt[0]))
+
+        # Only add quotes for non-bool/tristate symbols
+        add("value " + (self.str_value if self.orig_type in _BOOL_TRISTATE
+                        else '"{}"'.format(self.str_value)))
+
+        if not self.is_constant:
+            # These aren't helpful to show for constant symbols
+
+            if self.user_value is not None:
+                # Only add quotes for non-bool/tristate symbols
+                add("user value " + (TRI_TO_STR[self.user_value]
+                                     if self.orig_type in _BOOL_TRISTATE
+                                     else '"{}"'.format(self.user_value)))
+
+            add("visibility " + TRI_TO_STR[self.visibility])
+
+            if self.choice:
+                add("choice symbol")
+
+            if self.is_allnoconfig_y:
+                add("allnoconfig_y")
+
+            if self is self.kconfig.defconfig_list:
+                add("is the defconfig_list symbol")
+
+            if self.env_var is not None:
+                add("from environment variable " + self.env_var)
+
+            if self is self.kconfig.modules:
+                add("is the modules symbol")
+
+            add("direct deps " + TRI_TO_STR[expr_value(self.direct_dep)])
+
+        if self.nodes:
+            for node in self.nodes:
+                add("{}:{}".format(node.filename, node.linenr))
+        else:
+            add("constant" if self.is_constant else "undefined")
+
+        return "<{}>".format(", ".join(fields))
+
+    def __str__(self):
+        """
+        Returns a string representation of the symbol when it is printed.
+        Matches the Kconfig format, with any parent dependencies propagated to
+        the 'depends on' condition.
+
+        The string is constructed by joining the strings returned by
+        MenuNode.__str__() for each of the symbol's menu nodes, so symbols
+        defined in multiple locations will return a string with all
+        definitions.
+
+        The returned string does not end in a newline. An empty string is
+        returned for undefined and constant symbols.
+        """
+        return self.custom_str(standard_sc_expr_str)
+
+    def custom_str(self, sc_expr_str_fn):
+        """
+        Works like Symbol.__str__(), but allows a custom format to be used for
+        all symbol/choice references. See expr_str().
+        """
+        return "\n\n".join(node.custom_str(sc_expr_str_fn)
+                           for node in self.nodes)
+
+    #
+    # Private methods
+    #
+
+    def __init__(self):
+        """
+        Symbol constructor -- not intended to be called directly by Kconfiglib
+        clients.
+        """
+        # These attributes are always set on the instance from outside and
+        # don't need defaults:
+        #   kconfig
+        #   direct_dep
+        #   is_constant
+        #   name
+        #   rev_dep
+        #   weak_rev_dep
+
+        # - UNKNOWN == 0
+        # - _visited is used during tree iteration and dep. loop detection
+        self.orig_type = self._visited = 0
+
+        self.nodes = []
+
+        self.defaults = []
+        self.selects = []
+        self.implies = []
+        self.ranges = []
+
+        self.user_value = \
+        self.choice = \
+        self.env_var = \
+        self._cached_str_val = self._cached_tri_val = self._cached_vis = \
+        self._cached_assignable = None
+
+        # _write_to_conf is calculated along with the value. If True, the
+        # Symbol gets a .config entry.
+
+        self.is_allnoconfig_y = \
+        self._was_set = \
+        self._write_to_conf = False
+
+        # See Kconfig._build_dep()
+        self._dependents = set()
+
+    def _assignable(self):
+        # Worker function for the 'assignable' attribute
+
+        if self.orig_type not in _BOOL_TRISTATE:
+            return ()
+
+        # Warning: See Symbol._rec_invalidate(), and note that this is a hidden
+        # function call (property magic)
+        vis = self.visibility
+        if not vis:
+            return ()
+
+        rev_dep_val = expr_value(self.rev_dep)
+
+        if vis == 2:
+            if self.choice:
+                return (2,)
+
+            if not rev_dep_val:
+                if self.type is BOOL or expr_value(self.weak_rev_dep) == 2:
+                    return (0, 2)
+                return (0, 1, 2)
+
+            if rev_dep_val == 2:
+                return (2,)
+
+            # rev_dep_val == 1
+
+            if self.type is BOOL or expr_value(self.weak_rev_dep) == 2:
+                return (2,)
+            return (1, 2)
+
+        # vis == 1
+
+        # Must be a tristate here, because bool m visibility gets promoted to y
+
+        if not rev_dep_val:
+            return (0, 1) if expr_value(self.weak_rev_dep) != 2 else (0, 2)
+
+        if rev_dep_val == 2:
+            return (2,)
+
+        # vis == rev_dep_val == 1
+
+        return (1,)
+
+    def _invalidate(self):
+        # Marks the symbol as needing to be recalculated
+
+        self._cached_str_val = self._cached_tri_val = self._cached_vis = \
+        self._cached_assignable = None
+
+    def _rec_invalidate(self):
+        # Invalidates the symbol and all items that (possibly) depend on it
+
+        if self is self.kconfig.modules:
+            # Invalidating MODULES has wide-ranging effects
+            self.kconfig._invalidate_all()
+        else:
+            self._invalidate()
+
+            for item in self._dependents:
+                # _cached_vis doubles as a flag that tells us whether 'item'
+                # has cached values, because it's calculated as a side effect
+                # of calculating all other (non-constant) cached values.
+                #
+                # If item._cached_vis is None, it means there can't be cached
+                # values on other items that depend on 'item', because if there
+                # were, some value on 'item' would have been calculated and
+                # item._cached_vis set as a side effect. It's therefore safe to
+                # stop the invalidation at symbols with _cached_vis None.
+                #
+                # This approach massively speeds up scripts that set a lot of
+                # values, vs simply invalidating all possibly dependent symbols
+                # (even when you already have a list of all the dependent
+                # symbols, because some symbols get huge dependency trees).
+                #
+                # This gracefully handles dependency loops too, which is nice
+                # for choices, where the choice depends on the choice symbols
+                # and vice versa.
+                if item._cached_vis is not None:
+                    item._rec_invalidate()
+
+    def _rec_invalidate_if_has_prompt(self):
+        # Invalidates the symbol and its dependent symbols, but only if the
+        # symbol has a prompt. User values never have an effect on promptless
+        # symbols, so we skip invalidation for them as an optimization.
+        #
+        # This also prevents constant (quoted) symbols from being invalidated
+        # if set_value() is called on them, which would make them lose their
+        # value and break things.
+        #
+        # Prints a warning if the symbol has no prompt. In some contexts (e.g.
+        # when loading a .config files) assignments to promptless symbols are
+        # normal and expected, so the warning can be disabled.
+
+        for node in self.nodes:
+            if node.prompt:
+                self._rec_invalidate()
+                return
+
+        if self.kconfig._warn_assign_no_prompt:
+            self.kconfig._warn(self.name_and_loc + " has no prompt, meaning "
+                               "user values have no effect on it")
+
+    def _str_default(self):
+        # write_min_config() helper function. Returns the value the symbol
+        # would get from defaults if it didn't have a user value. Uses exactly
+        # the same algorithm as the C implementation (though a bit cleaned up),
+        # for compatibility.
+
+        if self.orig_type in _BOOL_TRISTATE:
+            val = 0
+
+            # Defaults, selects, and implies do not affect choice symbols
+            if not self.choice:
+                for default, cond in self.defaults:
+                    cond_val = expr_value(cond)
+                    if cond_val:
+                        val = min(expr_value(default), cond_val)
+                        break
+
+                val = max(expr_value(self.rev_dep),
+                          expr_value(self.weak_rev_dep),
+                          val)
+
+                # Transpose mod to yes if type is bool (possibly due to modules
+                # being disabled)
+                if val == 1 and self.type is BOOL:
+                    val = 2
+
+            return TRI_TO_STR[val]
+
+        if self.orig_type:  # STRING/INT/HEX
+            for default, cond in self.defaults:
+                if expr_value(cond):
+                    return default.str_value
+
+        return ""
+
+    def _warn_select_unsatisfied_deps(self):
+        # Helper for printing an informative warning when a symbol with
+        # unsatisfied direct dependencies (dependencies from 'depends on', ifs,
+        # and menus) is selected by some other symbol. Also warn if a symbol
+        # whose direct dependencies evaluate to m is selected to y.
+
+        msg = "{} has direct dependencies {} with value {}, but is " \
+              "currently being {}-selected by the following symbols:" \
+              .format(self.name_and_loc, expr_str(self.direct_dep),
+                      TRI_TO_STR[expr_value(self.direct_dep)],
+                      TRI_TO_STR[expr_value(self.rev_dep)])
+
+        # The reverse dependencies from each select are ORed together
+        for select in split_expr(self.rev_dep, OR):
+            if expr_value(select) <= expr_value(self.direct_dep):
+                # Only include selects that exceed the direct dependencies
+                continue
+
+            # - 'select A if B' turns into A && B
+            # - 'select A' just turns into A
+            #
+            # In both cases, we can split on AND and pick the first operand
+            selecting_sym = split_expr(select, AND)[0]
+
+            msg += "\n - {}, with value {}, direct dependencies {} " \
+                   "(value: {})" \
+                   .format(selecting_sym.name_and_loc,
+                           selecting_sym.str_value,
+                           expr_str(selecting_sym.direct_dep),
+                           TRI_TO_STR[expr_value(selecting_sym.direct_dep)])
+
+            if select.__class__ is tuple:
+                msg += ", and select condition {} (value: {})" \
+                       .format(expr_str(select[2]),
+                               TRI_TO_STR[expr_value(select[2])])
+
+        self.kconfig._warn(msg)
+
+
+class Choice(object):
+    """
+    Represents a choice statement:
+
+      choice
+          ...
+      endchoice
+
+    The following attributes are available on Choice instances. They should be
+    treated as read-only, and some are implemented through @property magic (but
+    are still efficient to access due to internal caching).
+
+    Note: Prompts, help texts, and locations are stored in the Choice's
+    MenuNode(s) rather than in the Choice itself. Check the MenuNode class and
+    the Choice.nodes attribute. This organization matches the C tools.
+
+    name:
+      The name of the choice, e.g. "FOO" for 'choice FOO', or None if the
+      Choice has no name.
+
+    type:
+      The type of the choice. One of BOOL, TRISTATE, UNKNOWN. UNKNOWN is for
+      choices defined without a type where none of the contained symbols have a
+      type either (otherwise the choice inherits the type of the first symbol
+      defined with a type).
+
+      When running without modules (CONFIG_MODULES=n), TRISTATE choices
+      magically change type to BOOL. This matches the C tools, and makes sense
+      for menuconfig-like functionality.
+
+    orig_type:
+      The type as given in the Kconfig file, without any magic applied. Used
+      when printing the choice.
+
+    tri_value:
+      The tristate value (mode) of the choice. A choice can be in one of three
+      modes:
+
+        0 (n) - The choice is disabled and no symbols can be selected. For
+                visible choices, this mode is only possible for choices with
+                the 'optional' flag set (see kconfig-language.txt).
+
+        1 (m) - Any number of choice symbols can be set to m, the rest will
+                be n.
+
+        2 (y) - One symbol will be y, the rest n.
+
+      Only tristate choices can be in m mode. The visibility of the choice is
+      an upper bound on the mode, and the mode in turn is an upper bound on the
+      visibility of the choice symbols.
+
+      To change the mode, use Choice.set_value().
+
+      Implementation note:
+        The C tools internally represent choices as a type of symbol, with
+        special-casing in many code paths. This is why there is a lot of
+        similarity to Symbol. The value (mode) of a choice is really just a
+        normal symbol value, and an implicit reverse dependency forces its
+        lower bound to m for visible non-optional choices (the reverse
+        dependency is 'm && <visibility>').
+
+        Symbols within choices get the choice propagated as a dependency to
+        their properties. This turns the mode of the choice into an upper bound
+        on e.g. the visibility of choice symbols, and explains the gotcha
+        related to printing choice symbols mentioned in the module docstring.
+
+        Kconfiglib uses a separate Choice class only because it makes the code
+        and interface less confusing (especially in a user-facing interface).
+        Corresponding attributes have the same name in the Symbol and Choice
+        classes, for consistency and compatibility.
+
+    str_value:
+      Like choice.tri_value, but gives the value as one of the strings
+      "n", "m", or "y"
+
+    user_value:
+      The value (mode) selected by the user through Choice.set_value(). Either
+      0, 1, or 2, or None if the user hasn't selected a mode. See
+      Symbol.user_value.
+
+      WARNING: Do not assign directly to this. It will break things. Use
+      Choice.set_value() instead.
+
+    assignable:
+      See the symbol class documentation. Gives the assignable values (modes).
+
+    selection:
+      The Symbol instance of the currently selected symbol. None if the Choice
+      is not in y mode or has no selected symbol (due to unsatisfied
+      dependencies on choice symbols).
+
+      WARNING: Do not assign directly to this. It will break things. Call
+      sym.set_value(2) on the choice symbol you want to select instead.
+
+    user_selection:
+      The symbol selected by the user (by setting it to y). Ignored if the
+      choice is not in y mode, but still remembered so that the choice "snaps
+      back" to the user selection if the mode is changed back to y. This might
+      differ from 'selection' due to unsatisfied dependencies.
+
+      WARNING: Do not assign directly to this. It will break things. Call
+      sym.set_value(2) on the choice symbol to be selected instead.
+
+    visibility:
+      See the Symbol class documentation. Acts on the value (mode).
+
+    name_and_loc:
+      Holds a string like
+
+        "<choice MY_CHOICE> (defined at foo/Kconfig:12)"
+
+      , giving the name of the choice and its definition location(s). If the
+      choice has no name (isn't defined with 'choice MY_CHOICE'), then it will
+      be shown as "<choice>" before the list of locations (always a single one
+      in that case).
+
+    syms:
+      List of symbols contained in the choice.
+
+      Obscure gotcha: If a symbol depends on the previous symbol within a
+      choice so that an implicit menu is created, it won't be a choice symbol,
+      and won't be included in 'syms'.
+
+    nodes:
+      A list of MenuNodes for this choice. In practice, the list will probably
+      always contain a single MenuNode, but it is possible to give a choice a
+      name and define it in multiple locations.
+
+    defaults:
+      List of (symbol, cond) tuples for the choice's 'defaults' properties. For
+      example, 'default A if B && C' is represented as (A, (AND, B, C)). If
+      there is no condition, 'cond' is self.kconfig.y.
+
+      Note that 'depends on' and parent dependencies are propagated to
+      'default' conditions.
+
+    orig_defaults:
+      See the corresponding attribute on the MenuNode class.
+
+    direct_dep:
+      See Symbol.direct_dep.
+
+    referenced:
+      A set() with all symbols referenced in the properties and property
+      conditions of the choice.
+
+      Also includes dependencies from surrounding menus and ifs, because those
+      get propagated to the choice (see the 'Intro to symbol values' section in
+      the module docstring).
+
+    is_optional:
+      True if the choice has the 'optional' flag set on it and can be in
+      n mode.
+
+    kconfig:
+      The Kconfig instance this choice is from.
+    """
+    __slots__ = (
+        "_cached_assignable",
+        "_cached_selection",
+        "_cached_vis",
+        "_dependents",
+        "_visited",
+        "_was_set",
+        "defaults",
+        "direct_dep",
+        "is_constant",
+        "is_optional",
+        "kconfig",
+        "name",
+        "nodes",
+        "orig_type",
+        "syms",
+        "user_selection",
+        "user_value",
+    )
+
+    #
+    # Public interface
+    #
+
+    @property
+    def type(self):
+        """
+        Returns the type of the choice. See Symbol.type.
+        """
+        if self.orig_type is TRISTATE and not self.kconfig.modules.tri_value:
+            return BOOL
+        return self.orig_type
+
+    @property
+    def str_value(self):
+        """
+        See the class documentation.
+        """
+        return TRI_TO_STR[self.tri_value]
+
+    @property
+    def tri_value(self):
+        """
+        See the class documentation.
+        """
+        # This emulates a reverse dependency of 'm && visibility' for
+        # non-optional choices, which is how the C implementation does it
+
+        val = 0 if self.is_optional else 1
+
+        if self.user_value is not None:
+            val = max(val, self.user_value)
+
+        # Warning: See Symbol._rec_invalidate(), and note that this is a hidden
+        # function call (property magic)
+        val = min(val, self.visibility)
+
+        # Promote m to y for boolean choices
+        return 2 if val == 1 and self.type is BOOL else val
+
+    @property
+    def assignable(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_assignable is None:
+            self._cached_assignable = self._assignable()
+        return self._cached_assignable
+
+    @property
+    def visibility(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_vis is None:
+            self._cached_vis = _visibility(self)
+        return self._cached_vis
+
+    @property
+    def name_and_loc(self):
+        """
+        See the class documentation.
+        """
+        # Reuse the expression format, which is '<choice (name, if any)>'.
+        return standard_sc_expr_str(self) + " " + _locs(self)
+
+    @property
+    def selection(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_selection is _NO_CACHED_SELECTION:
+            self._cached_selection = self._selection()
+        return self._cached_selection
+
+    def set_value(self, value):
+        """
+        Sets the user value (mode) of the choice. Like for Symbol.set_value(),
+        the visibility might truncate the value. Choices without the 'optional'
+        attribute (is_optional) can never be in n mode, but 0/"n" is still
+        accepted since it's not a malformed value (though it will have no
+        effect).
+
+        Returns True if the value is valid for the type of the choice, and
+        False otherwise. This only looks at the form of the value. Check the
+        Choice.assignable attribute to see what values are currently in range
+        and would actually be reflected in the mode of the choice.
+        """
+        if value in STR_TO_TRI:
+            value = STR_TO_TRI[value]
+
+        if value == self.user_value:
+            # We know the value must be valid if it was successfully set
+            # previously
+            self._was_set = True
+            return True
+
+        if not (self.orig_type is BOOL     and value in (2, 0) or
+                self.orig_type is TRISTATE and value in TRI_TO_STR):
+
+            # Display tristate values as n, m, y in the warning
+            self.kconfig._warn(
+                "the value {} is invalid for {}, which has type {} -- "
+                "assignment ignored"
+                .format(TRI_TO_STR[value] if value in TRI_TO_STR else
+                            "'{}'".format(value),
+                        self.name_and_loc, TYPE_TO_STR[self.orig_type]))
+
+            return False
+
+        self.user_value = value
+        self._was_set = True
+        self._rec_invalidate()
+
+        return True
+
+    def unset_value(self):
+        """
+        Resets the user value (mode) and user selection of the Choice, as if
+        the user had never touched the mode or any of the choice symbols.
+        """
+        if self.user_value is not None or self.user_selection:
+            self.user_value = self.user_selection = None
+            self._rec_invalidate()
+
+    @property
+    def referenced(self):
+        """
+        See the class documentation.
+        """
+        return {item for node in self.nodes for item in node.referenced}
+
+    @property
+    def orig_defaults(self):
+        """
+        See the class documentation.
+        """
+        return [d for node in self.nodes for d in node.orig_defaults]
+
+    def __repr__(self):
+        """
+        Returns a string with information about the choice when it is evaluated
+        on e.g. the interactive Python prompt.
+        """
+        fields = ["choice " + self.name if self.name else "choice",
+                  TYPE_TO_STR[self.type]]
+        add = fields.append
+
+        for node in self.nodes:
+            if node.prompt:
+                add('"{}"'.format(node.prompt[0]))
+
+        add("mode " + self.str_value)
+
+        if self.user_value is not None:
+            add('user mode {}'.format(TRI_TO_STR[self.user_value]))
+
+        if self.selection:
+            add("{} selected".format(self.selection.name))
+
+        if self.user_selection:
+            user_sel_str = "{} selected by user" \
+                           .format(self.user_selection.name)
+
+            if self.selection is not self.user_selection:
+                user_sel_str += " (overridden)"
+
+            add(user_sel_str)
+
+        add("visibility " + TRI_TO_STR[self.visibility])
+
+        if self.is_optional:
+            add("optional")
+
+        for node in self.nodes:
+            add("{}:{}".format(node.filename, node.linenr))
+
+        return "<{}>".format(", ".join(fields))
+
+    def __str__(self):
+        """
+        Returns a string representation of the choice when it is printed.
+        Matches the Kconfig format (though without the contained choice
+        symbols), with any parent dependencies propagated to the 'depends on'
+        condition.
+
+        The returned string does not end in a newline.
+
+        See Symbol.__str__() as well.
+        """
+        return self.custom_str(standard_sc_expr_str)
+
+    def custom_str(self, sc_expr_str_fn):
+        """
+        Works like Choice.__str__(), but allows a custom format to be used for
+        all symbol/choice references. See expr_str().
+        """
+        return "\n\n".join(node.custom_str(sc_expr_str_fn)
+                           for node in self.nodes)
+
+    #
+    # Private methods
+    #
+
+    def __init__(self):
+        """
+        Choice constructor -- not intended to be called directly by Kconfiglib
+        clients.
+        """
+        # These attributes are always set on the instance from outside and
+        # don't need defaults:
+        #   direct_dep
+        #   kconfig
+
+        # - UNKNOWN == 0
+        # - _visited is used during dep. loop detection
+        self.orig_type = self._visited = 0
+
+        self.nodes = []
+
+        self.syms = []
+        self.defaults = []
+
+        self.name = \
+        self.user_value = self.user_selection = \
+        self._cached_vis = self._cached_assignable = None
+
+        self._cached_selection = _NO_CACHED_SELECTION
+
+        # is_constant is checked by _depend_on(). Just set it to avoid having
+        # to special-case choices.
+        self.is_constant = self.is_optional = False
+
+        # See Kconfig._build_dep()
+        self._dependents = set()
+
+    def _assignable(self):
+        # Worker function for the 'assignable' attribute
+
+        # Warning: See Symbol._rec_invalidate(), and note that this is a hidden
+        # function call (property magic)
+        vis = self.visibility
+
+        if not vis:
+            return ()
+
+        if vis == 2:
+            if not self.is_optional:
+                return (2,) if self.type is BOOL else (1, 2)
+            return (0, 2) if self.type is BOOL else (0, 1, 2)
+
+        # vis == 1
+
+        return (0, 1) if self.is_optional else (1,)
+
+    def _selection(self):
+        # Worker function for the 'selection' attribute
+
+        # Warning: See Symbol._rec_invalidate(), and note that this is a hidden
+        # function call (property magic)
+        if self.tri_value != 2:
+            # Not in y mode, so no selection
+            return None
+
+        # Use the user selection if it's visible
+        if self.user_selection and self.user_selection.visibility:
+            return self.user_selection
+
+        # Otherwise, check if we have a default
+        return self._selection_from_defaults()
+
+    def _selection_from_defaults(self):
+        # Check if we have a default
+        for sym, cond in self.defaults:
+            # The default symbol must be visible too
+            if expr_value(cond) and sym.visibility:
+                return sym
+
+        # Otherwise, pick the first visible symbol, if any
+        for sym in self.syms:
+            if sym.visibility:
+                return sym
+
+        # Couldn't find a selection
+        return None
+
+    def _invalidate(self):
+        self._cached_vis = self._cached_assignable = None
+        self._cached_selection = _NO_CACHED_SELECTION
+
+    def _rec_invalidate(self):
+        # See Symbol._rec_invalidate()
+
+        self._invalidate()
+
+        for item in self._dependents:
+            if item._cached_vis is not None:
+                item._rec_invalidate()
+
+
+class MenuNode(object):
+    """
+    Represents a menu node in the configuration. This corresponds to an entry
+    in e.g. the 'make menuconfig' interface, though non-visible choices, menus,
+    and comments also get menu nodes. If a symbol or choice is defined in
+    multiple locations, it gets one menu node for each location.
+
+    The top-level menu node, corresponding to the implicit top-level menu, is
+    available in Kconfig.top_node.
+
+    The menu nodes for a Symbol or Choice can be found in the
+    Symbol/Choice.nodes attribute. Menus and comments are represented as plain
+    menu nodes, with their text stored in the prompt attribute (prompt[0]).
+    This mirrors the C implementation.
+
+    The following attributes are available on MenuNode instances. They should
+    be viewed as read-only.
+
+    item:
+      Either a Symbol, a Choice, or one of the constants MENU and COMMENT.
+      Menus and comments are represented as plain menu nodes. Ifs are collapsed
+      (matching the C implementation) and do not appear in the final menu tree.
+
+    next:
+      The following menu node. None if there is no following node.
+
+    list:
+      The first child menu node. None if there are no children.
+
+      Choices and menus naturally have children, but Symbols can also have
+      children because of menus created automatically from dependencies (see
+      kconfig-language.txt).
+
+    parent:
+      The parent menu node. None if there is no parent.
+
+    prompt:
+      A (string, cond) tuple with the prompt for the menu node and its
+      conditional expression (which is self.kconfig.y if there is no
+      condition). None if there is no prompt.
+
+      For symbols and choices, the prompt is stored in the MenuNode rather than
+      the Symbol or Choice instance. For menus and comments, the prompt holds
+      the text.
+
+    defaults:
+      The 'default' properties for this particular menu node. See
+      symbol.defaults.
+
+      When evaluating defaults, you should use Symbol/Choice.defaults instead,
+      as it include properties from all menu nodes (a symbol/choice can have
+      multiple definition locations/menu nodes). MenuNode.defaults is meant for
+      documentation generation.
+
+    selects:
+      Like MenuNode.defaults, for selects.
+
+    implies:
+      Like MenuNode.defaults, for implies.
+
+    ranges:
+      Like MenuNode.defaults, for ranges.
+
+    orig_prompt:
+    orig_defaults:
+    orig_selects:
+    orig_implies:
+    orig_ranges:
+      These work the like the corresponding attributes without orig_*, but omit
+      any dependencies propagated from 'depends on' and surrounding 'if's (the
+      direct dependencies, stored in MenuNode.dep).
+
+      One use for this is generating less cluttered documentation, by only
+      showing the direct dependencies in one place.
+
+    help:
+      The help text for the menu node for Symbols and Choices. None if there is
+      no help text. Always stored in the node rather than the Symbol or Choice.
+      It is possible to have a separate help text at each location if a symbol
+      is defined in multiple locations.
+
+      Trailing whitespace (including a final newline) is stripped from the help
+      text. This was not the case before Kconfiglib 10.21.0, where the format
+      was undocumented.
+
+    dep:
+      The direct ('depends on') dependencies for the menu node, or
+      self.kconfig.y if there are no direct dependencies.
+
+      This attribute includes any dependencies from surrounding menus and ifs.
+      Those get propagated to the direct dependencies, and the resulting direct
+      dependencies in turn get propagated to the conditions of all properties.
+
+      If a symbol or choice is defined in multiple locations, only the
+      properties defined at a particular location get the corresponding
+      MenuNode.dep dependencies propagated to them.
+
+    visibility:
+      The 'visible if' dependencies for the menu node (which must represent a
+      menu), or self.kconfig.y if there are no 'visible if' dependencies.
+      'visible if' dependencies are recursively propagated to the prompts of
+      symbols and choices within the menu.
+
+    referenced:
+      A set() with all symbols and choices referenced in the properties and
+      property conditions of the menu node.
+
+      Also includes dependencies inherited from surrounding menus and ifs.
+      Choices appear in the dependencies of choice symbols.
+
+    is_menuconfig:
+      Set to True if the children of the menu node should be displayed in a
+      separate menu. This is the case for the following items:
+
+        - Menus (node.item == MENU)
+
+        - Choices
+
+        - Symbols defined with the 'menuconfig' keyword. The children come from
+          implicitly created submenus, and should be displayed in a separate
+          menu rather than being indented.
+
+      'is_menuconfig' is just a hint on how to display the menu node. It's
+      ignored internally by Kconfiglib, except when printing symbols.
+
+    filename/linenr:
+      The location where the menu node appears. The filename is relative to
+      $srctree (or to the current directory if $srctree isn't set), except
+      absolute paths are used for paths outside $srctree.
+
+    include_path:
+      A tuple of (filename, linenr) tuples, giving the locations of the
+      'source' statements via which the Kconfig file containing this menu node
+      was included. The first element is the location of the 'source' statement
+      in the top-level Kconfig file passed to Kconfig.__init__(), etc.
+
+      Note that the Kconfig file of the menu node itself isn't included. Check
+      'filename' and 'linenr' for that.
+
+    kconfig:
+      The Kconfig instance the menu node is from.
+    """
+    __slots__ = (
+        "dep",
+        "filename",
+        "help",
+        "include_path",
+        "is_menuconfig",
+        "item",
+        "kconfig",
+        "linenr",
+        "list",
+        "next",
+        "parent",
+        "prompt",
+        "visibility",
+
+        # Properties
+        "defaults",
+        "selects",
+        "implies",
+        "ranges",
+    )
+
+    def __init__(self):
+        # Properties defined on this particular menu node. A local 'depends on'
+        # only applies to these, in case a symbol is defined in multiple
+        # locations.
+        self.defaults = []
+        self.selects = []
+        self.implies = []
+        self.ranges = []
+
+    @property
+    def orig_prompt(self):
+        """
+        See the class documentation.
+        """
+        if not self.prompt:
+            return None
+        return (self.prompt[0], self._strip_dep(self.prompt[1]))
+
+    @property
+    def orig_defaults(self):
+        """
+        See the class documentation.
+        """
+        return [(default, self._strip_dep(cond))
+                for default, cond in self.defaults]
+
+    @property
+    def orig_selects(self):
+        """
+        See the class documentation.
+        """
+        return [(select, self._strip_dep(cond))
+                for select, cond in self.selects]
+
+    @property
+    def orig_implies(self):
+        """
+        See the class documentation.
+        """
+        return [(imply, self._strip_dep(cond))
+                for imply, cond in self.implies]
+
+    @property
+    def orig_ranges(self):
+        """
+        See the class documentation.
+        """
+        return [(low, high, self._strip_dep(cond))
+                for low, high, cond in self.ranges]
+
+    @property
+    def referenced(self):
+        """
+        See the class documentation.
+        """
+        # self.dep is included to catch dependencies from a lone 'depends on'
+        # when there are no properties to propagate it to
+        res = expr_items(self.dep)
+
+        if self.prompt:
+            res |= expr_items(self.prompt[1])
+
+        if self.item is MENU:
+            res |= expr_items(self.visibility)
+
+        for value, cond in self.defaults:
+            res |= expr_items(value)
+            res |= expr_items(cond)
+
+        for value, cond in self.selects:
+            res.add(value)
+            res |= expr_items(cond)
+
+        for value, cond in self.implies:
+            res.add(value)
+            res |= expr_items(cond)
+
+        for low, high, cond in self.ranges:
+            res.add(low)
+            res.add(high)
+            res |= expr_items(cond)
+
+        return res
+
+    def __repr__(self):
+        """
+        Returns a string with information about the menu node when it is
+        evaluated on e.g. the interactive Python prompt.
+        """
+        fields = []
+        add = fields.append
+
+        if self.item.__class__ is Symbol:
+            add("menu node for symbol " + self.item.name)
+
+        elif self.item.__class__ is Choice:
+            s = "menu node for choice"
+            if self.item.name is not None:
+                s += " " + self.item.name
+            add(s)
+
+        elif self.item is MENU:
+            add("menu node for menu")
+
+        else:  # self.item is COMMENT
+            add("menu node for comment")
+
+        if self.prompt:
+            add('prompt "{}" (visibility {})'.format(
+                self.prompt[0], TRI_TO_STR[expr_value(self.prompt[1])]))
+
+        if self.item.__class__ is Symbol and self.is_menuconfig:
+            add("is menuconfig")
+
+        add("deps " + TRI_TO_STR[expr_value(self.dep)])
+
+        if self.item is MENU:
+            add("'visible if' deps " + TRI_TO_STR[expr_value(self.visibility)])
+
+        if self.item.__class__ in _SYMBOL_CHOICE and self.help is not None:
+            add("has help")
+
+        if self.list:
+            add("has child")
+
+        if self.next:
+            add("has next")
+
+        add("{}:{}".format(self.filename, self.linenr))
+
+        return "<{}>".format(", ".join(fields))
+
+    def __str__(self):
+        """
+        Returns a string representation of the menu node. Matches the Kconfig
+        format, with any parent dependencies propagated to the 'depends on'
+        condition.
+
+        The output could (almost) be fed back into a Kconfig parser to redefine
+        the object associated with the menu node. See the module documentation
+        for a gotcha related to choice symbols.
+
+        For symbols and choices with multiple menu nodes (multiple definition
+        locations), properties that aren't associated with a particular menu
+        node are shown on all menu nodes ('option env=...', 'optional' for
+        choices, etc.).
+
+        The returned string does not end in a newline.
+        """
+        return self.custom_str(standard_sc_expr_str)
+
+    def custom_str(self, sc_expr_str_fn):
+        """
+        Works like MenuNode.__str__(), but allows a custom format to be used
+        for all symbol/choice references. See expr_str().
+        """
+        return self._menu_comment_node_str(sc_expr_str_fn) \
+               if self.item in _MENU_COMMENT else \
+               self._sym_choice_node_str(sc_expr_str_fn)
+
+    def _menu_comment_node_str(self, sc_expr_str_fn):
+        s = '{} "{}"'.format("menu" if self.item is MENU else "comment",
+                             self.prompt[0])
+
+        if self.dep is not self.kconfig.y:
+            s += "\n\tdepends on {}".format(expr_str(self.dep, sc_expr_str_fn))
+
+        if self.item is MENU and self.visibility is not self.kconfig.y:
+            s += "\n\tvisible if {}".format(expr_str(self.visibility,
+                                                     sc_expr_str_fn))
+
+        return s
+
+    def _sym_choice_node_str(self, sc_expr_str_fn):
+        def indent_add(s):
+            lines.append("\t" + s)
+
+        def indent_add_cond(s, cond):
+            if cond is not self.kconfig.y:
+                s += " if " + expr_str(cond, sc_expr_str_fn)
+            indent_add(s)
+
+        sc = self.item
+
+        if sc.__class__ is Symbol:
+            lines = [("menuconfig " if self.is_menuconfig else "config ")
+                     + sc.name]
+        else:
+            lines = ["choice " + sc.name if sc.name else "choice"]
+
+        if sc.orig_type and not self.prompt:  # sc.orig_type != UNKNOWN
+            # If there's a prompt, we'll use the '<type> "prompt"' shorthand
+            # instead
+            indent_add(TYPE_TO_STR[sc.orig_type])
+
+        if self.prompt:
+            if sc.orig_type:
+                prefix = TYPE_TO_STR[sc.orig_type]
+            else:
+                # Symbol defined without a type (which generates a warning)
+                prefix = "prompt"
+
+            indent_add_cond(prefix + ' "{}"'.format(escape(self.prompt[0])),
+                            self.orig_prompt[1])
+
+        if sc.__class__ is Symbol:
+            if sc.is_allnoconfig_y:
+                indent_add("option allnoconfig_y")
+
+            if sc is sc.kconfig.defconfig_list:
+                indent_add("option defconfig_list")
+
+            if sc.env_var is not None:
+                indent_add('option env="{}"'.format(sc.env_var))
+
+            if sc is sc.kconfig.modules:
+                indent_add("option modules")
+
+            for low, high, cond in self.orig_ranges:
+                indent_add_cond(
+                    "range {} {}".format(sc_expr_str_fn(low),
+                                         sc_expr_str_fn(high)),
+                    cond)
+
+        for default, cond in self.orig_defaults:
+            indent_add_cond("default " + expr_str(default, sc_expr_str_fn),
+                            cond)
+
+        if sc.__class__ is Choice and sc.is_optional:
+            indent_add("optional")
+
+        if sc.__class__ is Symbol:
+            for select, cond in self.orig_selects:
+                indent_add_cond("select " + sc_expr_str_fn(select), cond)
+
+            for imply, cond in self.orig_implies:
+                indent_add_cond("imply " + sc_expr_str_fn(imply), cond)
+
+        if self.dep is not sc.kconfig.y:
+            indent_add("depends on " + expr_str(self.dep, sc_expr_str_fn))
+
+        if self.help is not None:
+            indent_add("help")
+            for line in self.help.splitlines():
+                indent_add("  " + line)
+
+        return "\n".join(lines)
+
+    def _strip_dep(self, expr):
+        # Helper function for removing MenuNode.dep from 'expr'. Uses two
+        # pieces of internal knowledge: (1) Expressions are reused rather than
+        # copied, and (2) the direct dependencies always appear at the end.
+
+        # ... if dep -> ... if y
+        if self.dep is expr:
+            return self.kconfig.y
+
+        # (AND, X, dep) -> X
+        if expr.__class__ is tuple and expr[0] is AND and expr[2] is self.dep:
+            return expr[1]
+
+        return expr
+
+
+class Variable(object):
+    """
+    Represents a preprocessor variable/function.
+
+    The following attributes are available:
+
+    name:
+      The name of the variable.
+
+    value:
+      The unexpanded value of the variable.
+
+    expanded_value:
+      The expanded value of the variable. For simple variables (those defined
+      with :=), this will equal 'value'. Accessing this property will raise a
+      KconfigError if the expansion seems to be stuck in a loop.
+
+      Accessing this field is the same as calling expanded_value_w_args() with
+      no arguments. I hadn't considered function arguments when adding it. It
+      is retained for backwards compatibility though.
+
+    is_recursive:
+      True if the variable is recursive (defined with =).
+    """
+    __slots__ = (
+        "_n_expansions",
+        "is_recursive",
+        "kconfig",
+        "name",
+        "value",
+    )
+
+    @property
+    def expanded_value(self):
+        """
+        See the class documentation.
+        """
+        return self.expanded_value_w_args()
+
+    def expanded_value_w_args(self, *args):
+        """
+        Returns the expanded value of the variable/function. Any arguments
+        passed will be substituted for $(1), $(2), etc.
+
+        Raises a KconfigError if the expansion seems to be stuck in a loop.
+        """
+        return self.kconfig._fn_val((self.name,) + args)
+
+    def __repr__(self):
+        return "<variable {}, {}, value '{}'>" \
+               .format(self.name,
+                       "recursive" if self.is_recursive else "immediate",
+                       self.value)
+
+
+class KconfigError(Exception):
+    """
+    Exception raised for Kconfig-related errors.
+
+    KconfigError and KconfigSyntaxError are the same class. The
+    KconfigSyntaxError alias is only maintained for backwards compatibility.
+    """
+
+KconfigSyntaxError = KconfigError  # Backwards compatibility
+
+
+class InternalError(Exception):
+    "Never raised. Kept around for backwards compatibility."
+
+
+# Workaround:
+#
+# If 'errno' and 'strerror' are set on IOError, then __str__() always returns
+# "[Errno <errno>] <strerror>", ignoring any custom message passed to the
+# constructor. By defining our own subclass, we can use a custom message while
+# also providing 'errno', 'strerror', and 'filename' to scripts.
+class _KconfigIOError(IOError):
+    def __init__(self, ioerror, msg):
+        self.msg = msg
+        super(_KconfigIOError, self).__init__(
+            ioerror.errno, ioerror.strerror, ioerror.filename)
+
+    def __str__(self):
+        return self.msg
+
+
+#
+# Public functions
+#
+
+
+def expr_value(expr):
+    """
+    Evaluates the expression 'expr' to a tristate value. Returns 0 (n), 1 (m),
+    or 2 (y).
+
+    'expr' must be an already-parsed expression from a Symbol, Choice, or
+    MenuNode property. To evaluate an expression represented as a string, use
+    Kconfig.eval_string().
+
+    Passing subexpressions of expressions to this function works as expected.
+    """
+    if expr.__class__ is not tuple:
+        return expr.tri_value
+
+    if expr[0] is AND:
+        v1 = expr_value(expr[1])
+        # Short-circuit the n case as an optimization (~5% faster
+        # allnoconfig.py and allyesconfig.py, as of writing)
+        return 0 if not v1 else min(v1, expr_value(expr[2]))
+
+    if expr[0] is OR:
+        v1 = expr_value(expr[1])
+        # Short-circuit the y case as an optimization
+        return 2 if v1 == 2 else max(v1, expr_value(expr[2]))
+
+    if expr[0] is NOT:
+        return 2 - expr_value(expr[1])
+
+    # Relation
+    #
+    # Implements <, <=, >, >= comparisons as well. These were added to
+    # kconfig in 31847b67 (kconfig: allow use of relations other than
+    # (in)equality).
+
+    rel, v1, v2 = expr
+
+    # If both operands are strings...
+    if v1.orig_type is STRING and v2.orig_type is STRING:
+        # ...then compare them lexicographically
+        comp = _strcmp(v1.str_value, v2.str_value)
+    else:
+        # Otherwise, try to compare them as numbers
+        try:
+            comp = _sym_to_num(v1) - _sym_to_num(v2)
+        except ValueError:
+            # Fall back on a lexicographic comparison if the operands don't
+            # parse as numbers
+            comp = _strcmp(v1.str_value, v2.str_value)
+
+    return 2*(comp == 0 if rel is EQUAL else
+              comp != 0 if rel is UNEQUAL else
+              comp <  0 if rel is LESS else
+              comp <= 0 if rel is LESS_EQUAL else
+              comp >  0 if rel is GREATER else
+              comp >= 0)
+
+
+def standard_sc_expr_str(sc):
+    """
+    Standard symbol/choice printing function. Uses plain Kconfig syntax, and
+    displays choices as <choice> (or <choice NAME>, for named choices).
+
+    See expr_str().
+    """
+    if sc.__class__ is Symbol:
+        if sc.is_constant and sc.name not in STR_TO_TRI:
+            return '"{}"'.format(escape(sc.name))
+        return sc.name
+
+    return "<choice {}>".format(sc.name) if sc.name else "<choice>"
+
+
+def expr_str(expr, sc_expr_str_fn=standard_sc_expr_str):
+    """
+    Returns the string representation of the expression 'expr', as in a Kconfig
+    file.
+
+    Passing subexpressions of expressions to this function works as expected.
+
+    sc_expr_str_fn (default: standard_sc_expr_str):
+      This function is called for every symbol/choice (hence "sc") appearing in
+      the expression, with the symbol/choice as the argument. It is expected to
+      return a string to be used for the symbol/choice.
+
+      This can be used e.g. to turn symbols/choices into links when generating
+      documentation, or for printing the value of each symbol/choice after it.
+
+      Note that quoted values are represented as constants symbols
+      (Symbol.is_constant == True).
+    """
+    if expr.__class__ is not tuple:
+        return sc_expr_str_fn(expr)
+
+    if expr[0] is AND:
+        return "{} && {}".format(_parenthesize(expr[1], OR, sc_expr_str_fn),
+                                 _parenthesize(expr[2], OR, sc_expr_str_fn))
+
+    if expr[0] is OR:
+        # This turns A && B || C && D into "(A && B) || (C && D)", which is
+        # redundant, but more readable
+        return "{} || {}".format(_parenthesize(expr[1], AND, sc_expr_str_fn),
+                                 _parenthesize(expr[2], AND, sc_expr_str_fn))
+
+    if expr[0] is NOT:
+        if expr[1].__class__ is tuple:
+            return "!({})".format(expr_str(expr[1], sc_expr_str_fn))
+        return "!" + sc_expr_str_fn(expr[1])  # Symbol
+
+    # Relation
+    #
+    # Relation operands are always symbols (quoted strings are constant
+    # symbols)
+    return "{} {} {}".format(sc_expr_str_fn(expr[1]), REL_TO_STR[expr[0]],
+                             sc_expr_str_fn(expr[2]))
+
+
+def expr_items(expr):
+    """
+    Returns a set() of all items (symbols and choices) that appear in the
+    expression 'expr'.
+
+    Passing subexpressions of expressions to this function works as expected.
+    """
+    res = set()
+
+    def rec(subexpr):
+        if subexpr.__class__ is tuple:
+            # AND, OR, NOT, or relation
+
+            rec(subexpr[1])
+
+            # NOTs only have a single operand
+            if subexpr[0] is not NOT:
+                rec(subexpr[2])
+
+        else:
+            # Symbol or choice
+            res.add(subexpr)
+
+    rec(expr)
+    return res
+
+
+def split_expr(expr, op):
+    """
+    Returns a list containing the top-level AND or OR operands in the
+    expression 'expr', in the same (left-to-right) order as they appear in
+    the expression.
+
+    This can be handy e.g. for splitting (weak) reverse dependencies
+    from 'select' and 'imply' into individual selects/implies.
+
+    op:
+      Either AND to get AND operands, or OR to get OR operands.
+
+      (Having this as an operand might be more future-safe than having two
+      hardcoded functions.)
+
+
+    Pseudo-code examples:
+
+      split_expr( A                    , OR  )  ->  [A]
+      split_expr( A && B               , OR  )  ->  [A && B]
+      split_expr( A || B               , OR  )  ->  [A, B]
+      split_expr( A || B               , AND )  ->  [A || B]
+      split_expr( A || B || (C && D)   , OR  )  ->  [A, B, C && D]
+
+      # Second || is not at the top level
+      split_expr( A || (B && (C || D)) , OR )  ->  [A, B && (C || D)]
+
+      # Parentheses don't matter as long as we stay at the top level (don't
+      # encounter any non-'op' nodes)
+      split_expr( (A || B) || C        , OR )  ->  [A, B, C]
+      split_expr( A || (B || C)        , OR )  ->  [A, B, C]
+    """
+    res = []
+
+    def rec(subexpr):
+        if subexpr.__class__ is tuple and subexpr[0] is op:
+            rec(subexpr[1])
+            rec(subexpr[2])
+        else:
+            res.append(subexpr)
+
+    rec(expr)
+    return res
+
+
+def escape(s):
+    r"""
+    Escapes the string 's' in the same fashion as is done for display in
+    Kconfig format and when writing strings to a .config file. " and \ are
+    replaced by \" and \\, respectively.
+    """
+    # \ must be escaped before " to avoid double escaping
+    return s.replace("\\", r"\\").replace('"', r'\"')
+
+
+def unescape(s):
+    r"""
+    Unescapes the string 's'. \ followed by any character is replaced with just
+    that character. Used internally when reading .config files.
+    """
+    return _unescape_sub(r"\1", s)
+
+# unescape() helper
+_unescape_sub = re.compile(r"\\(.)").sub
+
+
+def standard_kconfig(description=None):
+    """
+    Argument parsing helper for tools that take a single optional Kconfig file
+    argument (default: Kconfig). Returns the Kconfig instance for the parsed
+    configuration. Uses argparse internally.
+
+    Exits with sys.exit() (which raises SystemExit) on errors.
+
+    description (default: None):
+      The 'description' passed to argparse.ArgumentParser().
+      argparse.RawDescriptionHelpFormatter is used, so formatting is preserved.
+    """
+    import argparse
+
+    parser = argparse.ArgumentParser(
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        description=description)
+
+    parser.add_argument(
+        "kconfig",
+        metavar="KCONFIG",
+        default="Kconfig",
+        nargs="?",
+        help="Top-level Kconfig file (default: Kconfig)")
+
+    return Kconfig(parser.parse_args().kconfig, suppress_traceback=True)
+
+
+def standard_config_filename():
+    """
+    Helper for tools. Returns the value of KCONFIG_CONFIG (which specifies the
+    .config file to load/save) if it is set, and ".config" otherwise.
+
+    Calling load_config() with filename=None might give the behavior you want,
+    without having to use this function.
+    """
+    return os.getenv("KCONFIG_CONFIG", ".config")
+
+
+def load_allconfig(kconf, filename):
+    """
+    Use Kconfig.load_allconfig() instead, which was added in Kconfiglib 13.4.0.
+    Supported for backwards compatibility. Might be removed at some point after
+    a long period of deprecation warnings.
+    """
+    allconfig = os.getenv("KCONFIG_ALLCONFIG")
+    if allconfig is None:
+        return
+
+    def std_msg(e):
+        # "Upcasts" a _KconfigIOError to an IOError, removing the custom
+        # __str__() message. The standard message is better here.
+        #
+        # This might also convert an OSError to an IOError in obscure cases,
+        # but it's probably not a big deal. The distinction is shaky (see
+        # PEP-3151).
+        return IOError(e.errno, e.strerror, e.filename)
+
+    old_warn_assign_override = kconf.warn_assign_override
+    old_warn_assign_redun = kconf.warn_assign_redun
+    kconf.warn_assign_override = kconf.warn_assign_redun = False
+
+    if allconfig in ("", "1"):
+        try:
+            print(kconf.load_config(filename, False))
+        except EnvironmentError as e1:
+            try:
+                print(kconf.load_config("all.config", False))
+            except EnvironmentError as e2:
+                sys.exit("error: KCONFIG_ALLCONFIG is set, but neither {} "
+                         "nor all.config could be opened: {}, {}"
+                         .format(filename, std_msg(e1), std_msg(e2)))
+    else:
+        try:
+            print(kconf.load_config(allconfig, False))
+        except EnvironmentError as e:
+            sys.exit("error: KCONFIG_ALLCONFIG is set to '{}', which "
+                     "could not be opened: {}"
+                     .format(allconfig, std_msg(e)))
+
+    kconf.warn_assign_override = old_warn_assign_override
+    kconf.warn_assign_redun = old_warn_assign_redun
+
+
+#
+# Internal functions
+#
+
+
+def _visibility(sc):
+    # Symbols and Choices have a "visibility" that acts as an upper bound on
+    # the values a user can set for them, corresponding to the visibility in
+    # e.g. 'make menuconfig'. This function calculates the visibility for the
+    # Symbol or Choice 'sc' -- the logic is nearly identical.
+
+    vis = 0
+
+    for node in sc.nodes:
+        if node.prompt:
+            vis = max(vis, expr_value(node.prompt[1]))
+
+    if sc.__class__ is Symbol and sc.choice:
+        if sc.choice.orig_type is TRISTATE and \
+           sc.orig_type is not TRISTATE and sc.choice.tri_value != 2:
+            # Non-tristate choice symbols are only visible in y mode
+            return 0
+
+        if sc.orig_type is TRISTATE and vis == 1 and sc.choice.tri_value == 2:
+            # Choice symbols with m visibility are not visible in y mode
+            return 0
+
+    # Promote m to y if we're dealing with a non-tristate (possibly due to
+    # modules being disabled)
+    if vis == 1 and sc.type is not TRISTATE:
+        return 2
+
+    return vis
+
+
+def _depend_on(sc, expr):
+    # Adds 'sc' (symbol or choice) as a "dependee" to all symbols in 'expr'.
+    # Constant symbols in 'expr' are skipped as they can never change value
+    # anyway.
+
+    if expr.__class__ is tuple:
+        # AND, OR, NOT, or relation
+
+        _depend_on(sc, expr[1])
+
+        # NOTs only have a single operand
+        if expr[0] is not NOT:
+            _depend_on(sc, expr[2])
+
+    elif not expr.is_constant:
+        # Non-constant symbol, or choice
+        expr._dependents.add(sc)
+
+
+def _parenthesize(expr, type_, sc_expr_str_fn):
+    # expr_str() helper. Adds parentheses around expressions of type 'type_'.
+
+    if expr.__class__ is tuple and expr[0] is type_:
+        return "({})".format(expr_str(expr, sc_expr_str_fn))
+    return expr_str(expr, sc_expr_str_fn)
+
+
+def _ordered_unique(lst):
+    # Returns 'lst' with any duplicates removed, preserving order. This hacky
+    # version seems to be a common idiom. It relies on short-circuit evaluation
+    # and set.add() returning None, which is falsy.
+
+    seen = set()
+    seen_add = seen.add
+    return [x for x in lst if x not in seen and not seen_add(x)]
+
+
+def _is_base_n(s, n):
+    try:
+        int(s, n)
+        return True
+    except ValueError:
+        return False
+
+
+def _strcmp(s1, s2):
+    # strcmp()-alike that returns -1, 0, or 1
+
+    return (s1 > s2) - (s1 < s2)
+
+
+def _sym_to_num(sym):
+    # expr_value() helper for converting a symbol to a number. Raises
+    # ValueError for symbols that can't be converted.
+
+    # For BOOL and TRISTATE, n/m/y count as 0/1/2. This mirrors 9059a3493ef
+    # ("kconfig: fix relational operators for bool and tristate symbols") in
+    # the C implementation.
+    return sym.tri_value if sym.orig_type in _BOOL_TRISTATE else \
+           int(sym.str_value, _TYPE_TO_BASE[sym.orig_type])
+
+
+def _touch_dep_file(path, sym_name):
+    # If sym_name is MY_SYM_NAME, touches my/sym/name.h. See the sync_deps()
+    # docstring.
+
+    sym_path = path + os.sep + sym_name.lower().replace("_", os.sep) + ".h"
+    sym_path_dir = dirname(sym_path)
+    if not exists(sym_path_dir):
+        os.makedirs(sym_path_dir, 0o755)
+
+    # A kind of truncating touch, mirroring the C tools
+    os.close(os.open(
+        sym_path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o644))
+
+
+def _save_old(path):
+    # See write_config()
+
+    def copy(src, dst):
+        # Import as needed, to save some startup time
+        import shutil
+        shutil.copyfile(src, dst)
+
+    if islink(path):
+        # Preserve symlinks
+        copy_fn = copy
+    elif hasattr(os, "replace"):
+        # Python 3 (3.3+) only. Best choice when available, because it
+        # removes <filename>.old on both *nix and Windows.
+        copy_fn = os.replace
+    elif os.name == "posix":
+        # Removes <filename>.old on POSIX systems
+        copy_fn = os.rename
+    else:
+        # Fall back on copying
+        copy_fn = copy
+
+    try:
+        copy_fn(path, path + ".old")
+    except Exception:
+        # Ignore errors from 'path' missing as well as other errors.
+        # <filename>.old file is usually more of a nice-to-have, and not worth
+        # erroring out over e.g. if <filename>.old happens to be a directory or
+        # <filename> is something like /dev/null.
+        pass
+
+
+def _locs(sc):
+    # Symbol/Choice.name_and_loc helper. Returns the "(defined at ...)" part of
+    # the string. 'sc' is a Symbol or Choice.
+
+    if sc.nodes:
+        return "(defined at {})".format(
+            ", ".join("{0.filename}:{0.linenr}".format(node)
+                      for node in sc.nodes))
+
+    return "(undefined)"
+
+
+# Menu manipulation
+
+
+def _expr_depends_on(expr, sym):
+    # Reimplementation of expr_depends_symbol() from mconf.c. Used to determine
+    # if a submenu should be implicitly created. This also influences which
+    # items inside choice statements are considered choice items.
+
+    if expr.__class__ is not tuple:
+        return expr is sym
+
+    if expr[0] in _EQUAL_UNEQUAL:
+        # Check for one of the following:
+        # sym = m/y, m/y = sym, sym != n, n != sym
+
+        left, right = expr[1:]
+
+        if right is sym:
+            left, right = right, left
+        elif left is not sym:
+            return False
+
+        return (expr[0] is EQUAL and right is sym.kconfig.m or
+                                     right is sym.kconfig.y) or \
+               (expr[0] is UNEQUAL and right is sym.kconfig.n)
+
+    return expr[0] is AND and \
+           (_expr_depends_on(expr[1], sym) or
+            _expr_depends_on(expr[2], sym))
+
+
+def _auto_menu_dep(node1, node2):
+    # Returns True if node2 has an "automatic menu dependency" on node1. If
+    # node2 has a prompt, we check its condition. Otherwise, we look directly
+    # at node2.dep.
+
+    return _expr_depends_on(node2.prompt[1] if node2.prompt else node2.dep,
+                            node1.item)
+
+
+def _flatten(node):
+    # "Flattens" menu nodes without prompts (e.g. 'if' nodes and non-visible
+    # symbols with children from automatic menu creation) so that their
+    # children appear after them instead. This gives a clean menu structure
+    # with no unexpected "jumps" in the indentation.
+    #
+    # Do not flatten promptless choices (which can appear "legitimately" if a
+    # named choice is defined in multiple locations to add on symbols). It
+    # looks confusing, and the menuconfig already shows all choice symbols if
+    # you enter the choice at some location with a prompt.
+
+    while node:
+        if node.list and not node.prompt and \
+           node.item.__class__ is not Choice:
+
+            last_node = node.list
+            while 1:
+                last_node.parent = node.parent
+                if not last_node.next:
+                    break
+                last_node = last_node.next
+
+            last_node.next = node.next
+            node.next = node.list
+            node.list = None
+
+        node = node.next
+
+
+def _remove_ifs(node):
+    # Removes 'if' nodes (which can be recognized by MenuNode.item being None),
+    # which are assumed to already have been flattened. The C implementation
+    # doesn't bother to do this, but we expose the menu tree directly, and it
+    # makes it nicer to work with.
+
+    cur = node.list
+    while cur and not cur.item:
+        cur = cur.next
+
+    node.list = cur
+
+    while cur:
+        next = cur.next
+        while next and not next.item:
+            next = next.next
+
+        # Equivalent to
+        #
+        #   cur.next = next
+        #   cur = next
+        #
+        # due to tricky Python semantics. The order matters.
+        cur.next = cur = next
+
+
+def _finalize_choice(node):
+    # Finalizes a choice, marking each symbol whose menu node has the choice as
+    # the parent as a choice symbol, and automatically determining types if not
+    # specified.
+
+    choice = node.item
+
+    cur = node.list
+    while cur:
+        if cur.item.__class__ is Symbol:
+            cur.item.choice = choice
+            choice.syms.append(cur.item)
+        cur = cur.next
+
+    # If no type is specified for the choice, its type is that of
+    # the first choice item with a specified type
+    if not choice.orig_type:
+        for item in choice.syms:
+            if item.orig_type:
+                choice.orig_type = item.orig_type
+                break
+
+    # Each choice item of UNKNOWN type gets the type of the choice
+    for sym in choice.syms:
+        if not sym.orig_type:
+            sym.orig_type = choice.orig_type
+
+
+def _check_dep_loop_sym(sym, ignore_choice):
+    # Detects dependency loops using depth-first search on the dependency graph
+    # (which is calculated earlier in Kconfig._build_dep()).
+    #
+    # Algorithm:
+    #
+    #  1. Symbols/choices start out with _visited = 0, meaning unvisited.
+    #
+    #  2. When a symbol/choice is first visited, _visited is set to 1, meaning
+    #     "visited, potentially part of a dependency loop". The recursive
+    #     search then continues from the symbol/choice.
+    #
+    #  3. If we run into a symbol/choice X with _visited already set to 1,
+    #     there's a dependency loop. The loop is found on the call stack by
+    #     recording symbols while returning ("on the way back") until X is seen
+    #     again.
+    #
+    #  4. Once a symbol/choice and all its dependencies (or dependents in this
+    #     case) have been checked recursively without detecting any loops, its
+    #     _visited is set to 2, meaning "visited, not part of a dependency
+    #     loop".
+    #
+    #     This saves work if we run into the symbol/choice again in later calls
+    #     to _check_dep_loop_sym(). We just return immediately.
+    #
+    # Choices complicate things, as every choice symbol depends on every other
+    # choice symbol in a sense. When a choice is "entered" via a choice symbol
+    # X, we visit all choice symbols from the choice except X, and prevent
+    # immediately revisiting the choice with a flag (ignore_choice).
+    #
+    # Maybe there's a better way to handle this (different flags or the
+    # like...)
+
+    if not sym._visited:
+        # sym._visited == 0, unvisited
+
+        sym._visited = 1
+
+        for dep in sym._dependents:
+            # Choices show up in Symbol._dependents when the choice has the
+            # symbol in a 'prompt' or 'default' condition (e.g.
+            # 'default ... if SYM').
+            #
+            # Since we aren't entering the choice via a choice symbol, all
+            # choice symbols need to be checked, hence the None.
+            loop = _check_dep_loop_choice(dep, None) \
+                   if dep.__class__ is Choice \
+                   else _check_dep_loop_sym(dep, False)
+
+            if loop:
+                # Dependency loop found
+                return _found_dep_loop(loop, sym)
+
+        if sym.choice and not ignore_choice:
+            loop = _check_dep_loop_choice(sym.choice, sym)
+            if loop:
+                # Dependency loop found
+                return _found_dep_loop(loop, sym)
+
+        # The symbol is not part of a dependency loop
+        sym._visited = 2
+
+        # No dependency loop found
+        return None
+
+    if sym._visited == 2:
+        # The symbol was checked earlier and is already known to not be part of
+        # a dependency loop
+        return None
+
+    # sym._visited == 1, found a dependency loop. Return the symbol as the
+    # first element in it.
+    return (sym,)
+
+
+def _check_dep_loop_choice(choice, skip):
+    if not choice._visited:
+        # choice._visited == 0, unvisited
+
+        choice._visited = 1
+
+        # Check for loops involving choice symbols. If we came here via a
+        # choice symbol, skip that one, as we'd get a false positive
+        # '<sym FOO> -> <choice> -> <sym FOO>' loop otherwise.
+        for sym in choice.syms:
+            if sym is not skip:
+                # Prevent the choice from being immediately re-entered via the
+                # "is a choice symbol" path by passing True
+                loop = _check_dep_loop_sym(sym, True)
+                if loop:
+                    # Dependency loop found
+                    return _found_dep_loop(loop, choice)
+
+        # The choice is not part of a dependency loop
+        choice._visited = 2
+
+        # No dependency loop found
+        return None
+
+    if choice._visited == 2:
+        # The choice was checked earlier and is already known to not be part of
+        # a dependency loop
+        return None
+
+    # choice._visited == 1, found a dependency loop. Return the choice as the
+    # first element in it.
+    return (choice,)
+
+
+def _found_dep_loop(loop, cur):
+    # Called "on the way back" when we know we have a loop
+
+    # Is the symbol/choice 'cur' where the loop started?
+    if cur is not loop[0]:
+        # Nope, it's just a part of the loop
+        return loop + (cur,)
+
+    # Yep, we have the entire loop. Throw an exception that shows it.
+
+    msg = "\nDependency loop\n" \
+            "===============\n\n"
+
+    for item in loop:
+        if item is not loop[0]:
+            msg += "...depends on "
+            if item.__class__ is Symbol and item.choice:
+                msg += "the choice symbol "
+
+        msg += "{}, with definition...\n\n{}\n\n" \
+               .format(item.name_and_loc, item)
+
+        # Small wart: Since we reuse the already calculated
+        # Symbol/Choice._dependents sets for recursive dependency detection, we
+        # lose information on whether a dependency came from a 'select'/'imply'
+        # condition or e.g. a 'depends on'.
+        #
+        # This might cause selecting symbols to "disappear". For example,
+        # a symbol B having 'select A if C' gives a direct dependency from A to
+        # C, since it corresponds to a reverse dependency of B && C.
+        #
+        # Always print reverse dependencies for symbols that have them to make
+        # sure information isn't lost. I wonder if there's some neat way to
+        # improve this.
+
+        if item.__class__ is Symbol:
+            if item.rev_dep is not item.kconfig.n:
+                msg += "(select-related dependencies: {})\n\n" \
+                       .format(expr_str(item.rev_dep))
+
+            if item.weak_rev_dep is not item.kconfig.n:
+                msg += "(imply-related dependencies: {})\n\n" \
+                       .format(expr_str(item.rev_dep))
+
+    msg += "...depends again on " + loop[0].name_and_loc
+
+    raise KconfigError(msg)
+
+
+def _decoding_error(e, filename, macro_linenr=None):
+    # Gives the filename and context for UnicodeDecodeError's, which are a pain
+    # to debug otherwise. 'e' is the UnicodeDecodeError object.
+    #
+    # If the decoding error is for the output of a $(shell,...) command,
+    # macro_linenr holds the line number where it was run (the exact line
+    # number isn't available for decoding errors in files).
+
+    raise KconfigError(
+        "\n"
+        "Malformed {} in {}\n"
+        "Context: {}\n"
+        "Problematic data: {}\n"
+        "Reason: {}".format(
+            e.encoding,
+            "'{}'".format(filename) if macro_linenr is None else
+                "output from macro at {}:{}".format(filename, macro_linenr),
+            e.object[max(e.start - 40, 0):e.end + 40],
+            e.object[e.start:e.end],
+            e.reason))
+
+
+def _warn_verbose_deprecated(fn_name):
+    sys.stderr.write(
+        "Deprecation warning: {0}()'s 'verbose' argument has no effect. Since "
+        "Kconfiglib 12.0.0, the message is returned from {0}() instead, "
+        "and is always generated. Do e.g. print(kconf.{0}()) if you want to "
+        "want to show a message like \"Loaded configuration '.config'\" on "
+        "stdout. The old API required ugly hacks to reuse messages in "
+        "configuration interfaces.\n".format(fn_name))
+
+
+# Predefined preprocessor functions
+
+
+def _filename_fn(kconf, _):
+    return kconf.filename
+
+
+def _lineno_fn(kconf, _):
+    return str(kconf.linenr)
+
+
+def _info_fn(kconf, _, msg):
+    print("{}:{}: {}".format(kconf.filename, kconf.linenr, msg))
+
+    return ""
+
+
+def _warning_if_fn(kconf, _, cond, msg):
+    if cond == "y":
+        kconf._warn(msg, kconf.filename, kconf.linenr)
+
+    return ""
+
+
+def _error_if_fn(kconf, _, cond, msg):
+    if cond == "y":
+        raise KconfigError("{}:{}: {}".format(
+            kconf.filename, kconf.linenr, msg))
+
+    return ""
+
+
+def _shell_fn(kconf, _, command):
+    import subprocess  # Only import as needed, to save some startup time
+
+    stdout, stderr = subprocess.Popen(
+        command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+    ).communicate()
+
+    if not _IS_PY2:
+        try:
+            stdout = stdout.decode(kconf._encoding)
+            stderr = stderr.decode(kconf._encoding)
+        except UnicodeDecodeError as e:
+            _decoding_error(e, kconf.filename, kconf.linenr)
+
+    if stderr:
+        kconf._warn("'{}' wrote to stderr: {}".format(
+                        command, "\n".join(stderr.splitlines())),
+                    kconf.filename, kconf.linenr)
+
+    # Universal newlines with splitlines() (to prevent e.g. stray \r's in
+    # command output on Windows), trailing newline removal, and
+    # newline-to-space conversion.
+    #
+    # On Python 3 versions before 3.6, it's not possible to specify the
+    # encoding when passing universal_newlines=True to Popen() (the 'encoding'
+    # parameter was added in 3.6), so we do this manual version instead.
+    return "\n".join(stdout.splitlines()).rstrip("\n").replace("\n", " ")
+
+#
+# Global constants
+#
+
+TRI_TO_STR = {
+    0: "n",
+    1: "m",
+    2: "y",
+}
+
+STR_TO_TRI = {
+    "n": 0,
+    "m": 1,
+    "y": 2,
+}
+
+# Constant representing that there's no cached choice selection. This is
+# distinct from a cached None (no selection). Any object that's not None or a
+# Symbol will do. We test this with 'is'.
+_NO_CACHED_SELECTION = 0
+
+# Are we running on Python 2?
+_IS_PY2 = sys.version_info[0] < 3
+
+try:
+    _UNAME_RELEASE = os.uname()[2]
+except AttributeError:
+    # Only import as needed, to save some startup time
+    import platform
+    _UNAME_RELEASE = platform.uname()[2]
+
+# The token and type constants below are safe to test with 'is', which is a bit
+# faster (~30% faster on my machine, and a few % faster for total parsing
+# time), even without assuming Python's small integer optimization (which
+# caches small integer objects). The constants end up pointing to unique
+# integer objects, and since we consistently refer to them via the names below,
+# we always get the same object.
+#
+# Client code should use == though.
+
+# Tokens, with values 1, 2, ... . Avoiding 0 simplifies some checks by making
+# all tokens except empty strings truthy.
+(
+    _T_ALLNOCONFIG_Y,
+    _T_AND,
+    _T_BOOL,
+    _T_CHOICE,
+    _T_CLOSE_PAREN,
+    _T_COMMENT,
+    _T_CONFIG,
+    _T_DEFAULT,
+    _T_DEFCONFIG_LIST,
+    _T_DEF_BOOL,
+    _T_DEF_HEX,
+    _T_DEF_INT,
+    _T_DEF_STRING,
+    _T_DEF_TRISTATE,
+    _T_DEPENDS,
+    _T_ENDCHOICE,
+    _T_ENDIF,
+    _T_ENDMENU,
+    _T_ENV,
+    _T_EQUAL,
+    _T_GREATER,
+    _T_GREATER_EQUAL,
+    _T_HELP,
+    _T_HEX,
+    _T_IF,
+    _T_IMPLY,
+    _T_INT,
+    _T_LESS,
+    _T_LESS_EQUAL,
+    _T_MAINMENU,
+    _T_MENU,
+    _T_MENUCONFIG,
+    _T_MODULES,
+    _T_NOT,
+    _T_ON,
+    _T_OPEN_PAREN,
+    _T_OPTION,
+    _T_OPTIONAL,
+    _T_OR,
+    _T_ORSOURCE,
+    _T_OSOURCE,
+    _T_PROMPT,
+    _T_RANGE,
+    _T_RSOURCE,
+    _T_SELECT,
+    _T_SOURCE,
+    _T_STRING,
+    _T_TRISTATE,
+    _T_UNEQUAL,
+    _T_VISIBLE,
+) = range(1, 51)
+
+# Keyword to token map, with the get() method assigned directly as a small
+# optimization
+_get_keyword = {
+    "---help---":     _T_HELP,
+    "allnoconfig_y":  _T_ALLNOCONFIG_Y,
+    "bool":           _T_BOOL,
+    "boolean":        _T_BOOL,
+    "choice":         _T_CHOICE,
+    "comment":        _T_COMMENT,
+    "config":         _T_CONFIG,
+    "def_bool":       _T_DEF_BOOL,
+    "def_hex":        _T_DEF_HEX,
+    "def_int":        _T_DEF_INT,
+    "def_string":     _T_DEF_STRING,
+    "def_tristate":   _T_DEF_TRISTATE,
+    "default":        _T_DEFAULT,
+    "defconfig_list": _T_DEFCONFIG_LIST,
+    "depends":        _T_DEPENDS,
+    "endchoice":      _T_ENDCHOICE,
+    "endif":          _T_ENDIF,
+    "endmenu":        _T_ENDMENU,
+    "env":            _T_ENV,
+    "grsource":       _T_ORSOURCE,  # Backwards compatibility
+    "gsource":        _T_OSOURCE,   # Backwards compatibility
+    "help":           _T_HELP,
+    "hex":            _T_HEX,
+    "if":             _T_IF,
+    "imply":          _T_IMPLY,
+    "int":            _T_INT,
+    "mainmenu":       _T_MAINMENU,
+    "menu":           _T_MENU,
+    "menuconfig":     _T_MENUCONFIG,
+    "modules":        _T_MODULES,
+    "on":             _T_ON,
+    "option":         _T_OPTION,
+    "optional":       _T_OPTIONAL,
+    "orsource":       _T_ORSOURCE,
+    "osource":        _T_OSOURCE,
+    "prompt":         _T_PROMPT,
+    "range":          _T_RANGE,
+    "rsource":        _T_RSOURCE,
+    "select":         _T_SELECT,
+    "source":         _T_SOURCE,
+    "string":         _T_STRING,
+    "tristate":       _T_TRISTATE,
+    "visible":        _T_VISIBLE,
+}.get
+
+# The constants below match the value of the corresponding tokens to remove the
+# need for conversion
+
+# Node types
+MENU    = _T_MENU
+COMMENT = _T_COMMENT
+
+# Expression types
+AND           = _T_AND
+OR            = _T_OR
+NOT           = _T_NOT
+EQUAL         = _T_EQUAL
+UNEQUAL       = _T_UNEQUAL
+LESS          = _T_LESS
+LESS_EQUAL    = _T_LESS_EQUAL
+GREATER       = _T_GREATER
+GREATER_EQUAL = _T_GREATER_EQUAL
+
+REL_TO_STR = {
+    EQUAL:         "=",
+    UNEQUAL:       "!=",
+    LESS:          "<",
+    LESS_EQUAL:    "<=",
+    GREATER:       ">",
+    GREATER_EQUAL: ">=",
+}
+
+# Symbol/choice types. UNKNOWN is 0 (falsy) to simplify some checks.
+# Client code shouldn't rely on it though, as it was non-zero in
+# older versions.
+UNKNOWN  = 0
+BOOL     = _T_BOOL
+TRISTATE = _T_TRISTATE
+STRING   = _T_STRING
+INT      = _T_INT
+HEX      = _T_HEX
+
+TYPE_TO_STR = {
+    UNKNOWN:  "unknown",
+    BOOL:     "bool",
+    TRISTATE: "tristate",
+    STRING:   "string",
+    INT:      "int",
+    HEX:      "hex",
+}
+
+# Used in comparisons. 0 means the base is inferred from the format of the
+# string.
+_TYPE_TO_BASE = {
+    HEX:      16,
+    INT:      10,
+    STRING:   0,
+    UNKNOWN:  0,
+}
+
+# def_bool -> BOOL, etc.
+_DEF_TOKEN_TO_TYPE = {
+    _T_DEF_BOOL:     BOOL,
+    _T_DEF_HEX:      HEX,
+    _T_DEF_INT:      INT,
+    _T_DEF_STRING:   STRING,
+    _T_DEF_TRISTATE: TRISTATE,
+}
+
+# Tokens after which strings are expected. This is used to tell strings from
+# constant symbol references during tokenization, both of which are enclosed in
+# quotes.
+#
+# Identifier-like lexemes ("missing quotes") are also treated as strings after
+# these tokens. _T_CHOICE is included to avoid symbols being registered for
+# named choices.
+_STRING_LEX = frozenset({
+    _T_BOOL,
+    _T_CHOICE,
+    _T_COMMENT,
+    _T_HEX,
+    _T_INT,
+    _T_MAINMENU,
+    _T_MENU,
+    _T_ORSOURCE,
+    _T_OSOURCE,
+    _T_PROMPT,
+    _T_RSOURCE,
+    _T_SOURCE,
+    _T_STRING,
+    _T_TRISTATE,
+})
+
+# Various sets for quick membership tests. Gives a single global lookup and
+# avoids creating temporary dicts/tuples.
+
+_TYPE_TOKENS = frozenset({
+    _T_BOOL,
+    _T_TRISTATE,
+    _T_INT,
+    _T_HEX,
+    _T_STRING,
+})
+
+_SOURCE_TOKENS = frozenset({
+    _T_SOURCE,
+    _T_RSOURCE,
+    _T_OSOURCE,
+    _T_ORSOURCE,
+})
+
+_REL_SOURCE_TOKENS = frozenset({
+    _T_RSOURCE,
+    _T_ORSOURCE,
+})
+
+# Obligatory (non-optional) sources
+_OBL_SOURCE_TOKENS = frozenset({
+    _T_SOURCE,
+    _T_RSOURCE,
+})
+
+_BOOL_TRISTATE = frozenset({
+    BOOL,
+    TRISTATE,
+})
+
+_BOOL_TRISTATE_UNKNOWN = frozenset({
+    BOOL,
+    TRISTATE,
+    UNKNOWN,
+})
+
+_INT_HEX = frozenset({
+    INT,
+    HEX,
+})
+
+_SYMBOL_CHOICE = frozenset({
+    Symbol,
+    Choice,
+})
+
+_MENU_COMMENT = frozenset({
+    MENU,
+    COMMENT,
+})
+
+_EQUAL_UNEQUAL = frozenset({
+    EQUAL,
+    UNEQUAL,
+})
+
+_RELATIONS = frozenset({
+    EQUAL,
+    UNEQUAL,
+    LESS,
+    LESS_EQUAL,
+    GREATER,
+    GREATER_EQUAL,
+})
+
+# Helper functions for getting compiled regular expressions, with the needed
+# matching function returned directly as a small optimization.
+#
+# Use ASCII regex matching on Python 3. It's already the default on Python 2.
+
+
+def _re_match(regex):
+    return re.compile(regex, 0 if _IS_PY2 else re.ASCII).match
+
+
+def _re_search(regex):
+    return re.compile(regex, 0 if _IS_PY2 else re.ASCII).search
+
+
+# Various regular expressions used during parsing
+
+# The initial token on a line. Also eats leading and trailing whitespace, so
+# that we can jump straight to the next token (or to the end of the line if
+# there is only one token).
+#
+# This regex will also fail to match for empty lines and comment lines.
+#
+# '$' is included to detect preprocessor variable assignments with macro
+# expansions in the left-hand side.
+_command_match = _re_match(r"\s*([A-Za-z0-9_$-]+)\s*")
+
+# An identifier/keyword after the first token. Also eats trailing whitespace.
+# '$' is included to detect identifiers containing macro expansions.
+_id_keyword_match = _re_match(r"([A-Za-z0-9_$/.-]+)\s*")
+
+# A fragment in the left-hand side of a preprocessor variable assignment. These
+# are the portions between macro expansions ($(foo)). Macros are supported in
+# the LHS (variable name).
+_assignment_lhs_fragment_match = _re_match("[A-Za-z0-9_-]*")
+
+# The assignment operator and value (right-hand side) in a preprocessor
+# variable assignment
+_assignment_rhs_match = _re_match(r"\s*(=|:=|\+=)\s*(.*)")
+
+# Special characters/strings while expanding a macro ('(', ')', ',', and '$(')
+_macro_special_search = _re_search(r"\(|\)|,|\$\(")
+
+# Special characters/strings while expanding a string (quotes, '\', and '$(')
+_string_special_search = _re_search(r'"|\'|\\|\$\(')
+
+# Special characters/strings while expanding a symbol name. Also includes
+# end-of-line, in case the macro is the last thing on the line.
+_name_special_search = _re_search(r'[^A-Za-z0-9_$/.-]|\$\(|$')
+
+# A valid right-hand side for an assignment to a string symbol in a .config
+# file, including escaped characters. Extracts the contents.
+_conf_string_match = _re_match(r'"((?:[^\\"]|\\.)*)"')
diff --git a/scripts/Kconfiglib/listnewconfig.py b/scripts/Kconfiglib/listnewconfig.py
new file mode 100755
index 0000000..8276de1
--- /dev/null
+++ b/scripts/Kconfiglib/listnewconfig.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Lists all user-modifiable symbols that are not given a value in the
+configuration file. Usually, these are new symbols that have been added to the
+Kconfig files.
+
+The default configuration filename is '.config'. A different filename can be
+passed in the KCONFIG_CONFIG environment variable.
+"""
+from __future__ import print_function
+
+import argparse
+import sys
+
+from kconfiglib import Kconfig, BOOL, TRISTATE, INT, HEX, STRING, TRI_TO_STR
+
+
+def main():
+    parser = argparse.ArgumentParser(
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        description=__doc__)
+
+    parser.add_argument(
+        "--show-help", "-l",
+        action="store_true",
+        help="Show any help texts as well")
+
+    parser.add_argument(
+        "kconfig",
+        metavar="KCONFIG",
+        nargs="?",
+        default="Kconfig",
+        help="Top-level Kconfig file (default: Kconfig)")
+
+    args = parser.parse_args()
+
+    kconf = Kconfig(args.kconfig, suppress_traceback=True)
+    # Make it possible to filter this message out
+    print(kconf.load_config(), file=sys.stderr)
+
+    for sym in kconf.unique_defined_syms:
+        # Only show symbols that can be toggled. Choice symbols are a special
+        # case in that sym.assignable will be (2,) (length 1) for visible
+        # symbols in choices in y mode, but they can still be toggled by
+        # selecting some other symbol.
+        if sym.user_value is None and \
+           (len(sym.assignable) > 1 or
+            (sym.visibility and (sym.orig_type in (INT, HEX, STRING) or
+                                 sym.choice))):
+
+            # Don't reuse the 'config_string' format for bool/tristate symbols,
+            # to show n-valued symbols as 'CONFIG_FOO=n' instead of
+            # '# CONFIG_FOO is not set'. This matches the C tools.
+            if sym.orig_type in (BOOL, TRISTATE):
+                s = "{}{}={}\n".format(kconf.config_prefix, sym.name,
+                                       TRI_TO_STR[sym.tri_value])
+            else:
+                s = sym.config_string
+
+            print(s, end="")
+            if args.show_help:
+                for node in sym.nodes:
+                    if node.help is not None:
+                        # Indent by two spaces. textwrap.indent() is not
+                        # available in Python 2 (it's 3.3+).
+                        print("\n".join("  " + line
+                                        for line in node.help.split("\n")))
+                        break
+
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/Kconfiglib/makefile.patch b/scripts/Kconfiglib/makefile.patch
new file mode 100644
index 0000000..a617ebd
--- /dev/null
+++ b/scripts/Kconfiglib/makefile.patch
@@ -0,0 +1,48 @@
+From 93daf46f309b0c8f86149ef58c4906387d054c22 Mon Sep 17 00:00:00 2001
+From: Ulf Magnusson <ulfalizer at gmail.com>
+Date: Tue, 9 Jun 2015 13:01:34 +0200
+Subject: [PATCH] Kconfiglib scripts/kconfig/Makefile patch
+
+---
+ scripts/kconfig/Makefile | 29 +++++++++++++++++++++++++++++
+ 1 file changed, 29 insertions(+)
+
+diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile
+index 3f327e21f60e..8b7dd1292005 100644
+--- a/scripts/kconfig/Makefile
++++ b/scripts/kconfig/Makefile
+@@ -27,2 +27,31 @@ gconfig: $(obj)/gconf
+ 
++PHONY += scriptconfig iscriptconfig kmenuconfig guiconfig dumpvarsconfig
++
++PYTHONCMD ?= python
++kpython := PYTHONPATH=$(srctree)/Kconfiglib:$$PYTHONPATH $(PYTHONCMD)
++
++ifneq ($(filter scriptconfig,$(MAKECMDGOALS)),)
++ifndef SCRIPT
++$(error Use "make scriptconfig SCRIPT=<path to script> [SCRIPT_ARG=<argument>]")
++endif
++endif
++
++scriptconfig:
++	$(Q)$(kpython) $(SCRIPT) $(Kconfig) $(if $(SCRIPT_ARG),"$(SCRIPT_ARG)")
++
++iscriptconfig:
++	$(Q)$(kpython) -i -c \
++	  "import kconfiglib; \
++	   kconf = kconfiglib.Kconfig('$(Kconfig)'); \
++	   print('A Kconfig instance \'kconf\' for the architecture $(ARCH) has been created.')"
++
++kmenuconfig:
++	$(Q)$(kpython) $(srctree)/Kconfiglib/menuconfig.py $(Kconfig)
++
++guiconfig:
++	$(Q)$(kpython) $(srctree)/Kconfiglib/guiconfig.py $(Kconfig)
++
++dumpvarsconfig:
++	$(Q)$(kpython) $(srctree)/Kconfiglib/examples/dumpvars.py $(Kconfig)
++
+ menuconfig: $(obj)/mconf
+-- 
+2.20.1
+
diff --git a/scripts/Kconfiglib/menuconfig.py b/scripts/Kconfiglib/menuconfig.py
new file mode 100755
index 0000000..7e765d3
--- /dev/null
+++ b/scripts/Kconfiglib/menuconfig.py
@@ -0,0 +1,3278 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018-2019, Nordic Semiconductor ASA and Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Overview
+========
+
+A curses-based Python 2/3 menuconfig implementation. The interface should feel
+familiar to people used to mconf ('make menuconfig').
+
+Supports the same keys as mconf, and also supports a set of keybindings
+inspired by Vi:
+
+  J/K     : Down/Up
+  L       : Enter menu/Toggle item
+  H       : Leave menu
+  Ctrl-D/U: Page Down/Page Up
+  G/End   : Jump to end of list
+  g/Home  : Jump to beginning of list
+
+[Space] toggles values if possible, and enters menus otherwise. [Enter] works
+the other way around.
+
+The mconf feature where pressing a key jumps to a menu entry with that
+character in it in the current menu isn't supported. A jump-to feature for
+jumping directly to any symbol (including invisible symbols), choice, menu or
+comment (as in a Kconfig 'comment "Foo"') is available instead.
+
+A few different modes are available:
+
+  F: Toggle show-help mode, which shows the help text of the currently selected
+  item in the window at the bottom of the menu display. This is handy when
+  browsing through options.
+
+  C: Toggle show-name mode, which shows the symbol name before each symbol menu
+  entry
+
+  A: Toggle show-all mode, which shows all items, including currently invisible
+  items and items that lack a prompt. Invisible items are drawn in a different
+  style to make them stand out.
+
+
+Running
+=======
+
+menuconfig.py can be run either as a standalone executable or by calling the
+menuconfig() function with an existing Kconfig instance. The second option is a
+bit inflexible in that it will still load and save .config, etc.
+
+When run in standalone mode, the top-level Kconfig file to load can be passed
+as a command-line argument. With no argument, it defaults to "Kconfig".
+
+The KCONFIG_CONFIG environment variable specifies the .config file to load (if
+it exists) and save. If KCONFIG_CONFIG is unset, ".config" is used.
+
+When overwriting a configuration file, the old version is saved to
+<filename>.old (e.g. .config.old).
+
+$srctree is supported through Kconfiglib.
+
+
+Color schemes
+=============
+
+It is possible to customize the color scheme by setting the MENUCONFIG_STYLE
+environment variable. For example, setting it to 'aquatic' will enable an
+alternative, less yellow, more 'make menuconfig'-like color scheme, contributed
+by Mitja Horvat (pinkfluid).
+
+This is the current list of built-in styles:
+    - default       classic Kconfiglib theme with a yellow accent
+    - monochrome    colorless theme (uses only bold and standout) attributes,
+                    this style is used if the terminal doesn't support colors
+    - aquatic       blue-tinted style loosely resembling the lxdialog theme
+
+It is possible to customize the current style by changing colors of UI
+elements on the screen. This is the list of elements that can be stylized:
+
+    - path          Top row in the main display, with the menu path
+    - separator     Separator lines between windows. Also used for the top line
+                    in the symbol information display.
+    - list          List of items, e.g. the main display
+    - selection     Style for the selected item
+    - inv-list      Like list, but for invisible items. Used in show-all mode.
+    - inv-selection Like selection, but for invisible items. Used in show-all
+                    mode.
+    - help          Help text windows at the bottom of various fullscreen
+                    dialogs
+    - show-help     Window showing the help text in show-help mode
+    - frame         Frame around dialog boxes
+    - body          Body of dialog boxes
+    - edit          Edit box in pop-up dialogs
+    - jump-edit     Edit box in jump-to dialog
+    - text          Symbol information text
+
+The color definition is a comma separated list of attributes:
+
+    - fg:COLOR      Set the foreground/background colors. COLOR can be one of
+      * or *        the basic 16 colors (black, red, green, yellow, blue,
+    - bg:COLOR      magenta, cyan, white and brighter versions, for example,
+                    brightred). On terminals that support more than 8 colors,
+                    you can also directly put in a color number, e.g. fg:123
+                    (hexadecimal and octal constants are accepted as well).
+                    Colors outside the range -1..curses.COLORS-1 (which is
+                    terminal-dependent) are ignored (with a warning). The COLOR
+                    can be also specified using a RGB value in the HTML
+                    notation, for example #RRGGBB. If the terminal supports
+                    color changing, the color is rendered accurately.
+                    Otherwise, the visually nearest color is used.
+
+                    If the background or foreground color of an element is not
+                    specified, it defaults to -1, representing the default
+                    terminal foreground or background color.
+
+                    Note: On some terminals a bright version of the color
+                    implies bold.
+    - bold          Use bold text
+    - underline     Use underline text
+    - standout      Standout text attribute (reverse color)
+
+More often than not, some UI elements share the same color definition. In such
+cases the right value may specify an UI element from which the color definition
+will be copied. For example, "separator=help" will apply the current color
+definition for "help" to "separator".
+
+A keyword without the '=' is assumed to be a style template. The template name
+is looked up in the built-in styles list and the style definition is expanded
+in-place. With this, built-in styles can be used as basis for new styles.
+
+For example, take the aquatic theme and give it a red selection bar:
+
+MENUCONFIG_STYLE="aquatic selection=fg:white,bg:red"
+
+If there's an error in the style definition or if a missing style is assigned
+to, the assignment will be ignored, along with a warning being printed on
+stderr.
+
+The 'default' theme is always implicitly parsed first, so the following two
+settings have the same effect:
+
+    MENUCONFIG_STYLE="selection=fg:white,bg:red"
+    MENUCONFIG_STYLE="default selection=fg:white,bg:red"
+
+If the terminal doesn't support colors, the 'monochrome' theme is used, and
+MENUCONFIG_STYLE is ignored. The assumption is that the environment is broken
+somehow, and that the important thing is to get something usable.
+
+
+Other features
+==============
+
+  - Seamless terminal resizing
+
+  - No dependencies on *nix, as the 'curses' module is in the Python standard
+    library
+
+  - Unicode text entry
+
+  - Improved information screen compared to mconf:
+
+      * Expressions are split up by their top-level &&/|| operands to improve
+        readability
+
+      * Undefined symbols in expressions are pointed out
+
+      * Menus and comments have information displays
+
+      * Kconfig definitions are printed
+
+      * The include path is shown, listing the locations of the 'source'
+        statements that included the Kconfig file of the symbol (or other
+        item)
+
+
+Limitations
+===========
+
+Doesn't work out of the box on Windows, but can be made to work with
+
+    pip install windows-curses
+
+See the https://github.com/zephyrproject-rtos/windows-curses repository.
+"""
+from __future__ import print_function
+
+import os
+import sys
+
+_IS_WINDOWS = os.name == "nt"  # Are we running on Windows?
+
+try:
+    import curses
+except ImportError as e:
+    if not _IS_WINDOWS:
+        raise
+    sys.exit("""\
+menuconfig failed to import the standard Python 'curses' library. Try
+installing a package like windows-curses
+(https://github.com/zephyrproject-rtos/windows-curses) by running this command
+in cmd.exe:
+
+    pip install windows-curses
+
+Starting with Kconfiglib 13.0.0, windows-curses is no longer automatically
+installed when installing Kconfiglib via pip on Windows (because it breaks
+installation on MSYS2).
+
+Exception:
+{}: {}""".format(type(e).__name__, e))
+
+import errno
+import locale
+import re
+import textwrap
+
+from kconfiglib import Symbol, Choice, MENU, COMMENT, MenuNode, \
+                       BOOL, TRISTATE, STRING, INT, HEX, \
+                       AND, OR, \
+                       expr_str, expr_value, split_expr, \
+                       standard_sc_expr_str, \
+                       TRI_TO_STR, TYPE_TO_STR, \
+                       standard_kconfig, standard_config_filename
+
+
+#
+# Configuration variables
+#
+
+# If True, try to change LC_CTYPE to a UTF-8 locale if it is set to the C
+# locale (which implies ASCII). This fixes curses Unicode I/O issues on systems
+# with bad defaults. ncurses configures itself from the locale settings.
+#
+# Related PEP: https://www.python.org/dev/peps/pep-0538/
+_CHANGE_C_LC_CTYPE_TO_UTF8 = True
+
+# How many steps an implicit submenu will be indented. Implicit submenus are
+# created when an item depends on the symbol before it. Note that symbols
+# defined with 'menuconfig' create a separate menu instead of indenting.
+_SUBMENU_INDENT = 4
+
+# Number of steps for Page Up/Down to jump
+_PG_JUMP = 6
+
+# Height of the help window in show-help mode
+_SHOW_HELP_HEIGHT = 8
+
+# How far the cursor needs to be from the edge of the window before it starts
+# to scroll. Used for the main menu display, the information display, the
+# search display, and for text boxes.
+_SCROLL_OFFSET = 5
+
+# Minimum width of dialogs that ask for text input
+_INPUT_DIALOG_MIN_WIDTH = 30
+
+# Number of arrows pointing up/down to draw when a window is scrolled
+_N_SCROLL_ARROWS = 14
+
+# Lines of help text shown at the bottom of the "main" display
+_MAIN_HELP_LINES = """
+[Space/Enter] Toggle/enter  [ESC] Leave menu           [S] Save
+[O] Load                    [?] Symbol info            [/] Jump to symbol
+[F] Toggle show-help mode   [C] Toggle show-name mode  [A] Toggle show-all mode
+[Q] Quit (prompts for save) [D] Save minimal config (advanced)
+"""[1:-1].split("\n")
+
+# Lines of help text shown at the bottom of the information dialog
+_INFO_HELP_LINES = """
+[ESC/q] Return to menu      [/] Jump to symbol
+"""[1:-1].split("\n")
+
+# Lines of help text shown at the bottom of the search dialog
+_JUMP_TO_HELP_LINES = """
+Type text to narrow the search. Regexes are supported (via Python's 're'
+module). The up/down cursor keys step in the list. [Enter] jumps to the
+selected symbol. [ESC] aborts the search. Type multiple space-separated
+strings/regexes to find entries that match all of them. Type Ctrl-F to
+view the help of the selected item without leaving the dialog.
+"""[1:-1].split("\n")
+
+#
+# Styling
+#
+
+_STYLES = {
+    "default": """
+    path=fg:black,bg:white,bold
+    separator=fg:black,bg:yellow,bold
+    list=fg:black,bg:white
+    selection=fg:white,bg:blue,bold
+    inv-list=fg:red,bg:white
+    inv-selection=fg:red,bg:blue
+    help=path
+    show-help=list
+    frame=fg:black,bg:yellow,bold
+    body=fg:white,bg:black
+    edit=fg:white,bg:blue
+    jump-edit=edit
+    text=list
+    """,
+
+    # This style is forced on terminals that do no support colors
+    "monochrome": """
+    path=bold
+    separator=bold,standout
+    list=
+    selection=bold,standout
+    inv-list=bold
+    inv-selection=bold,standout
+    help=bold
+    show-help=
+    frame=bold,standout
+    body=
+    edit=standout
+    jump-edit=
+    text=
+    """,
+
+    # Blue-tinted style loosely resembling lxdialog
+    "aquatic": """
+    path=fg:white,bg:blue
+    separator=fg:white,bg:cyan
+    help=path
+    frame=fg:white,bg:cyan
+    body=fg:white,bg:blue
+    edit=fg:black,bg:white
+    """
+}
+
+_NAMED_COLORS = {
+    # Basic colors
+    "black":         curses.COLOR_BLACK,
+    "red":           curses.COLOR_RED,
+    "green":         curses.COLOR_GREEN,
+    "yellow":        curses.COLOR_YELLOW,
+    "blue":          curses.COLOR_BLUE,
+    "magenta":       curses.COLOR_MAGENTA,
+    "cyan":          curses.COLOR_CYAN,
+    "white":         curses.COLOR_WHITE,
+
+    # Bright versions
+    "brightblack":   curses.COLOR_BLACK + 8,
+    "brightred":     curses.COLOR_RED + 8,
+    "brightgreen":   curses.COLOR_GREEN + 8,
+    "brightyellow":  curses.COLOR_YELLOW + 8,
+    "brightblue":    curses.COLOR_BLUE + 8,
+    "brightmagenta": curses.COLOR_MAGENTA + 8,
+    "brightcyan":    curses.COLOR_CYAN + 8,
+    "brightwhite":   curses.COLOR_WHITE + 8,
+
+    # Aliases
+    "purple":        curses.COLOR_MAGENTA,
+    "brightpurple":  curses.COLOR_MAGENTA + 8,
+}
+
+
+def _rgb_to_6cube(rgb):
+    # Converts an 888 RGB color to a 3-tuple (nice in that it's hashable)
+    # representing the closest xterm 256-color 6x6x6 color cube color.
+    #
+    # The xterm 256-color extension uses a RGB color palette with components in
+    # the range 0-5 (a 6x6x6 cube). The catch is that the mapping is nonlinear.
+    # Index 0 in the 6x6x6 cube is mapped to 0, index 1 to 95, then 135, 175,
+    # etc., in increments of 40. See the links below:
+    #
+    #   https://commons.wikimedia.org/wiki/File:Xterm_256color_chart.svg
+    #   https://github.com/tmux/tmux/blob/master/colour.c
+
+    # 48 is the middle ground between 0 and 95.
+    return tuple(0 if x < 48 else int(round(max(1, (x - 55)/40))) for x in rgb)
+
+
+def _6cube_to_rgb(r6g6b6):
+    # Returns the 888 RGB color for a 666 xterm color cube index
+
+    return tuple(0 if x == 0 else 40*x + 55 for x in r6g6b6)
+
+
+def _rgb_to_gray(rgb):
+    # Converts an 888 RGB color to the index of an xterm 256-color grayscale
+    # color with approx. the same perceived brightness
+
+    # Calculate the luminance (gray intensity) of the color. See
+    #   https://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color
+    # and
+    #   https://www.w3.org/TR/AERT/#color-contrast
+    luma = 0.299*rgb[0] + 0.587*rgb[1] + 0.114*rgb[2]
+
+    # Closest index in the grayscale palette, which starts at RGB 0x080808,
+    # with stepping 0x0A0A0A
+    index = int(round((luma - 8)/10))
+
+    # Clamp the index to 0-23, corresponding to 232-255
+    return max(0, min(index, 23))
+
+
+def _gray_to_rgb(index):
+    # Convert a grayscale index to its closet single RGB component
+
+    return 3*(10*index + 8,)  # Returns a 3-tuple
+
+
+# Obscure Python: We never pass a value for rgb2index, and it keeps pointing to
+# the same dict. This avoids a global.
+def _alloc_rgb(rgb, rgb2index={}):
+    # Initialize a new entry in the xterm palette to the given RGB color,
+    # returning its index. If the color has already been initialized, the index
+    # of the existing entry is returned.
+    #
+    # ncurses is palette-based, so we need to overwrite palette entries to make
+    # new colors.
+    #
+    # The colors from 0 to 15 are user-defined, and there's no way to query
+    # their RGB values, so we better leave them untouched. Also leave any
+    # hypothetical colors above 255 untouched (though we're unlikely to
+    # allocate that many colors anyway).
+
+    if rgb in rgb2index:
+        return rgb2index[rgb]
+
+    # Many terminals allow the user to customize the first 16 colors. Avoid
+    # changing their values.
+    color_index = 16 + len(rgb2index)
+    if color_index >= 256:
+        _warn("Unable to allocate new RGB color ", rgb, ". Too many colors "
+              "allocated.")
+        return 0
+
+    # Map each RGB component from the range 0-255 to the range 0-1000, which is
+    # what curses uses
+    curses.init_color(color_index, *(int(round(1000*x/255)) for x in rgb))
+    rgb2index[rgb] = color_index
+
+    return color_index
+
+
+def _color_from_num(num):
+    # Returns the index of a color that looks like color 'num' in the xterm
+    # 256-color palette (but that might not be 'num', if we're redefining
+    # colors)
+
+    # - _alloc_rgb() won't touch the first 16 colors or any (hypothetical)
+    #   colors above 255, so we can always return them as-is
+    #
+    # - If the terminal doesn't support changing color definitions, or if
+    #   curses.COLORS < 256, _alloc_rgb() won't touch any color, and all colors
+    #   can be returned as-is
+    if num < 16 or num > 255 or not curses.can_change_color() or \
+       curses.COLORS < 256:
+        return num
+
+    # _alloc_rgb() might redefine colors, so emulate the xterm 256-color
+    # palette by allocating new colors instead of returning color numbers
+    # directly
+
+    if num < 232:
+        num -= 16
+        return _alloc_rgb(_6cube_to_rgb(((num//36)%6, (num//6)%6, num%6)))
+
+    return _alloc_rgb(_gray_to_rgb(num - 232))
+
+
+def _color_from_rgb(rgb):
+    # Returns the index of a color matching the 888 RGB color 'rgb'. The
+    # returned color might be an ~exact match or an approximation, depending on
+    # terminal capabilities.
+
+    # Calculates the Euclidean distance between two RGB colors
+    def dist(r1, r2): return sum((x - y)**2 for x, y in zip(r1, r2))
+
+    if curses.COLORS >= 256:
+        # Assume we're dealing with xterm's 256-color extension
+
+        if curses.can_change_color():
+            # Best case -- the terminal supports changing palette entries via
+            # curses.init_color(). Initialize an unused palette entry and
+            # return it.
+            return _alloc_rgb(rgb)
+
+        # Second best case -- pick between the xterm 256-color extension colors
+
+        # Closest 6-cube "color" color
+        c6 = _rgb_to_6cube(rgb)
+        # Closest gray color
+        gray = _rgb_to_gray(rgb)
+
+        if dist(rgb, _6cube_to_rgb(c6)) < dist(rgb, _gray_to_rgb(gray)):
+            # Use the "color" color from the 6x6x6 color palette. Calculate the
+            # color number from the 6-cube index triplet.
+            return 16 + 36*c6[0] + 6*c6[1] + c6[2]
+
+        # Use the color from the gray palette
+        return 232 + gray
+
+    # Terminal not in xterm 256-color mode. This is probably the best we can
+    # do, or is it? Submit patches. :)
+    min_dist = float('inf')
+    best = -1
+    for color in range(curses.COLORS):
+        # ncurses uses the range 0..1000. Scale that down to 0..255.
+        d = dist(rgb, tuple(int(round(255*c/1000))
+                            for c in curses.color_content(color)))
+        if d < min_dist:
+            min_dist = d
+            best = color
+
+    return best
+
+
+def _parse_style(style_str, parsing_default):
+    # Parses a string with '<element>=<style>' assignments. Anything not
+    # containing '=' is assumed to be a reference to a built-in style, which is
+    # treated as if all the assignments from the style were inserted at that
+    # point in the string.
+    #
+    # The parsing_default flag is set to True when we're implicitly parsing the
+    # 'default'/'monochrome' style, to prevent warnings.
+
+    for sline in style_str.split():
+        # Words without a "=" character represents a style template
+        if "=" in sline:
+            key, data = sline.split("=", 1)
+
+            # The 'default' style template is assumed to define all keys. We
+            # run _style_to_curses() for non-existing keys as well, so that we
+            # print warnings for errors to the right of '=' for those too.
+            if key not in _style and not parsing_default:
+                _warn("Ignoring non-existent style", key)
+
+            # If data is a reference to another key, copy its style
+            if data in _style:
+                _style[key] = _style[data]
+            else:
+                _style[key] = _style_to_curses(data)
+
+        elif sline in _STYLES:
+            # Recursively parse style template. Ignore styles that don't exist,
+            # for backwards/forwards compatibility.
+            _parse_style(_STYLES[sline], parsing_default)
+
+        else:
+            _warn("Ignoring non-existent style template", sline)
+
+# Dictionary mapping element types to the curses attributes used to display
+# them
+_style = {}
+
+
+def _style_to_curses(style_def):
+    # Parses a style definition string (<element>=<style>), returning
+    # a (fg_color, bg_color, attributes) tuple.
+
+    def parse_color(color_def):
+        color_def = color_def.split(":", 1)[1]
+
+        # HTML format, #RRGGBB
+        if re.match("#[A-Fa-f0-9]{6}", color_def):
+            return _color_from_rgb((
+                int(color_def[1:3], 16),
+                int(color_def[3:5], 16),
+                int(color_def[5:7], 16)))
+
+        if color_def in _NAMED_COLORS:
+            color_num = _color_from_num(_NAMED_COLORS[color_def])
+        else:
+            try:
+                color_num = _color_from_num(int(color_def, 0))
+            except ValueError:
+                _warn("Ignoring color", color_def, "that's neither "
+                      "predefined nor a number")
+                return -1
+
+        if not -1 <= color_num < curses.COLORS:
+            _warn("Ignoring color {}, which is outside the range "
+                  "-1..curses.COLORS-1 (-1..{})"
+                  .format(color_def, curses.COLORS - 1))
+            return -1
+
+        return color_num
+
+    fg_color = -1
+    bg_color = -1
+    attrs = 0
+
+    if style_def:
+        for field in style_def.split(","):
+            if field.startswith("fg:"):
+                fg_color = parse_color(field)
+            elif field.startswith("bg:"):
+                bg_color = parse_color(field)
+            elif field == "bold":
+                # A_BOLD tends to produce faint and hard-to-read text on the
+                # Windows console, especially with the old color scheme, before
+                # the introduction of
+                # https://blogs.msdn.microsoft.com/commandline/2017/08/02/updating-the-windows-console-colors/
+                attrs |= curses.A_NORMAL if _IS_WINDOWS else curses.A_BOLD
+            elif field == "standout":
+                attrs |= curses.A_STANDOUT
+            elif field == "underline":
+                attrs |= curses.A_UNDERLINE
+            else:
+                _warn("Ignoring unknown style attribute", field)
+
+    return _style_attr(fg_color, bg_color, attrs)
+
+
+def _init_styles():
+    if curses.has_colors():
+        try:
+            curses.use_default_colors()
+        except curses.error:
+            # Ignore errors on funky terminals that support colors but not
+            # using default colors. Worst it can do is break transparency and
+            # the like. Ran across this with the MSYS2/winpty setup in
+            # https://github.com/msys2/MINGW-packages/issues/5823, though there
+            # seems to be a lot of general brokenness there.
+            pass
+
+        # Use the 'default' theme as the base, and add any user-defined style
+        # settings from the environment
+        _parse_style("default", True)
+        if "MENUCONFIG_STYLE" in os.environ:
+            _parse_style(os.environ["MENUCONFIG_STYLE"], False)
+    else:
+        # Force the 'monochrome' theme if the terminal doesn't support colors.
+        # MENUCONFIG_STYLE is likely to mess things up here (though any colors
+        # would be ignored), so ignore it.
+        _parse_style("monochrome", True)
+
+
+# color_attribs holds the color pairs we've already created, indexed by a
+# (<foreground color>, <background color>) tuple.
+#
+# Obscure Python: We never pass a value for color_attribs, and it keeps
+# pointing to the same dict. This avoids a global.
+def _style_attr(fg_color, bg_color, attribs, color_attribs={}):
+    # Returns an attribute with the specified foreground and background color
+    # and the attributes in 'attribs'. Reuses color pairs already created if
+    # possible, and creates a new color pair otherwise.
+    #
+    # Returns 'attribs' if colors aren't supported.
+
+    if not curses.has_colors():
+        return attribs
+
+    if (fg_color, bg_color) not in color_attribs:
+        # Create new color pair. Color pair number 0 is hardcoded and cannot be
+        # changed, hence the +1s.
+        curses.init_pair(len(color_attribs) + 1, fg_color, bg_color)
+        color_attribs[(fg_color, bg_color)] = \
+            curses.color_pair(len(color_attribs) + 1)
+
+    return color_attribs[(fg_color, bg_color)] | attribs
+
+
+#
+# Main application
+#
+
+
+def _main():
+    menuconfig(standard_kconfig(__doc__))
+
+
+def menuconfig(kconf):
+    """
+    Launches the configuration interface, returning after the user exits.
+
+    kconf:
+      Kconfig instance to be configured
+    """
+    global _kconf
+    global _conf_filename
+    global _conf_changed
+    global _minconf_filename
+    global _show_all
+
+    _kconf = kconf
+
+    # Filename to save configuration to
+    _conf_filename = standard_config_filename()
+
+    # Load existing configuration and set _conf_changed True if it is outdated
+    _conf_changed = _load_config()
+
+    # Filename to save minimal configuration to
+    _minconf_filename = "defconfig"
+
+    # Any visible items in the top menu?
+    _show_all = False
+    if not _shown_nodes(kconf.top_node):
+        # Nothing visible. Start in show-all mode and try again.
+        _show_all = True
+        if not _shown_nodes(kconf.top_node):
+            # Give up. The implementation relies on always having a selected
+            # node.
+            print("Empty configuration -- nothing to configure.\n"
+                  "Check that environment variables are set properly.")
+            return
+
+    # Disable warnings. They get mangled in curses mode, and we deal with
+    # errors ourselves.
+    kconf.warn = False
+
+    # Make curses use the locale settings specified in the environment
+    locale.setlocale(locale.LC_ALL, "")
+
+    # Try to fix Unicode issues on systems with bad defaults
+    if _CHANGE_C_LC_CTYPE_TO_UTF8:
+        _change_c_lc_ctype_to_utf8()
+
+    # Get rid of the delay between pressing ESC and jumping to the parent menu,
+    # unless the user has set ESCDELAY (see ncurses(3)). This makes the UI much
+    # smoother to work with.
+    #
+    # Note: This is strictly pretty iffy, since escape codes for e.g. cursor
+    # keys start with ESC, but I've never seen it cause problems in practice
+    # (probably because it's unlikely that the escape code for a key would get
+    # split up across read()s, at least with a terminal emulator). Please
+    # report if you run into issues. Some suitable small default value could be
+    # used here instead in that case. Maybe it's silly to not put in the
+    # smallest imperceptible delay here already, though I don't like guessing.
+    #
+    # (From a quick glance at the ncurses source code, ESCDELAY might only be
+    # relevant for mouse events there, so maybe escapes are assumed to arrive
+    # in one piece already...)
+    os.environ.setdefault("ESCDELAY", "0")
+
+    # Enter curses mode. _menuconfig() returns a string to print on exit, after
+    # curses has been de-initialized.
+    print(curses.wrapper(_menuconfig))
+
+
+def _load_config():
+    # Loads any existing .config file. See the Kconfig.load_config() docstring.
+    #
+    # Returns True if .config is missing or outdated. We always prompt for
+    # saving the configuration in that case.
+
+    print(_kconf.load_config())
+    if not os.path.exists(_conf_filename):
+        # No .config
+        return True
+
+    return _needs_save()
+
+
+def _needs_save():
+    # Returns True if a just-loaded .config file is outdated (would get
+    # modified when saving)
+
+    if _kconf.missing_syms:
+        # Assignments to undefined symbols in the .config
+        return True
+
+    for sym in _kconf.unique_defined_syms:
+        if sym.user_value is None:
+            if sym.config_string:
+                # Unwritten symbol
+                return True
+        elif sym.orig_type in (BOOL, TRISTATE):
+            if sym.tri_value != sym.user_value:
+                # Written bool/tristate symbol, new value
+                return True
+        elif sym.str_value != sym.user_value:
+            # Written string/int/hex symbol, new value
+            return True
+
+    # No need to prompt for save
+    return False
+
+
+# Global variables used below:
+#
+#   _stdscr:
+#     stdscr from curses
+#
+#   _cur_menu:
+#     Menu node of the menu (or menuconfig symbol, or choice) currently being
+#     shown
+#
+#   _shown:
+#     List of items in _cur_menu that are shown (ignoring scrolling). In
+#     show-all mode, this list contains all items in _cur_menu. Otherwise, it
+#     contains just the visible items.
+#
+#   _sel_node_i:
+#     Index in _shown of the currently selected node
+#
+#   _menu_scroll:
+#     Index in _shown of the top row of the main display
+#
+#   _parent_screen_rows:
+#     List/stack of the row numbers that the selections in the parent menus
+#     appeared on. This is used to prevent the scrolling from jumping around
+#     when going in and out of menus.
+#
+#   _show_help/_show_name/_show_all:
+#     If True, the corresponding mode is on. See the module docstring.
+#
+#   _conf_filename:
+#     File to save the configuration to
+#
+#   _minconf_filename:
+#     File to save minimal configurations to
+#
+#   _conf_changed:
+#     True if the configuration has been changed. If False, we don't bother
+#     showing the save-and-quit dialog.
+#
+#     We reset this to False whenever the configuration is saved explicitly
+#     from the save dialog.
+
+
+def _menuconfig(stdscr):
+    # Logic for the main display, with the list of symbols, etc.
+
+    global _stdscr
+    global _conf_filename
+    global _conf_changed
+    global _minconf_filename
+    global _show_help
+    global _show_name
+
+    _stdscr = stdscr
+
+    _init()
+
+    while True:
+        _draw_main()
+        curses.doupdate()
+
+
+        c = _getch_compat(_menu_win)
+
+        if c == curses.KEY_RESIZE:
+            _resize_main()
+
+        elif c in (curses.KEY_DOWN, "j", "J"):
+            _select_next_menu_entry()
+
+        elif c in (curses.KEY_UP, "k", "K"):
+            _select_prev_menu_entry()
+
+        elif c in (curses.KEY_NPAGE, "\x04"):  # Page Down/Ctrl-D
+            # Keep it simple. This way we get sane behavior for small windows,
+            # etc., for free.
+            for _ in range(_PG_JUMP):
+                _select_next_menu_entry()
+
+        elif c in (curses.KEY_PPAGE, "\x15"):  # Page Up/Ctrl-U
+            for _ in range(_PG_JUMP):
+                _select_prev_menu_entry()
+
+        elif c in (curses.KEY_END, "G"):
+            _select_last_menu_entry()
+
+        elif c in (curses.KEY_HOME, "g"):
+            _select_first_menu_entry()
+
+        elif c == " ":
+            # Toggle the node if possible
+            sel_node = _shown[_sel_node_i]
+            if not _change_node(sel_node):
+                _enter_menu(sel_node)
+
+        elif c in (curses.KEY_RIGHT, "\n", "l", "L"):
+            # Enter the node if possible
+            sel_node = _shown[_sel_node_i]
+            if not _enter_menu(sel_node):
+                _change_node(sel_node)
+
+        elif c in ("n", "N"):
+            _set_sel_node_tri_val(0)
+
+        elif c in ("m", "M"):
+            _set_sel_node_tri_val(1)
+
+        elif c in ("y", "Y"):
+            _set_sel_node_tri_val(2)
+
+        elif c in (curses.KEY_LEFT, curses.KEY_BACKSPACE, _ERASE_CHAR,
+                   "\x1B", "h", "H"):  # \x1B = ESC
+
+            if c == "\x1B" and _cur_menu is _kconf.top_node:
+                res = _quit_dialog()
+                if res:
+                    return res
+            else:
+                _leave_menu()
+
+        elif c in ("o", "O"):
+            _load_dialog()
+
+        elif c in ("s", "S"):
+            filename = _save_dialog(_kconf.write_config, _conf_filename,
+                                    "configuration")
+            if filename:
+                _conf_filename = filename
+                _conf_changed = False
+
+        elif c in ("d", "D"):
+            filename = _save_dialog(_kconf.write_min_config, _minconf_filename,
+                                    "minimal configuration")
+            if filename:
+                _minconf_filename = filename
+
+        elif c == "/":
+            _jump_to_dialog()
+            # The terminal might have been resized while the fullscreen jump-to
+            # dialog was open
+            _resize_main()
+
+        elif c == "?":
+            _info_dialog(_shown[_sel_node_i], False)
+            # The terminal might have been resized while the fullscreen info
+            # dialog was open
+            _resize_main()
+
+        elif c in ("f", "F"):
+            _show_help = not _show_help
+            _set_style(_help_win, "show-help" if _show_help else "help")
+            _resize_main()
+
+        elif c in ("c", "C"):
+            _show_name = not _show_name
+
+        elif c in ("a", "A"):
+            _toggle_show_all()
+
+        elif c in ("q", "Q"):
+            res = _quit_dialog()
+            if res:
+                return res
+
+
+def _quit_dialog():
+    if not _conf_changed:
+        return "No changes to save (for '{}')".format(_conf_filename)
+
+    while True:
+        c = _key_dialog(
+            "Quit",
+            " Save configuration?\n"
+            "\n"
+            "(Y)es  (N)o  (C)ancel",
+            "ync")
+
+        if c is None or c == "c":
+            return None
+
+        if c == "y":
+            # Returns a message to print
+            msg = _try_save(_kconf.write_config, _conf_filename, "configuration")
+            if msg:
+                return msg
+
+        elif c == "n":
+            return "Configuration ({}) was not saved".format(_conf_filename)
+
+
+def _init():
+    # Initializes the main display with the list of symbols, etc. Also does
+    # misc. global initialization that needs to happen after initializing
+    # curses.
+
+    global _ERASE_CHAR
+
+    global _path_win
+    global _top_sep_win
+    global _menu_win
+    global _bot_sep_win
+    global _help_win
+
+    global _parent_screen_rows
+    global _cur_menu
+    global _shown
+    global _sel_node_i
+    global _menu_scroll
+
+    global _show_help
+    global _show_name
+
+    # Looking for this in addition to KEY_BACKSPACE (which is unreliable) makes
+    # backspace work with TERM=vt100. That makes it likely to work in sane
+    # environments.
+    _ERASE_CHAR = curses.erasechar()
+    if sys.version_info[0] >= 3:
+        # erasechar() returns a one-byte bytes object on Python 3. This sets
+        # _ERASE_CHAR to a blank string if it can't be decoded, which should be
+        # harmless.
+        _ERASE_CHAR = _ERASE_CHAR.decode("utf-8", "ignore")
+
+    _init_styles()
+
+    # Hide the cursor
+    _safe_curs_set(0)
+
+    # Initialize windows
+
+    # Top row, with menu path
+    _path_win = _styled_win("path")
+
+    # Separator below menu path, with title and arrows pointing up
+    _top_sep_win = _styled_win("separator")
+
+    # List of menu entries with symbols, etc.
+    _menu_win = _styled_win("list")
+    _menu_win.keypad(True)
+
+    # Row below menu list, with arrows pointing down
+    _bot_sep_win = _styled_win("separator")
+
+    # Help window with keys at the bottom. Shows help texts in show-help mode.
+    _help_win = _styled_win("help")
+
+    # The rows we'd like the nodes in the parent menus to appear on. This
+    # prevents the scroll from jumping around when going in and out of menus.
+    _parent_screen_rows = []
+
+    # Initial state
+
+    _cur_menu = _kconf.top_node
+    _shown = _shown_nodes(_cur_menu)
+    _sel_node_i = _menu_scroll = 0
+
+    _show_help = _show_name = False
+
+    # Give windows their initial size
+    _resize_main()
+
+
+def _resize_main():
+    # Resizes the main display, with the list of symbols, etc., to fill the
+    # terminal
+
+    global _menu_scroll
+
+    screen_height, screen_width = _stdscr.getmaxyx()
+
+    _path_win.resize(1, screen_width)
+    _top_sep_win.resize(1, screen_width)
+    _bot_sep_win.resize(1, screen_width)
+
+    help_win_height = _SHOW_HELP_HEIGHT if _show_help else \
+        len(_MAIN_HELP_LINES)
+
+    menu_win_height = screen_height - help_win_height - 3
+
+    if menu_win_height >= 1:
+        _menu_win.resize(menu_win_height, screen_width)
+        _help_win.resize(help_win_height, screen_width)
+
+        _top_sep_win.mvwin(1, 0)
+        _menu_win.mvwin(2, 0)
+        _bot_sep_win.mvwin(2 + menu_win_height, 0)
+        _help_win.mvwin(2 + menu_win_height + 1, 0)
+    else:
+        # Degenerate case. Give up on nice rendering and just prevent errors.
+
+        menu_win_height = 1
+
+        _menu_win.resize(1, screen_width)
+        _help_win.resize(1, screen_width)
+
+        for win in _top_sep_win, _menu_win, _bot_sep_win, _help_win:
+            win.mvwin(0, 0)
+
+    # Adjust the scroll so that the selected node is still within the window,
+    # if needed
+    if _sel_node_i - _menu_scroll >= menu_win_height:
+        _menu_scroll = _sel_node_i - menu_win_height + 1
+
+
+def _height(win):
+    # Returns the height of 'win'
+
+    return win.getmaxyx()[0]
+
+
+def _width(win):
+    # Returns the width of 'win'
+
+    return win.getmaxyx()[1]
+
+
+def _enter_menu(menu):
+    # Makes 'menu' the currently displayed menu. In addition to actual 'menu's,
+    # "menu" here includes choices and symbols defined with the 'menuconfig'
+    # keyword.
+    #
+    # Returns False if 'menu' can't be entered.
+
+    global _cur_menu
+    global _shown
+    global _sel_node_i
+    global _menu_scroll
+
+    if not menu.is_menuconfig:
+        return False  # Not a menu
+
+    shown_sub = _shown_nodes(menu)
+    # Never enter empty menus. We depend on having a current node.
+    if not shown_sub:
+        return False
+
+    # Remember where the current node appears on the screen, so we can try
+    # to get it to appear in the same place when we leave the menu
+    _parent_screen_rows.append(_sel_node_i - _menu_scroll)
+
+    # Jump into menu
+    _cur_menu = menu
+    _shown = shown_sub
+    _sel_node_i = _menu_scroll = 0
+
+    if isinstance(menu.item, Choice):
+        _select_selected_choice_sym()
+
+    return True
+
+
+def _select_selected_choice_sym():
+    # Puts the cursor on the currently selected (y-valued) choice symbol, if
+    # any. Does nothing if if the choice has no selection (is not visible/in y
+    # mode).
+
+    global _sel_node_i
+
+    choice = _cur_menu.item
+    if choice.selection:
+        # Search through all menu nodes to handle choice symbols being defined
+        # in multiple locations
+        for node in choice.selection.nodes:
+            if node in _shown:
+                _sel_node_i = _shown.index(node)
+                _center_vertically()
+                return
+
+
+def _jump_to(node):
+    # Jumps directly to the menu node 'node'
+
+    global _cur_menu
+    global _shown
+    global _sel_node_i
+    global _menu_scroll
+    global _show_all
+    global _parent_screen_rows
+
+    # Clear remembered menu locations. We might not even have been in the
+    # parent menus before.
+    _parent_screen_rows = []
+
+    old_show_all = _show_all
+    jump_into = (isinstance(node.item, Choice) or node.item == MENU) and \
+                node.list
+
+    # If we're jumping to a non-empty choice or menu, jump to the first entry
+    # in it instead of jumping to its menu node
+    if jump_into:
+        _cur_menu = node
+        node = node.list
+    else:
+        _cur_menu = _parent_menu(node)
+
+    _shown = _shown_nodes(_cur_menu)
+    if node not in _shown:
+        # The node wouldn't be shown. Turn on show-all to show it.
+        _show_all = True
+        _shown = _shown_nodes(_cur_menu)
+
+    _sel_node_i = _shown.index(node)
+
+    if jump_into and not old_show_all and _show_all:
+        # If we're jumping into a choice or menu and were forced to turn on
+        # show-all because the first entry wasn't visible, try turning it off.
+        # That will land us at the first visible node if there are visible
+        # nodes, and is a no-op otherwise.
+        _toggle_show_all()
+
+    _center_vertically()
+
+    # If we're jumping to a non-empty choice, jump to the selected symbol, if
+    # any
+    if jump_into and isinstance(_cur_menu.item, Choice):
+        _select_selected_choice_sym()
+
+
+def _leave_menu():
+    # Jumps to the parent menu of the current menu. Does nothing if we're in
+    # the top menu.
+
+    global _cur_menu
+    global _shown
+    global _sel_node_i
+    global _menu_scroll
+
+    if _cur_menu is _kconf.top_node:
+        return
+
+    # Jump to parent menu
+    parent = _parent_menu(_cur_menu)
+    _shown = _shown_nodes(parent)
+    _sel_node_i = _shown.index(_cur_menu)
+    _cur_menu = parent
+
+    # Try to make the menu entry appear on the same row on the screen as it did
+    # before we entered the menu.
+
+    if _parent_screen_rows:
+        # The terminal might have shrunk since we were last in the parent menu
+        screen_row = min(_parent_screen_rows.pop(), _height(_menu_win) - 1)
+        _menu_scroll = max(_sel_node_i - screen_row, 0)
+    else:
+        # No saved parent menu locations, meaning we jumped directly to some
+        # node earlier
+        _center_vertically()
+
+
+def _select_next_menu_entry():
+    # Selects the menu entry after the current one, adjusting the scroll if
+    # necessary. Does nothing if we're already at the last menu entry.
+
+    global _sel_node_i
+    global _menu_scroll
+
+    if _sel_node_i < len(_shown) - 1:
+        # Jump to the next node
+        _sel_node_i += 1
+
+        # If the new node is sufficiently close to the edge of the menu window
+        # (as determined by _SCROLL_OFFSET), increase the scroll by one. This
+        # gives nice and non-jumpy behavior even when
+        # _SCROLL_OFFSET >= _height(_menu_win).
+        if _sel_node_i >= _menu_scroll + _height(_menu_win) - _SCROLL_OFFSET \
+           and _menu_scroll < _max_scroll(_shown, _menu_win):
+
+            _menu_scroll += 1
+
+
+def _select_prev_menu_entry():
+    # Selects the menu entry before the current one, adjusting the scroll if
+    # necessary. Does nothing if we're already at the first menu entry.
+
+    global _sel_node_i
+    global _menu_scroll
+
+    if _sel_node_i > 0:
+        # Jump to the previous node
+        _sel_node_i -= 1
+
+        # See _select_next_menu_entry()
+        if _sel_node_i < _menu_scroll + _SCROLL_OFFSET:
+            _menu_scroll = max(_menu_scroll - 1, 0)
+
+
+def _select_last_menu_entry():
+    # Selects the last menu entry in the current menu
+
+    global _sel_node_i
+    global _menu_scroll
+
+    _sel_node_i = len(_shown) - 1
+    _menu_scroll = _max_scroll(_shown, _menu_win)
+
+
+def _select_first_menu_entry():
+    # Selects the first menu entry in the current menu
+
+    global _sel_node_i
+    global _menu_scroll
+
+    _sel_node_i = _menu_scroll = 0
+
+
+def _toggle_show_all():
+    # Toggles show-all mode on/off. If turning it off would give no visible
+    # items in the current menu, it is left on.
+
+    global _show_all
+    global _shown
+    global _sel_node_i
+    global _menu_scroll
+
+    # Row on the screen the cursor is on. Preferably we want the same row to
+    # stay highlighted.
+    old_row = _sel_node_i - _menu_scroll
+
+    _show_all = not _show_all
+    # List of new nodes to be shown after toggling _show_all
+    new_shown = _shown_nodes(_cur_menu)
+
+    # Find a good node to select. The selected node might disappear if show-all
+    # mode is turned off.
+
+    # Select the previously selected node itself if it is still visible. If
+    # there are visible nodes before it, select the closest one.
+    for node in _shown[_sel_node_i::-1]:
+        if node in new_shown:
+            _sel_node_i = new_shown.index(node)
+            break
+    else:
+        # No visible nodes before the previously selected node. Select the
+        # closest visible node after it instead.
+        for node in _shown[_sel_node_i + 1:]:
+            if node in new_shown:
+                _sel_node_i = new_shown.index(node)
+                break
+        else:
+            # No visible nodes at all, meaning show-all was turned off inside
+            # an invisible menu. Don't allow that, as the implementation relies
+            # on always having a selected node.
+            _show_all = True
+            return
+
+    _shown = new_shown
+
+    # Try to make the cursor stay on the same row in the menu window. This
+    # might be impossible if too many nodes have disappeared above the node.
+    _menu_scroll = max(_sel_node_i - old_row, 0)
+
+
+def _center_vertically():
+    # Centers the selected node vertically, if possible
+
+    global _menu_scroll
+
+    _menu_scroll = min(max(_sel_node_i - _height(_menu_win)//2, 0),
+                       _max_scroll(_shown, _menu_win))
+
+
+def _draw_main():
+    # Draws the "main" display, with the list of symbols, the header, and the
+    # footer.
+    #
+    # This could be optimized to only update the windows that have actually
+    # changed, but keep it simple for now and let curses sort it out.
+
+    term_width = _width(_stdscr)
+
+    #
+    # Update the separator row below the menu path
+    #
+
+    _top_sep_win.erase()
+
+    # Draw arrows pointing up if the symbol window is scrolled down. Draw them
+    # before drawing the title, so the title ends up on top for small windows.
+    if _menu_scroll > 0:
+        _safe_hline(_top_sep_win, 0, 4, curses.ACS_UARROW, _N_SCROLL_ARROWS)
+
+    # Add the 'mainmenu' text as the title, centered at the top
+    _safe_addstr(_top_sep_win,
+                 0, max((term_width - len(_kconf.mainmenu_text))//2, 0),
+                 _kconf.mainmenu_text)
+
+    _top_sep_win.noutrefresh()
+
+    # Note: The menu path at the top is deliberately updated last. See below.
+
+    #
+    # Update the symbol window
+    #
+
+    _menu_win.erase()
+
+    # Draw the _shown nodes starting from index _menu_scroll up to either as
+    # many as fit in the window, or to the end of _shown
+    for i in range(_menu_scroll,
+                   min(_menu_scroll + _height(_menu_win), len(_shown))):
+
+        node = _shown[i]
+
+        # The 'not _show_all' test avoids showing invisible items in red
+        # outside show-all mode, which could look confusing/broken. Invisible
+        # symbols show up outside show-all mode if an invisible symbol has
+        # visible children in an implicit (indented) menu.
+        if _visible(node) or not _show_all:
+            style = _style["selection" if i == _sel_node_i else "list"]
+        else:
+            style = _style["inv-selection" if i == _sel_node_i else "inv-list"]
+
+        _safe_addstr(_menu_win, i - _menu_scroll, 0, _node_str(node), style)
+
+    _menu_win.noutrefresh()
+
+    #
+    # Update the bottom separator window
+    #
+
+    _bot_sep_win.erase()
+
+    # Draw arrows pointing down if the symbol window is scrolled up
+    if _menu_scroll < _max_scroll(_shown, _menu_win):
+        _safe_hline(_bot_sep_win, 0, 4, curses.ACS_DARROW, _N_SCROLL_ARROWS)
+
+    # Indicate when show-name/show-help/show-all mode is enabled
+    enabled_modes = []
+    if _show_help:
+        enabled_modes.append("show-help (toggle with [F])")
+    if _show_name:
+        enabled_modes.append("show-name")
+    if _show_all:
+        enabled_modes.append("show-all")
+    if enabled_modes:
+        s = " and ".join(enabled_modes) + " mode enabled"
+        _safe_addstr(_bot_sep_win, 0, max(term_width - len(s) - 2, 0), s)
+
+    _bot_sep_win.noutrefresh()
+
+    #
+    # Update the help window, which shows either key bindings or help texts
+    #
+
+    _help_win.erase()
+
+    if _show_help:
+        node = _shown[_sel_node_i]
+        if isinstance(node.item, (Symbol, Choice)) and node.help:
+            help_lines = textwrap.wrap(node.help, _width(_help_win))
+            for i in range(min(_height(_help_win), len(help_lines))):
+                _safe_addstr(_help_win, i, 0, help_lines[i])
+        else:
+            _safe_addstr(_help_win, 0, 0, "(no help)")
+    else:
+        for i, line in enumerate(_MAIN_HELP_LINES):
+            _safe_addstr(_help_win, i, 0, line)
+
+    _help_win.noutrefresh()
+
+    #
+    # Update the top row with the menu path.
+    #
+    # Doing this last leaves the cursor on the top row, which avoids some minor
+    # annoying jumpiness in gnome-terminal when reducing the height of the
+    # terminal. It seems to happen whenever the row with the cursor on it
+    # disappears.
+    #
+
+    _path_win.erase()
+
+    # Draw the menu path ("(Top) -> Menu -> Submenu -> ...")
+
+    menu_prompts = []
+
+    menu = _cur_menu
+    while menu is not _kconf.top_node:
+        # Promptless choices can be entered in show-all mode. Use
+        # standard_sc_expr_str() for them, so they show up as
+        # '<choice (name if any)>'.
+        menu_prompts.append(menu.prompt[0] if menu.prompt else
+                            standard_sc_expr_str(menu.item))
+        menu = menu.parent
+    menu_prompts.append("(Top)")
+    menu_prompts.reverse()
+
+    # Hack: We can't put ACS_RARROW directly in the string. Temporarily
+    # represent it with NULL.
+    menu_path_str = " \0 ".join(menu_prompts)
+
+    # Scroll the menu path to the right if needed to make the current menu's
+    # title visible
+    if len(menu_path_str) > term_width:
+        menu_path_str = menu_path_str[len(menu_path_str) - term_width:]
+
+    # Print the path with the arrows reinserted
+    split_path = menu_path_str.split("\0")
+    _safe_addstr(_path_win, split_path[0])
+    for s in split_path[1:]:
+        _safe_addch(_path_win, curses.ACS_RARROW)
+        _safe_addstr(_path_win, s)
+
+    _path_win.noutrefresh()
+
+
+def _parent_menu(node):
+    # Returns the menu node of the menu that contains 'node'. In addition to
+    # proper 'menu's, this might also be a 'menuconfig' symbol or a 'choice'.
+    # "Menu" here means a menu in the interface.
+
+    menu = node.parent
+    while not menu.is_menuconfig:
+        menu = menu.parent
+    return menu
+
+
+def _shown_nodes(menu):
+    # Returns the list of menu nodes from 'menu' (see _parent_menu()) that
+    # would be shown when entering it
+
+    def rec(node):
+        res = []
+
+        while node:
+            if _visible(node) or _show_all:
+                res.append(node)
+                if node.list and not node.is_menuconfig:
+                    # Nodes from implicit menu created from dependencies. Will
+                    # be shown indented. Note that is_menuconfig is True for
+                    # menus and choices as well as 'menuconfig' symbols.
+                    res += rec(node.list)
+
+            elif node.list and isinstance(node.item, Symbol):
+                # Show invisible symbols if they have visible children. This
+                # can happen for an m/y-valued symbol with an optional prompt
+                # ('prompt "foo" is COND') that is currently disabled. Note
+                # that it applies to both 'config' and 'menuconfig' symbols.
+                shown_children = rec(node.list)
+                if shown_children:
+                    res.append(node)
+                    if not node.is_menuconfig:
+                        res += shown_children
+
+            node = node.next
+
+        return res
+
+    if isinstance(menu.item, Choice):
+        # For named choices defined in multiple locations, entering the choice
+        # at a particular menu node would normally only show the choice symbols
+        # defined there (because that's what the MenuNode tree looks like).
+        #
+        # That might look confusing, and makes extending choices by defining
+        # them in multiple locations less useful. Instead, gather all the child
+        # menu nodes for all the choices whenever a choice is entered. That
+        # makes all choice symbols visible at all locations.
+        #
+        # Choices can contain non-symbol items (people do all sorts of weird
+        # stuff with them), hence the generality here. We really need to
+        # preserve the menu tree at each choice location.
+        #
+        # Note: Named choices are pretty broken in the C tools, and this is
+        # super obscure, so you probably won't find much that relies on this.
+        # This whole 'if' could be deleted if you don't care about defining
+        # choices in multiple locations to add symbols (which will still work,
+        # just with things being displayed in a way that might be unexpected).
+
+        # Do some additional work to avoid listing choice symbols twice if all
+        # or part of the choice is copied in multiple locations (e.g. by
+        # including some Kconfig file multiple times). We give the prompts at
+        # the current location precedence.
+        seen_syms = {node.item for node in rec(menu.list)
+                     if isinstance(node.item, Symbol)}
+        res = []
+        for choice_node in menu.item.nodes:
+            for node in rec(choice_node.list):
+                # 'choice_node is menu' checks if we're dealing with the
+                # current location
+                if node.item not in seen_syms or choice_node is menu:
+                    res.append(node)
+                    if isinstance(node.item, Symbol):
+                        seen_syms.add(node.item)
+        return res
+
+    return rec(menu.list)
+
+
+def _visible(node):
+    # Returns True if the node should appear in the menu (outside show-all
+    # mode)
+
+    return node.prompt and expr_value(node.prompt[1]) and not \
+        (node.item == MENU and not expr_value(node.visibility))
+
+
+def _change_node(node):
+    # Changes the value of the menu node 'node' if it is a symbol. Bools and
+    # tristates are toggled, while other symbol types pop up a text entry
+    # dialog.
+    #
+    # Returns False if the value of 'node' can't be changed.
+
+    if not _changeable(node):
+        return False
+
+    # sc = symbol/choice
+    sc = node.item
+
+    if sc.orig_type in (INT, HEX, STRING):
+        s = sc.str_value
+
+        while True:
+            s = _input_dialog(
+                "{} ({})".format(node.prompt[0], TYPE_TO_STR[sc.orig_type]),
+                s, _range_info(sc))
+
+            if s is None:
+                break
+
+            if sc.orig_type in (INT, HEX):
+                s = s.strip()
+
+                # 'make menuconfig' does this too. Hex values not starting with
+                # '0x' are accepted when loading .config files though.
+                if sc.orig_type == HEX and not s.startswith(("0x", "0X")):
+                    s = "0x" + s
+
+            if _check_valid(sc, s):
+                _set_val(sc, s)
+                break
+
+    elif len(sc.assignable) == 1:
+        # Handles choice symbols for choices in y mode, which are a special
+        # case: .assignable can be (2,) while .tri_value is 0.
+        _set_val(sc, sc.assignable[0])
+
+    else:
+        # Set the symbol to the value after the current value in
+        # sc.assignable, with wrapping
+        val_index = sc.assignable.index(sc.tri_value)
+        _set_val(sc, sc.assignable[(val_index + 1) % len(sc.assignable)])
+
+
+    if _is_y_mode_choice_sym(sc) and not node.list:
+        # Immediately jump to the parent menu after making a choice selection,
+        # like 'make menuconfig' does, except if the menu node has children
+        # (which can happen if a symbol 'depends on' a choice symbol that
+        # immediately precedes it).
+        _leave_menu()
+
+
+    return True
+
+
+def _changeable(node):
+    # Returns True if the value if 'node' can be changed
+
+    sc = node.item
+
+    if not isinstance(sc, (Symbol, Choice)):
+        return False
+
+    # This will hit for invisible symbols, which appear in show-all mode and
+    # when an invisible symbol has visible children (which can happen e.g. for
+    # symbols with optional prompts)
+    if not (node.prompt and expr_value(node.prompt[1])):
+        return False
+
+    return sc.orig_type in (STRING, INT, HEX) or len(sc.assignable) > 1 \
+        or _is_y_mode_choice_sym(sc)
+
+
+def _set_sel_node_tri_val(tri_val):
+    # Sets the value of the currently selected menu entry to 'tri_val', if that
+    # value can be assigned
+
+    sc = _shown[_sel_node_i].item
+    if isinstance(sc, (Symbol, Choice)) and tri_val in sc.assignable:
+        _set_val(sc, tri_val)
+
+
+def _set_val(sc, val):
+    # Wrapper around Symbol/Choice.set_value() for updating the menu state and
+    # _conf_changed
+
+    global _conf_changed
+
+    # Use the string representation of tristate values. This makes the format
+    # consistent for all symbol types.
+    if val in TRI_TO_STR:
+        val = TRI_TO_STR[val]
+
+    if val != sc.str_value:
+        sc.set_value(val)
+        _conf_changed = True
+
+        # Changing the value of the symbol might have changed what items in the
+        # current menu are visible. Recalculate the state.
+        _update_menu()
+
+
+def _update_menu():
+    # Updates the current menu after the value of a symbol or choice has been
+    # changed. Changing a value might change which items in the menu are
+    # visible.
+    #
+    # If possible, preserves the location of the cursor on the screen when
+    # items are added/removed above the selected item.
+
+    global _shown
+    global _sel_node_i
+    global _menu_scroll
+
+    # Row on the screen the cursor was on
+    old_row = _sel_node_i - _menu_scroll
+
+    sel_node = _shown[_sel_node_i]
+
+    # New visible nodes
+    _shown = _shown_nodes(_cur_menu)
+
+    # New index of selected node
+    _sel_node_i = _shown.index(sel_node)
+
+    # Try to make the cursor stay on the same row in the menu window. This
+    # might be impossible if too many nodes have disappeared above the node.
+    _menu_scroll = max(_sel_node_i - old_row, 0)
+
+
+def _input_dialog(title, initial_text, info_text=None):
+    # Pops up a dialog that prompts the user for a string
+    #
+    # title:
+    #   Title to display at the top of the dialog window's border
+    #
+    # initial_text:
+    #   Initial text to prefill the input field with
+    #
+    # info_text:
+    #   String to show next to the input field. If None, just the input field
+    #   is shown.
+
+    win = _styled_win("body")
+    win.keypad(True)
+
+    info_lines = info_text.split("\n") if info_text else []
+
+    # Give the input dialog its initial size
+    _resize_input_dialog(win, title, info_lines)
+
+    _safe_curs_set(2)
+
+    # Input field text
+    s = initial_text
+
+    # Cursor position
+    i = len(initial_text)
+
+    def edit_width():
+        return _width(win) - 4
+
+    # Horizontal scroll offset
+    hscroll = max(i - edit_width() + 1, 0)
+
+    while True:
+        # Draw the "main" display with the menu, etc., so that resizing still
+        # works properly. This is like a stack of windows, only hardcoded for
+        # now.
+        _draw_main()
+        _draw_input_dialog(win, title, info_lines, s, i, hscroll)
+        curses.doupdate()
+
+
+        c = _getch_compat(win)
+
+        if c == curses.KEY_RESIZE:
+            # Resize the main display too. The dialog floats above it.
+            _resize_main()
+            _resize_input_dialog(win, title, info_lines)
+
+        elif c == "\n":
+            _safe_curs_set(0)
+            return s
+
+        elif c == "\x1B":  # \x1B = ESC
+            _safe_curs_set(0)
+            return None
+
+        else:
+            s, i, hscroll = _edit_text(c, s, i, hscroll, edit_width())
+
+
+def _resize_input_dialog(win, title, info_lines):
+    # Resizes the input dialog to a size appropriate for the terminal size
+
+    screen_height, screen_width = _stdscr.getmaxyx()
+
+    win_height = 5
+    if info_lines:
+        win_height += len(info_lines) + 1
+    win_height = min(win_height, screen_height)
+
+    win_width = max(_INPUT_DIALOG_MIN_WIDTH,
+                    len(title) + 4,
+                    *(len(line) + 4 for line in info_lines))
+    win_width = min(win_width, screen_width)
+
+    win.resize(win_height, win_width)
+    win.mvwin((screen_height - win_height)//2,
+              (screen_width - win_width)//2)
+
+
+def _draw_input_dialog(win, title, info_lines, s, i, hscroll):
+    edit_width = _width(win) - 4
+
+    win.erase()
+
+    # Note: Perhaps having a separate window for the input field would be nicer
+    visible_s = s[hscroll:hscroll + edit_width]
+    _safe_addstr(win, 2, 2, visible_s + " "*(edit_width - len(visible_s)),
+                 _style["edit"])
+
+    for linenr, line in enumerate(info_lines):
+        _safe_addstr(win, 4 + linenr, 2, line)
+
+    # Draw the frame last so that it overwrites the body text for small windows
+    _draw_frame(win, title)
+
+    _safe_move(win, 2, 2 + i - hscroll)
+
+    win.noutrefresh()
+
+
+def _load_dialog():
+    # Dialog for loading a new configuration
+
+    global _conf_changed
+    global _conf_filename
+    global _show_all
+
+    if _conf_changed:
+        c = _key_dialog(
+            "Load",
+            "You have unsaved changes. Load new\n"
+            "configuration anyway?\n"
+            "\n"
+            "         (O)K  (C)ancel",
+            "oc")
+
+        if c is None or c == "c":
+            return
+
+    filename = _conf_filename
+    while True:
+        filename = _input_dialog("File to load", filename, _load_save_info())
+        if filename is None:
+            return
+
+        filename = os.path.expanduser(filename)
+
+        if _try_load(filename):
+            _conf_filename = filename
+            _conf_changed = _needs_save()
+
+            # Turn on show-all mode if the selected node is not visible after
+            # loading the new configuration. _shown still holds the old state.
+            if _shown[_sel_node_i] not in _shown_nodes(_cur_menu):
+                _show_all = True
+
+            _update_menu()
+
+            # The message dialog indirectly updates the menu display, so _msg()
+            # must be called after the new state has been initialized
+            _msg("Success", "Loaded " + filename)
+            return
+
+
+def _try_load(filename):
+    # Tries to load a configuration file. Pops up an error and returns False on
+    # failure.
+    #
+    # filename:
+    #   Configuration file to load
+
+    try:
+        _kconf.load_config(filename)
+        return True
+    except EnvironmentError as e:
+        _error("Error loading '{}'\n\n{} (errno: {})"
+               .format(filename, e.strerror, errno.errorcode[e.errno]))
+        return False
+
+
+def _save_dialog(save_fn, default_filename, description):
+    # Dialog for saving the current configuration
+    #
+    # save_fn:
+    #   Function to call with 'filename' to save the file
+    #
+    # default_filename:
+    #   Prefilled filename in the input field
+    #
+    # description:
+    #   String describing the thing being saved
+    #
+    # Return value:
+    #   The path to the saved file, or None if no file was saved
+
+    filename = default_filename
+    while True:
+        filename = _input_dialog("Filename to save {} to".format(description),
+                                 filename, _load_save_info())
+        if filename is None:
+            return None
+
+        filename = os.path.expanduser(filename)
+
+        msg = _try_save(save_fn, filename, description)
+        if msg:
+            _msg("Success", msg)
+            return filename
+
+
+def _try_save(save_fn, filename, description):
+    # Tries to save a configuration file. Returns a message to print on
+    # success.
+    #
+    # save_fn:
+    #   Function to call with 'filename' to save the file
+    #
+    # description:
+    #   String describing the thing being saved
+    #
+    # Return value:
+    #   A message to print on success, and None on failure
+
+    try:
+        # save_fn() returns a message to print
+        return save_fn(filename)
+    except EnvironmentError as e:
+        _error("Error saving {} to '{}'\n\n{} (errno: {})"
+               .format(description, e.filename, e.strerror,
+                       errno.errorcode[e.errno]))
+        return None
+
+
+def _key_dialog(title, text, keys):
+    # Pops up a dialog that can be closed by pressing a key
+    #
+    # title:
+    #   Title to display at the top of the dialog window's border
+    #
+    # text:
+    #   Text to show in the dialog
+    #
+    # keys:
+    #   List of keys that will close the dialog. Other keys (besides ESC) are
+    #   ignored. The caller is responsible for providing a hint about which
+    #   keys can be pressed in 'text'.
+    #
+    # Return value:
+    #   The key that was pressed to close the dialog. Uppercase characters are
+    #   converted to lowercase. ESC will always close the dialog, and returns
+    #   None.
+
+    win = _styled_win("body")
+    win.keypad(True)
+
+    _resize_key_dialog(win, text)
+
+    while True:
+        # See _input_dialog()
+        _draw_main()
+        _draw_key_dialog(win, title, text)
+        curses.doupdate()
+
+
+        c = _getch_compat(win)
+
+        if c == curses.KEY_RESIZE:
+            # Resize the main display too. The dialog floats above it.
+            _resize_main()
+            _resize_key_dialog(win, text)
+
+        elif c == "\x1B":  # \x1B = ESC
+            return None
+
+        elif isinstance(c, str):
+            c = c.lower()
+            if c in keys:
+                return c
+
+
+def _resize_key_dialog(win, text):
+    # Resizes the key dialog to a size appropriate for the terminal size
+
+    screen_height, screen_width = _stdscr.getmaxyx()
+
+    lines = text.split("\n")
+
+    win_height = min(len(lines) + 4, screen_height)
+    win_width = min(max(len(line) for line in lines) + 4, screen_width)
+
+    win.resize(win_height, win_width)
+    win.mvwin((screen_height - win_height)//2,
+              (screen_width - win_width)//2)
+
+
+def _draw_key_dialog(win, title, text):
+    win.erase()
+
+    for i, line in enumerate(text.split("\n")):
+        _safe_addstr(win, 2 + i, 2, line)
+
+    # Draw the frame last so that it overwrites the body text for small windows
+    _draw_frame(win, title)
+
+    win.noutrefresh()
+
+
+def _draw_frame(win, title):
+    # Draw a frame around the inner edges of 'win', with 'title' at the top
+
+    win_height, win_width = win.getmaxyx()
+
+    win.attron(_style["frame"])
+
+    # Draw top/bottom edge
+    _safe_hline(win,              0, 0, " ", win_width)
+    _safe_hline(win, win_height - 1, 0, " ", win_width)
+
+    # Draw left/right edge
+    _safe_vline(win, 0,             0, " ", win_height)
+    _safe_vline(win, 0, win_width - 1, " ", win_height)
+
+    # Draw title
+    _safe_addstr(win, 0, max((win_width - len(title))//2, 0), title)
+
+    win.attroff(_style["frame"])
+
+
+def _jump_to_dialog():
+    # Implements the jump-to dialog, where symbols can be looked up via
+    # incremental search and jumped to.
+    #
+    # Returns True if the user jumped to a symbol, and False if the dialog was
+    # canceled.
+
+    s = ""  # Search text
+    prev_s = None  # Previous search text
+    s_i = 0  # Search text cursor position
+    hscroll = 0  # Horizontal scroll offset
+
+    sel_node_i = 0  # Index of selected row
+    scroll = 0  # Index in 'matches' of the top row of the list
+
+    # Edit box at the top
+    edit_box = _styled_win("jump-edit")
+    edit_box.keypad(True)
+
+    # List of matches
+    matches_win = _styled_win("list")
+
+    # Bottom separator, with arrows pointing down
+    bot_sep_win = _styled_win("separator")
+
+    # Help window with instructions at the bottom
+    help_win = _styled_win("help")
+
+    # Give windows their initial size
+    _resize_jump_to_dialog(edit_box, matches_win, bot_sep_win, help_win,
+                           sel_node_i, scroll)
+
+    _safe_curs_set(2)
+
+    # Logic duplication with _select_{next,prev}_menu_entry(), except we do a
+    # functional variant that returns the new (sel_node_i, scroll) values to
+    # avoid 'nonlocal'. TODO: Can this be factored out in some nice way?
+
+    def select_next_match():
+        if sel_node_i == len(matches) - 1:
+            return sel_node_i, scroll
+
+        if sel_node_i + 1 >= scroll + _height(matches_win) - _SCROLL_OFFSET \
+           and scroll < _max_scroll(matches, matches_win):
+
+            return sel_node_i + 1, scroll + 1
+
+        return sel_node_i + 1, scroll
+
+    def select_prev_match():
+        if sel_node_i == 0:
+            return sel_node_i, scroll
+
+        if sel_node_i - 1 < scroll + _SCROLL_OFFSET:
+            return sel_node_i - 1, max(scroll - 1, 0)
+
+        return sel_node_i - 1, scroll
+
+    while True:
+        if s != prev_s:
+            # The search text changed. Find new matching nodes.
+
+            prev_s = s
+
+            try:
+                # We could use re.IGNORECASE here instead of lower(), but this
+                # is noticeably less jerky while inputting regexes like
+                # '.*debug$' (though the '.*' is redundant there). Those
+                # probably have bad interactions with re.search(), which
+                # matches anywhere in the string.
+                #
+                # It's not horrible either way. Just a bit smoother.
+                regex_searches = [re.compile(regex).search
+                                  for regex in s.lower().split()]
+
+                # No exception thrown, so the regexes are okay
+                bad_re = None
+
+                # List of matching nodes
+                matches = []
+                add_match = matches.append
+
+                # Search symbols and choices
+
+                for node in _sorted_sc_nodes():
+                    # Symbol/choice
+                    sc = node.item
+
+                    for search in regex_searches:
+                        # Both the name and the prompt might be missing, since
+                        # we're searching both symbols and choices
+
+                        # Does the regex match either the symbol name or the
+                        # prompt (if any)?
+                        if not (sc.name and search(sc.name.lower()) or
+                                node.prompt and search(node.prompt[0].lower())):
+
+                            # Give up on the first regex that doesn't match, to
+                            # speed things up a bit when multiple regexes are
+                            # entered
+                            break
+
+                    else:
+                        add_match(node)
+
+                # Search menus and comments
+
+                for node in _sorted_menu_comment_nodes():
+                    for search in regex_searches:
+                        if not search(node.prompt[0].lower()):
+                            break
+                    else:
+                        add_match(node)
+
+            except re.error as e:
+                # Bad regex. Remember the error message so we can show it.
+                bad_re = "Bad regular expression"
+                # re.error.msg was added in Python 3.5
+                if hasattr(e, "msg"):
+                    bad_re += ": " + e.msg
+
+                matches = []
+
+            # Reset scroll and jump to the top of the list of matches
+            sel_node_i = scroll = 0
+
+        _draw_jump_to_dialog(edit_box, matches_win, bot_sep_win, help_win,
+                             s, s_i, hscroll,
+                             bad_re, matches, sel_node_i, scroll)
+        curses.doupdate()
+
+
+        c = _getch_compat(edit_box)
+
+        if c == "\n":
+            if matches:
+                _jump_to(matches[sel_node_i])
+                _safe_curs_set(0)
+                return True
+
+        elif c == "\x1B":  # \x1B = ESC
+            _safe_curs_set(0)
+            return False
+
+        elif c == curses.KEY_RESIZE:
+            # We adjust the scroll so that the selected node stays visible in
+            # the list when the terminal is resized, hence the 'scroll'
+            # assignment
+            scroll = _resize_jump_to_dialog(
+                edit_box, matches_win, bot_sep_win, help_win,
+                sel_node_i, scroll)
+
+        elif c == "\x06":  # \x06 = Ctrl-F
+            if matches:
+                _safe_curs_set(0)
+                _info_dialog(matches[sel_node_i], True)
+                _safe_curs_set(2)
+
+                scroll = _resize_jump_to_dialog(
+                    edit_box, matches_win, bot_sep_win, help_win,
+                    sel_node_i, scroll)
+
+        elif c == curses.KEY_DOWN:
+            sel_node_i, scroll = select_next_match()
+
+        elif c == curses.KEY_UP:
+            sel_node_i, scroll = select_prev_match()
+
+        elif c in (curses.KEY_NPAGE, "\x04"):  # Page Down/Ctrl-D
+            # Keep it simple. This way we get sane behavior for small windows,
+            # etc., for free.
+            for _ in range(_PG_JUMP):
+                sel_node_i, scroll = select_next_match()
+
+        # Page Up (no Ctrl-U, as it's already used by the edit box)
+        elif c == curses.KEY_PPAGE:
+            for _ in range(_PG_JUMP):
+                sel_node_i, scroll = select_prev_match()
+
+        elif c == curses.KEY_END:
+            sel_node_i = len(matches) - 1
+            scroll = _max_scroll(matches, matches_win)
+
+        elif c == curses.KEY_HOME:
+            sel_node_i = scroll = 0
+
+        else:
+            s, s_i, hscroll = _edit_text(c, s, s_i, hscroll,
+                                         _width(edit_box) - 2)
+
+
+# Obscure Python: We never pass a value for cached_nodes, and it keeps pointing
+# to the same list. This avoids a global.
+def _sorted_sc_nodes(cached_nodes=[]):
+    # Returns a sorted list of symbol and choice nodes to search. The symbol
+    # nodes appear first, sorted by name, and then the choice nodes, sorted by
+    # prompt and (secondarily) name.
+
+    if not cached_nodes:
+        # Add symbol nodes
+        for sym in sorted(_kconf.unique_defined_syms,
+                          key=lambda sym: sym.name):
+            # += is in-place for lists
+            cached_nodes += sym.nodes
+
+        # Add choice nodes
+
+        choices = sorted(_kconf.unique_choices,
+                         key=lambda choice: choice.name or "")
+
+        cached_nodes += sorted(
+            [node for choice in choices for node in choice.nodes],
+            key=lambda node: node.prompt[0] if node.prompt else "")
+
+    return cached_nodes
+
+
+def _sorted_menu_comment_nodes(cached_nodes=[]):
+    # Returns a list of menu and comment nodes to search, sorted by prompt,
+    # with the menus first
+
+    if not cached_nodes:
+        def prompt_text(mc):
+            return mc.prompt[0]
+
+        cached_nodes += sorted(_kconf.menus, key=prompt_text)
+        cached_nodes += sorted(_kconf.comments, key=prompt_text)
+
+    return cached_nodes
+
+
+def _resize_jump_to_dialog(edit_box, matches_win, bot_sep_win, help_win,
+                           sel_node_i, scroll):
+    # Resizes the jump-to dialog to fill the terminal.
+    #
+    # Returns the new scroll index. We adjust the scroll if needed so that the
+    # selected node stays visible.
+
+    screen_height, screen_width = _stdscr.getmaxyx()
+
+    bot_sep_win.resize(1, screen_width)
+
+    help_win_height = len(_JUMP_TO_HELP_LINES)
+    matches_win_height = screen_height - help_win_height - 4
+
+    if matches_win_height >= 1:
+        edit_box.resize(3, screen_width)
+        matches_win.resize(matches_win_height, screen_width)
+        help_win.resize(help_win_height, screen_width)
+
+        matches_win.mvwin(3, 0)
+        bot_sep_win.mvwin(3 + matches_win_height, 0)
+        help_win.mvwin(3 + matches_win_height + 1, 0)
+    else:
+        # Degenerate case. Give up on nice rendering and just prevent errors.
+
+        matches_win_height = 1
+
+        edit_box.resize(screen_height, screen_width)
+        matches_win.resize(1, screen_width)
+        help_win.resize(1, screen_width)
+
+        for win in matches_win, bot_sep_win, help_win:
+            win.mvwin(0, 0)
+
+    # Adjust the scroll so that the selected row is still within the window, if
+    # needed
+    if sel_node_i - scroll >= matches_win_height:
+        return sel_node_i - matches_win_height + 1
+    return scroll
+
+
+def _draw_jump_to_dialog(edit_box, matches_win, bot_sep_win, help_win,
+                         s, s_i, hscroll,
+                         bad_re, matches, sel_node_i, scroll):
+
+    edit_width = _width(edit_box) - 2
+
+    #
+    # Update list of matches
+    #
+
+    matches_win.erase()
+
+    if matches:
+        for i in range(scroll,
+                       min(scroll + _height(matches_win), len(matches))):
+
+            node = matches[i]
+
+            if isinstance(node.item, (Symbol, Choice)):
+                node_str = _name_and_val_str(node.item)
+                if node.prompt:
+                    node_str += ' "{}"'.format(node.prompt[0])
+            elif node.item == MENU:
+                node_str = 'menu "{}"'.format(node.prompt[0])
+            else:  # node.item == COMMENT
+                node_str = 'comment "{}"'.format(node.prompt[0])
+
+            _safe_addstr(matches_win, i - scroll, 0, node_str,
+                         _style["selection" if i == sel_node_i else "list"])
+
+    else:
+        # bad_re holds the error message from the re.error exception on errors
+        _safe_addstr(matches_win, 0, 0, bad_re or "No matches")
+
+    matches_win.noutrefresh()
+
+    #
+    # Update bottom separator line
+    #
+
+    bot_sep_win.erase()
+
+    # Draw arrows pointing down if the symbol list is scrolled up
+    if scroll < _max_scroll(matches, matches_win):
+        _safe_hline(bot_sep_win, 0, 4, curses.ACS_DARROW, _N_SCROLL_ARROWS)
+
+    bot_sep_win.noutrefresh()
+
+    #
+    # Update help window at bottom
+    #
+
+    help_win.erase()
+
+    for i, line in enumerate(_JUMP_TO_HELP_LINES):
+        _safe_addstr(help_win, i, 0, line)
+
+    help_win.noutrefresh()
+
+    #
+    # Update edit box. We do this last since it makes it handy to position the
+    # cursor.
+    #
+
+    edit_box.erase()
+
+    _draw_frame(edit_box, "Jump to symbol/choice/menu/comment")
+
+    # Draw arrows pointing up if the symbol list is scrolled down
+    if scroll > 0:
+        # TODO: Bit ugly that _style["frame"] is repeated here
+        _safe_hline(edit_box, 2, 4, curses.ACS_UARROW, _N_SCROLL_ARROWS,
+                    _style["frame"])
+
+    visible_s = s[hscroll:hscroll + edit_width]
+    _safe_addstr(edit_box, 1, 1, visible_s)
+
+    _safe_move(edit_box, 1, 1 + s_i - hscroll)
+
+    edit_box.noutrefresh()
+
+
+def _info_dialog(node, from_jump_to_dialog):
+    # Shows a fullscreen window with information about 'node'.
+    #
+    # If 'from_jump_to_dialog' is True, the information dialog was opened from
+    # within the jump-to-dialog. In this case, we make '/' from within the
+    # information dialog just return, to avoid a confusing recursive invocation
+    # of the jump-to-dialog.
+
+    # Top row, with title and arrows point up
+    top_line_win = _styled_win("separator")
+
+    # Text display
+    text_win = _styled_win("text")
+    text_win.keypad(True)
+
+    # Bottom separator, with arrows pointing down
+    bot_sep_win = _styled_win("separator")
+
+    # Help window with keys at the bottom
+    help_win = _styled_win("help")
+
+    # Give windows their initial size
+    _resize_info_dialog(top_line_win, text_win, bot_sep_win, help_win)
+
+
+    # Get lines of help text
+    lines = _info_str(node).split("\n")
+
+    # Index of first row in 'lines' to show
+    scroll = 0
+
+    while True:
+        _draw_info_dialog(node, lines, scroll, top_line_win, text_win,
+                          bot_sep_win, help_win)
+        curses.doupdate()
+
+
+        c = _getch_compat(text_win)
+
+        if c == curses.KEY_RESIZE:
+            _resize_info_dialog(top_line_win, text_win, bot_sep_win, help_win)
+
+        elif c in (curses.KEY_DOWN, "j", "J"):
+            if scroll < _max_scroll(lines, text_win):
+                scroll += 1
+
+        elif c in (curses.KEY_NPAGE, "\x04"):  # Page Down/Ctrl-D
+            scroll = min(scroll + _PG_JUMP, _max_scroll(lines, text_win))
+
+        elif c in (curses.KEY_PPAGE, "\x15"):  # Page Up/Ctrl-U
+            scroll = max(scroll - _PG_JUMP, 0)
+
+        elif c in (curses.KEY_END, "G"):
+            scroll = _max_scroll(lines, text_win)
+
+        elif c in (curses.KEY_HOME, "g"):
+            scroll = 0
+
+        elif c in (curses.KEY_UP, "k", "K"):
+            if scroll > 0:
+                scroll -= 1
+
+        elif c == "/":
+            # Support starting a search from within the information dialog
+
+            if from_jump_to_dialog:
+                return  # Avoid recursion
+
+            if _jump_to_dialog():
+                return  # Jumped to a symbol. Cancel the information dialog.
+
+            # Stay in the information dialog if the jump-to dialog was
+            # canceled. Resize it in case the terminal was resized while the
+            # fullscreen jump-to dialog was open.
+            _resize_info_dialog(top_line_win, text_win, bot_sep_win, help_win)
+
+        elif c in (curses.KEY_LEFT, curses.KEY_BACKSPACE, _ERASE_CHAR,
+                   "\x1B",  # \x1B = ESC
+                   "q", "Q", "h", "H"):
+
+            return
+
+
+def _resize_info_dialog(top_line_win, text_win, bot_sep_win, help_win):
+    # Resizes the info dialog to fill the terminal
+
+    screen_height, screen_width = _stdscr.getmaxyx()
+
+    top_line_win.resize(1, screen_width)
+    bot_sep_win.resize(1, screen_width)
+
+    help_win_height = len(_INFO_HELP_LINES)
+    text_win_height = screen_height - help_win_height - 2
+
+    if text_win_height >= 1:
+        text_win.resize(text_win_height, screen_width)
+        help_win.resize(help_win_height, screen_width)
+
+        text_win.mvwin(1, 0)
+        bot_sep_win.mvwin(1 + text_win_height, 0)
+        help_win.mvwin(1 + text_win_height + 1, 0)
+    else:
+        # Degenerate case. Give up on nice rendering and just prevent errors.
+
+        text_win.resize(1, screen_width)
+        help_win.resize(1, screen_width)
+
+        for win in text_win, bot_sep_win, help_win:
+            win.mvwin(0, 0)
+
+
+def _draw_info_dialog(node, lines, scroll, top_line_win, text_win,
+                      bot_sep_win, help_win):
+
+    text_win_height, text_win_width = text_win.getmaxyx()
+
+    # Note: The top row is deliberately updated last. See _draw_main().
+
+    #
+    # Update text display
+    #
+
+    text_win.erase()
+
+    for i, line in enumerate(lines[scroll:scroll + text_win_height]):
+        _safe_addstr(text_win, i, 0, line)
+
+    text_win.noutrefresh()
+
+    #
+    # Update bottom separator line
+    #
+
+    bot_sep_win.erase()
+
+    # Draw arrows pointing down if the symbol window is scrolled up
+    if scroll < _max_scroll(lines, text_win):
+        _safe_hline(bot_sep_win, 0, 4, curses.ACS_DARROW, _N_SCROLL_ARROWS)
+
+    bot_sep_win.noutrefresh()
+
+    #
+    # Update help window at bottom
+    #
+
+    help_win.erase()
+
+    for i, line in enumerate(_INFO_HELP_LINES):
+        _safe_addstr(help_win, i, 0, line)
+
+    help_win.noutrefresh()
+
+    #
+    # Update top row
+    #
+
+    top_line_win.erase()
+
+    # Draw arrows pointing up if the information window is scrolled down. Draw
+    # them before drawing the title, so the title ends up on top for small
+    # windows.
+    if scroll > 0:
+        _safe_hline(top_line_win, 0, 4, curses.ACS_UARROW, _N_SCROLL_ARROWS)
+
+    title = ("Symbol" if isinstance(node.item, Symbol) else
+             "Choice" if isinstance(node.item, Choice) else
+             "Menu"   if node.item == MENU else
+             "Comment") + " information"
+    _safe_addstr(top_line_win, 0, max((text_win_width - len(title))//2, 0),
+                 title)
+
+    top_line_win.noutrefresh()
+
+
+def _info_str(node):
+    # Returns information about the menu node 'node' as a string.
+    #
+    # The helper functions are responsible for adding newlines. This allows
+    # them to return "" if they don't want to add any output.
+
+    if isinstance(node.item, Symbol):
+        sym = node.item
+
+        return (
+            _name_info(sym) +
+            _prompt_info(sym) +
+            "Type: {}\n".format(TYPE_TO_STR[sym.type]) +
+            _value_info(sym) +
+            _help_info(sym) +
+            _direct_dep_info(sym) +
+            _defaults_info(sym) +
+            _select_imply_info(sym) +
+            _kconfig_def_info(sym)
+        )
+
+    if isinstance(node.item, Choice):
+        choice = node.item
+
+        return (
+            _name_info(choice) +
+            _prompt_info(choice) +
+            "Type: {}\n".format(TYPE_TO_STR[choice.type]) +
+            'Mode: {}\n'.format(choice.str_value) +
+            _help_info(choice) +
+            _choice_syms_info(choice) +
+            _direct_dep_info(choice) +
+            _defaults_info(choice) +
+            _kconfig_def_info(choice)
+        )
+
+    return _kconfig_def_info(node)  # node.item in (MENU, COMMENT)
+
+
+def _name_info(sc):
+    # Returns a string with the name of the symbol/choice. Names are optional
+    # for choices.
+
+    return "Name: {}\n".format(sc.name) if sc.name else ""
+
+
+def _prompt_info(sc):
+    # Returns a string listing the prompts of 'sc' (Symbol or Choice)
+
+    s = ""
+
+    for node in sc.nodes:
+        if node.prompt:
+            s += "Prompt: {}\n".format(node.prompt[0])
+
+    return s
+
+
+def _value_info(sym):
+    # Returns a string showing 'sym's value
+
+    # Only put quotes around the value for string symbols
+    return "Value: {}\n".format(
+        '"{}"'.format(sym.str_value)
+        if sym.orig_type == STRING
+        else sym.str_value)
+
+
+def _choice_syms_info(choice):
+    # Returns a string listing the choice symbols in 'choice'. Adds
+    # "(selected)" next to the selected one.
+
+    s = "Choice symbols:\n"
+
+    for sym in choice.syms:
+        s += "  - " + sym.name
+        if sym is choice.selection:
+            s += " (selected)"
+        s += "\n"
+
+    return s + "\n"
+
+
+def _help_info(sc):
+    # Returns a string with the help text(s) of 'sc' (Symbol or Choice).
+    # Symbols and choices defined in multiple locations can have multiple help
+    # texts.
+
+    s = "\n"
+
+    for node in sc.nodes:
+        if node.help is not None:
+            s += "Help:\n\n{}\n\n".format(_indent(node.help, 2))
+
+    return s
+
+
+def _direct_dep_info(sc):
+    # Returns a string describing the direct dependencies of 'sc' (Symbol or
+    # Choice). The direct dependencies are the OR of the dependencies from each
+    # definition location. The dependencies at each definition location come
+    # from 'depends on' and dependencies inherited from parent items.
+
+    return "" if sc.direct_dep is _kconf.y else \
+        'Direct dependencies (={}):\n{}\n' \
+        .format(TRI_TO_STR[expr_value(sc.direct_dep)],
+                _split_expr_info(sc.direct_dep, 2))
+
+
+def _defaults_info(sc):
+    # Returns a string describing the defaults of 'sc' (Symbol or Choice)
+
+    if not sc.defaults:
+        return ""
+
+    s = "Default"
+    if len(sc.defaults) > 1:
+        s += "s"
+    s += ":\n"
+
+    for val, cond in sc.orig_defaults:
+        s += "  - "
+        if isinstance(sc, Symbol):
+            s += _expr_str(val)
+
+            # Skip the tristate value hint if the expression is just a single
+            # symbol. _expr_str() already shows its value as a string.
+            #
+            # This also avoids showing the tristate value for string/int/hex
+            # defaults, which wouldn't make any sense.
+            if isinstance(val, tuple):
+                s += '  (={})'.format(TRI_TO_STR[expr_value(val)])
+        else:
+            # Don't print the value next to the symbol name for choice
+            # defaults, as it looks a bit confusing
+            s += val.name
+        s += "\n"
+
+        if cond is not _kconf.y:
+            s += "    Condition (={}):\n{}" \
+                 .format(TRI_TO_STR[expr_value(cond)],
+                         _split_expr_info(cond, 4))
+
+    return s + "\n"
+
+
+def _split_expr_info(expr, indent):
+    # Returns a string with 'expr' split into its top-level && or || operands,
+    # with one operand per line, together with the operand's value. This is
+    # usually enough to get something readable for long expressions. A fancier
+    # recursive thingy would be possible too.
+    #
+    # indent:
+    #   Number of leading spaces to add before the split expression.
+
+    if len(split_expr(expr, AND)) > 1:
+        split_op = AND
+        op_str = "&&"
+    else:
+        split_op = OR
+        op_str = "||"
+
+    s = ""
+    for i, term in enumerate(split_expr(expr, split_op)):
+        s += "{}{} {}".format(indent*" ",
+                              "  " if i == 0 else op_str,
+                              _expr_str(term))
+
+        # Don't bother showing the value hint if the expression is just a
+        # single symbol. _expr_str() already shows its value.
+        if isinstance(term, tuple):
+            s += "  (={})".format(TRI_TO_STR[expr_value(term)])
+
+        s += "\n"
+
+    return s
+
+
+def _select_imply_info(sym):
+    # Returns a string with information about which symbols 'select' or 'imply'
+    # 'sym'. The selecting/implying symbols are grouped according to which
+    # value they select/imply 'sym' to (n/m/y).
+
+    def sis(expr, val, title):
+        # sis = selects/implies
+        sis = [si for si in split_expr(expr, OR) if expr_value(si) == val]
+        if not sis:
+            return ""
+
+        res = title
+        for si in sis:
+            res += "  - {}\n".format(split_expr(si, AND)[0].name)
+        return res + "\n"
+
+    s = ""
+
+    if sym.rev_dep is not _kconf.n:
+        s += sis(sym.rev_dep, 2,
+                 "Symbols currently y-selecting this symbol:\n")
+        s += sis(sym.rev_dep, 1,
+                 "Symbols currently m-selecting this symbol:\n")
+        s += sis(sym.rev_dep, 0,
+                 "Symbols currently n-selecting this symbol (no effect):\n")
+
+    if sym.weak_rev_dep is not _kconf.n:
+        s += sis(sym.weak_rev_dep, 2,
+                 "Symbols currently y-implying this symbol:\n")
+        s += sis(sym.weak_rev_dep, 1,
+                 "Symbols currently m-implying this symbol:\n")
+        s += sis(sym.weak_rev_dep, 0,
+                 "Symbols currently n-implying this symbol (no effect):\n")
+
+    return s
+
+
+def _kconfig_def_info(item):
+    # Returns a string with the definition of 'item' in Kconfig syntax,
+    # together with the definition location(s) and their include and menu paths
+
+    nodes = [item] if isinstance(item, MenuNode) else item.nodes
+
+    s = "Kconfig definition{}, with parent deps. propagated to 'depends on'\n" \
+        .format("s" if len(nodes) > 1 else "")
+    s += (len(s) - 1)*"="
+
+    for node in nodes:
+        s += "\n\n" \
+             "At {}:{}\n" \
+             "{}" \
+             "Menu path: {}\n\n" \
+             "{}" \
+             .format(node.filename, node.linenr,
+                     _include_path_info(node),
+                     _menu_path_info(node),
+                     _indent(node.custom_str(_name_and_val_str), 2))
+
+    return s
+
+
+def _include_path_info(node):
+    if not node.include_path:
+        # In the top-level Kconfig file
+        return ""
+
+    return "Included via {}\n".format(
+        " -> ".join("{}:{}".format(filename, linenr)
+                    for filename, linenr in node.include_path))
+
+
+def _menu_path_info(node):
+    # Returns a string describing the menu path leading up to 'node'
+
+    path = ""
+
+    while node.parent is not _kconf.top_node:
+        node = node.parent
+
+        # Promptless choices might appear among the parents. Use
+        # standard_sc_expr_str() for them, so that they show up as
+        # '<choice (name if any)>'.
+        path = " -> " + (node.prompt[0] if node.prompt else
+                         standard_sc_expr_str(node.item)) + path
+
+    return "(Top)" + path
+
+
+def _indent(s, n):
+    # Returns 's' with each line indented 'n' spaces. textwrap.indent() is not
+    # available in Python 2 (it's 3.3+).
+
+    return "\n".join(n*" " + line for line in s.split("\n"))
+
+
+def _name_and_val_str(sc):
+    # Custom symbol/choice printer that shows symbol values after symbols
+
+    # Show the values of non-constant (non-quoted) symbols that don't look like
+    # numbers. Things like 123 are actually symbol references, and only work as
+    # expected due to undefined symbols getting their name as their value.
+    # Showing the symbol value for those isn't helpful though.
+    if isinstance(sc, Symbol) and not sc.is_constant and not _is_num(sc.name):
+        if not sc.nodes:
+            # Undefined symbol reference
+            return "{}(undefined/n)".format(sc.name)
+
+        return '{}(={})'.format(sc.name, sc.str_value)
+
+    # For other items, use the standard format
+    return standard_sc_expr_str(sc)
+
+
+def _expr_str(expr):
+    # Custom expression printer that shows symbol values
+    return expr_str(expr, _name_and_val_str)
+
+
+def _styled_win(style):
+    # Returns a new curses window with style 'style' and space as the fill
+    # character. The initial dimensions are (1, 1), so the window needs to be
+    # sized and positioned separately.
+
+    win = curses.newwin(1, 1)
+    _set_style(win, style)
+    return win
+
+
+def _set_style(win, style):
+    # Changes the style of an existing window
+
+    win.bkgdset(" ", _style[style])
+
+
+def _max_scroll(lst, win):
+    # Assuming 'lst' is a list of items to be displayed in 'win',
+    # returns the maximum number of steps 'win' can be scrolled down.
+    # We stop scrolling when the bottom item is visible.
+
+    return max(0, len(lst) - _height(win))
+
+
+def _edit_text(c, s, i, hscroll, width):
+    # Implements text editing commands for edit boxes. Takes a character (which
+    # could also be e.g. curses.KEY_LEFT) and the edit box state, and returns
+    # the new state after the character has been processed.
+    #
+    # c:
+    #   Character from user
+    #
+    # s:
+    #   Current contents of string
+    #
+    # i:
+    #   Current cursor index in string
+    #
+    # hscroll:
+    #   Index in s of the leftmost character in the edit box, for horizontal
+    #   scrolling
+    #
+    # width:
+    #   Width in characters of the edit box
+    #
+    # Return value:
+    #   An (s, i, hscroll) tuple for the new state
+
+    if c == curses.KEY_LEFT:
+        if i > 0:
+            i -= 1
+
+    elif c == curses.KEY_RIGHT:
+        if i < len(s):
+            i += 1
+
+    elif c in (curses.KEY_HOME, "\x01"):  # \x01 = CTRL-A
+        i = 0
+
+    elif c in (curses.KEY_END, "\x05"):  # \x05 = CTRL-E
+        i = len(s)
+
+    elif c in (curses.KEY_BACKSPACE, _ERASE_CHAR):
+        if i > 0:
+            s = s[:i-1] + s[i:]
+            i -= 1
+
+    elif c == curses.KEY_DC:
+        s = s[:i] + s[i+1:]
+
+    elif c == "\x17":  # \x17 = CTRL-W
+        # The \W removes characters like ',' one at a time
+        new_i = re.search(r"(?:\w*|\W)\s*$", s[:i]).start()
+        s = s[:new_i] + s[i:]
+        i = new_i
+
+    elif c == "\x0B":  # \x0B = CTRL-K
+        s = s[:i]
+
+    elif c == "\x15":  # \x15 = CTRL-U
+        s = s[i:]
+        i = 0
+
+    elif isinstance(c, str):
+        # Insert character
+        s = s[:i] + c + s[i:]
+        i += 1
+
+    # Adjust the horizontal scroll so that the cursor never touches the left or
+    # right edges of the edit box, except when it's at the beginning or the end
+    # of the string
+    if i < hscroll + _SCROLL_OFFSET:
+        hscroll = max(i - _SCROLL_OFFSET, 0)
+    elif i >= hscroll + width - _SCROLL_OFFSET:
+        max_scroll = max(len(s) - width + 1, 0)
+        hscroll = min(i - width + _SCROLL_OFFSET + 1, max_scroll)
+
+    return s, i, hscroll
+
+
+def _load_save_info():
+    # Returns an information string for load/save dialog boxes
+
+    return "(Relative to {})\n\nRefer to your home directory with ~" \
+           .format(os.path.join(os.getcwd(), ""))
+
+
+def _msg(title, text):
+    # Pops up a message dialog that can be dismissed with Space/Enter/ESC
+
+    _key_dialog(title, text, " \n")
+
+
+def _error(text):
+    # Pops up an error dialog that can be dismissed with Space/Enter/ESC
+
+    _msg("Error", text)
+
+
+def _node_str(node):
+    # Returns the complete menu entry text for a menu node.
+    #
+    # Example return value: "[*] Support for X"
+
+    # Calculate the indent to print the item with by checking how many levels
+    # above it the closest 'menuconfig' item is (this includes menus and
+    # choices as well as menuconfig symbols)
+    indent = 0
+    parent = node.parent
+    while not parent.is_menuconfig:
+        indent += _SUBMENU_INDENT
+        parent = parent.parent
+
+    # This approach gives nice alignment for empty string symbols ("()  Foo")
+    s = "{:{}}".format(_value_str(node), 3 + indent)
+
+    if _should_show_name(node):
+        if isinstance(node.item, Symbol):
+            s += " <{}>".format(node.item.name)
+        else:
+            # For choices, use standard_sc_expr_str(). That way they show up as
+            # '<choice (name if any)>'.
+            s += " " + standard_sc_expr_str(node.item)
+
+    if node.prompt:
+        if node.item == COMMENT:
+            s += " *** {} ***".format(node.prompt[0])
+        else:
+            s += " " + node.prompt[0]
+
+        if isinstance(node.item, Symbol):
+            sym = node.item
+
+            # Print "(NEW)" next to symbols without a user value (from e.g. a
+            # .config), but skip it for choice symbols in choices in y mode,
+            # and for symbols of UNKNOWN type (which generate a warning though)
+            if sym.user_value is None and sym.orig_type and \
+               not (sym.choice and sym.choice.tri_value == 2):
+
+                s += " (NEW)"
+
+    if isinstance(node.item, Choice) and node.item.tri_value == 2:
+        # Print the prompt of the selected symbol after the choice for
+        # choices in y mode
+        sym = node.item.selection
+        if sym:
+            for sym_node in sym.nodes:
+                # Use the prompt used at this choice location, in case the
+                # choice symbol is defined in multiple locations
+                if sym_node.parent is node and sym_node.prompt:
+                    s += " ({})".format(sym_node.prompt[0])
+                    break
+            else:
+                # If the symbol isn't defined at this choice location, then
+                # just use whatever prompt we can find for it
+                for sym_node in sym.nodes:
+                    if sym_node.prompt:
+                        s += " ({})".format(sym_node.prompt[0])
+                        break
+
+    # Print "--->" next to nodes that have menus that can potentially be
+    # entered. Print "----" if the menu is empty. We don't allow those to be
+    # entered.
+    if node.is_menuconfig:
+        s += "  --->" if _shown_nodes(node) else "  ----"
+
+    return s
+
+
+def _should_show_name(node):
+    # Returns True if 'node' is a symbol or choice whose name should shown (if
+    # any, as names are optional for choices)
+
+    # The 'not node.prompt' case only hits in show-all mode, for promptless
+    # symbols and choices
+    return not node.prompt or \
+           (_show_name and isinstance(node.item, (Symbol, Choice)))
+
+
+def _value_str(node):
+    # Returns the value part ("[*]", "<M>", "(foo)" etc.) of a menu node
+
+    item = node.item
+
+    if item in (MENU, COMMENT):
+        return ""
+
+    # Wouldn't normally happen, and generates a warning
+    if not item.orig_type:
+        return ""
+
+    if item.orig_type in (STRING, INT, HEX):
+        return "({})".format(item.str_value)
+
+    # BOOL or TRISTATE
+
+    if _is_y_mode_choice_sym(item):
+        return "(X)" if item.choice.selection is item else "( )"
+
+    tri_val_str = (" ", "M", "*")[item.tri_value]
+
+    if len(item.assignable) <= 1:
+        # Pinned to a single value
+        return "" if isinstance(item, Choice) else "-{}-".format(tri_val_str)
+
+    if item.type == BOOL:
+        return "[{}]".format(tri_val_str)
+
+    # item.type == TRISTATE
+    if item.assignable == (1, 2):
+        return "{{{}}}".format(tri_val_str)  # {M}/{*}
+    return "<{}>".format(tri_val_str)
+
+
+def _is_y_mode_choice_sym(item):
+    # The choice mode is an upper bound on the visibility of choice symbols, so
+    # we can check the choice symbols' own visibility to see if the choice is
+    # in y mode
+    return isinstance(item, Symbol) and item.choice and item.visibility == 2
+
+
+def _check_valid(sym, s):
+    # Returns True if the string 's' is a well-formed value for 'sym'.
+    # Otherwise, displays an error and returns False.
+
+    if sym.orig_type not in (INT, HEX):
+        return True  # Anything goes for non-int/hex symbols
+
+    base = 10 if sym.orig_type == INT else 16
+    try:
+        int(s, base)
+    except ValueError:
+        _error("'{}' is a malformed {} value"
+               .format(s, TYPE_TO_STR[sym.orig_type]))
+        return False
+
+    for low_sym, high_sym, cond in sym.ranges:
+        if expr_value(cond):
+            low_s = low_sym.str_value
+            high_s = high_sym.str_value
+
+            if not int(low_s, base) <= int(s, base) <= int(high_s, base):
+                _error("{} is outside the range {}-{}"
+                       .format(s, low_s, high_s))
+                return False
+
+            break
+
+    return True
+
+
+def _range_info(sym):
+    # Returns a string with information about the valid range for the symbol
+    # 'sym', or None if 'sym' doesn't have a range
+
+    if sym.orig_type in (INT, HEX):
+        for low, high, cond in sym.ranges:
+            if expr_value(cond):
+                return "Range: {}-{}".format(low.str_value, high.str_value)
+
+    return None
+
+
+def _is_num(name):
+    # Heuristic to see if a symbol name looks like a number, for nicer output
+    # when printing expressions. Things like 16 are actually symbol names, only
+    # they get their name as their value when the symbol is undefined.
+
+    try:
+        int(name)
+    except ValueError:
+        if not name.startswith(("0x", "0X")):
+            return False
+
+        try:
+            int(name, 16)
+        except ValueError:
+            return False
+
+    return True
+
+
+def _getch_compat(win):
+    # Uses get_wch() if available (Python 3.3+) and getch() otherwise.
+    #
+    # Also falls back on getch() if get_wch() raises curses.error, to work
+    # around an issue when resizing the terminal on at least macOS Catalina.
+    # See https://github.com/ulfalizer/Kconfiglib/issues/84.
+    #
+    # Also handles a PDCurses resizing quirk.
+
+    try:
+        c = win.get_wch()
+    except (AttributeError, curses.error):
+        c = win.getch()
+        if 0 <= c <= 255:
+            c = chr(c)
+
+    # Decent resizing behavior on PDCurses requires calling resize_term(0, 0)
+    # after receiving KEY_RESIZE, while ncurses (usually) handles terminal
+    # resizing automatically in get(_w)ch() (see the end of the
+    # resizeterm(3NCURSES) man page).
+    #
+    # resize_term(0, 0) reliably fails and does nothing on ncurses, so this
+    # hack gives ncurses/PDCurses compatibility for resizing. I don't know
+    # whether it would cause trouble for other implementations.
+    if c == curses.KEY_RESIZE:
+        try:
+            curses.resize_term(0, 0)
+        except curses.error:
+            pass
+
+    return c
+
+
+def _warn(*args):
+    # Temporarily returns from curses to shell mode and prints a warning to
+    # stderr. The warning would get lost in curses mode.
+    curses.endwin()
+    print("menuconfig warning: ", end="", file=sys.stderr)
+    print(*args, file=sys.stderr)
+    curses.doupdate()
+
+
+# Ignore exceptions from some functions that might fail, e.g. for small
+# windows. They usually do reasonable things anyway.
+
+
+def _safe_curs_set(visibility):
+    try:
+        curses.curs_set(visibility)
+    except curses.error:
+        pass
+
+
+def _safe_addstr(win, *args):
+    # Clip the line to avoid wrapping to the next line, which looks glitchy.
+    # addchstr() would do it for us, but it's not available in the 'curses'
+    # module.
+
+    attr = None
+    if isinstance(args[0], str):
+        y, x = win.getyx()
+        s = args[0]
+        if len(args) == 2:
+            attr = args[1]
+    else:
+        y, x, s = args[:3]
+        if len(args) == 4:
+            attr = args[3]
+
+    maxlen = _width(win) - x
+    s = s.expandtabs()
+
+    try:
+        # The 'curses' module uses wattr_set() internally if you pass 'attr',
+        # overwriting the background style, so setting 'attr' to 0 in the first
+        # case won't do the right thing
+        if attr is None:
+            win.addnstr(y, x, s, maxlen)
+        else:
+            win.addnstr(y, x, s, maxlen, attr)
+    except curses.error:
+        pass
+
+
+def _safe_addch(win, *args):
+    try:
+        win.addch(*args)
+    except curses.error:
+        pass
+
+
+def _safe_hline(win, *args):
+    try:
+        win.hline(*args)
+    except curses.error:
+        pass
+
+
+def _safe_vline(win, *args):
+    try:
+        win.vline(*args)
+    except curses.error:
+        pass
+
+
+def _safe_move(win, *args):
+    try:
+        win.move(*args)
+    except curses.error:
+        pass
+
+
+def _change_c_lc_ctype_to_utf8():
+    # See _CHANGE_C_LC_CTYPE_TO_UTF8
+
+    if _IS_WINDOWS:
+        # Windows rarely has issues here, and the PEP 538 implementation avoids
+        # changing the locale on it. None of the UTF-8 locales below were
+        # supported from some quick testing either. Play it safe.
+        return
+
+    def try_set_locale(loc):
+        try:
+            locale.setlocale(locale.LC_CTYPE, loc)
+            return True
+        except locale.Error:
+            return False
+
+    # Is LC_CTYPE set to the C locale?
+    if locale.setlocale(locale.LC_CTYPE) == "C":
+        # This list was taken from the PEP 538 implementation in the CPython
+        # code, in Python/pylifecycle.c
+        for loc in "C.UTF-8", "C.utf8", "UTF-8":
+            if try_set_locale(loc):
+                # LC_CTYPE successfully changed
+                return
+
+
+if __name__ == "__main__":
+    _main()
diff --git a/scripts/Kconfiglib/oldconfig.py b/scripts/Kconfiglib/oldconfig.py
new file mode 100755
index 0000000..53434b2
--- /dev/null
+++ b/scripts/Kconfiglib/oldconfig.py
@@ -0,0 +1,246 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Implements oldconfig functionality.
+
+  1. Loads existing .config
+  2. Prompts for the value of all modifiable symbols/choices that
+     aren't already set in the .config
+  3. Writes an updated .config
+
+The default input/output filename is '.config'. A different filename can be
+passed in the KCONFIG_CONFIG environment variable.
+
+When overwriting a configuration file, the old version is saved to
+<filename>.old (e.g. .config.old).
+
+Entering '?' displays the help text of the symbol/choice, if any.
+
+Unlike 'make oldconfig', this script doesn't print menu titles and comments,
+but gives Kconfig definition locations. Printing menus and comments would be
+pretty easy to add: Look at the parents of each item, and print all menu
+prompts and comments unless they have already been printed (assuming you want
+to skip "irrelevant" menus).
+"""
+from __future__ import print_function
+
+import sys
+
+from kconfiglib import Symbol, Choice, BOOL, TRISTATE, HEX, standard_kconfig
+
+
+# Python 2/3 compatibility hack
+if sys.version_info[0] < 3:
+    input = raw_input
+
+
+def _main():
+    # Earlier symbols in Kconfig files might depend on later symbols and become
+    # visible if their values change. This flag is set to True if the value of
+    # any symbol changes, in which case we rerun the oldconfig to check for new
+    # visible symbols.
+    global conf_changed
+
+    kconf = standard_kconfig(__doc__)
+    print(kconf.load_config())
+
+    while True:
+        conf_changed = False
+
+        for node in kconf.node_iter():
+            oldconfig(node)
+
+        if not conf_changed:
+            break
+
+    print(kconf.write_config())
+
+
+def oldconfig(node):
+    """
+    Prompts the user for a value if node.item is a visible symbol/choice with
+    no user value.
+    """
+    # See main()
+    global conf_changed
+
+    # Only symbols and choices can be configured
+    if not isinstance(node.item, (Symbol, Choice)):
+        return
+
+    # Skip symbols and choices that aren't visible
+    if not node.item.visibility:
+        return
+
+    # Skip symbols and choices that don't have a prompt (at this location)
+    if not node.prompt:
+        return
+
+    if isinstance(node.item, Symbol):
+        sym = node.item
+
+        # Skip symbols that already have a user value
+        if sym.user_value is not None:
+            return
+
+        # Skip symbols that can only have a single value, due to selects
+        if len(sym.assignable) == 1:
+            return
+
+        # Skip symbols in choices in y mode. We ask once for the entire choice
+        # instead.
+        if sym.choice and sym.choice.tri_value == 2:
+            return
+
+        # Loop until the user enters a valid value or enters a blank string
+        # (for the default value)
+        while True:
+            val = input("{} ({}) [{}] ".format(
+                node.prompt[0], _name_and_loc_str(sym),
+                _default_value_str(sym)))
+
+            if val == "?":
+                _print_help(node)
+                continue
+
+            # Substitute a blank string with the default value the symbol
+            # would get
+            if not val:
+                val = sym.str_value
+
+            # Automatically add a "0x" prefix for hex symbols, like the
+            # menuconfig interface does. This isn't done when loading .config
+            # files, hence why set_value() doesn't do it automatically.
+            if sym.type == HEX and not val.startswith(("0x", "0X")):
+                val = "0x" + val
+
+            old_str_val = sym.str_value
+
+            # Kconfiglib itself will print a warning here if the value
+            # is invalid, so we don't need to bother
+            if sym.set_value(val):
+                # Valid value input. We're done with this node.
+
+                if sym.str_value != old_str_val:
+                    conf_changed = True
+
+                return
+
+    else:
+        choice = node.item
+
+        # Skip choices that already have a visible user selection...
+        if choice.user_selection and choice.user_selection.visibility == 2:
+            # ...unless there are new visible symbols in the choice. (We know
+            # they have y (2) visibility in that case, because m-visible
+            # symbols get demoted to n-visibility in y-mode choices, and the
+            # user-selected symbol had visibility y.)
+            for sym in choice.syms:
+                if sym is not choice.user_selection and sym.visibility and \
+                   sym.user_value is None:
+                    # New visible symbols in the choice
+                    break
+            else:
+                # No new visible symbols in the choice
+                return
+
+        # Get a list of available selections. The mode of the choice limits
+        # the visibility of the choice value symbols, so this will indirectly
+        # skip choices in n and m mode.
+        options = [sym for sym in choice.syms if sym.visibility == 2]
+
+        if not options:
+            # No y-visible choice value symbols
+            return
+
+        # Loop until the user enters a valid selection or a blank string (for
+        # the default selection)
+        while True:
+            print("{} ({})".format(node.prompt[0], _name_and_loc_str(choice)))
+
+            for i, sym in enumerate(options, 1):
+                print("{} {}. {} ({})".format(
+                    ">" if sym is choice.selection else " ",
+                    i,
+                    # Assume people don't define choice symbols with multiple
+                    # prompts. That generates a warning anyway.
+                    sym.nodes[0].prompt[0],
+                    sym.name))
+
+            sel_index = input("choice[1-{}]: ".format(len(options)))
+
+            if sel_index == "?":
+                _print_help(node)
+                continue
+
+            # Pick the default selection if the string is blank
+            if not sel_index:
+                choice.selection.set_value(2)
+                break
+
+            try:
+                sel_index = int(sel_index)
+            except ValueError:
+                print("Bad index", file=sys.stderr)
+                continue
+
+            if not 1 <= sel_index <= len(options):
+                print("Bad index", file=sys.stderr)
+                continue
+
+            # Valid selection
+
+            if options[sel_index - 1].tri_value != 2:
+                conf_changed = True
+
+            options[sel_index - 1].set_value(2)
+            break
+
+        # Give all of the non-selected visible choice symbols the user value n.
+        # This makes it so that the choice is no longer considered new once we
+        # do additional passes, if the reason that it was considered new was
+        # that it had new visible choice symbols.
+        #
+        # Only giving visible choice symbols the user value n means we will
+        # prompt for the choice again if later user selections make more new
+        # choice symbols visible, which is correct.
+        for sym in choice.syms:
+            if sym is not choice.user_selection and sym.visibility:
+                sym.set_value(0)
+
+
+def _name_and_loc_str(sc):
+    # Helper for printing the name of the symbol/choice 'sc' along with the
+    # location(s) in the Kconfig files where it is defined. Unnamed choices
+    # return "choice" instead of the name.
+
+    return "{}, defined at {}".format(
+        sc.name or "choice",
+        ", ".join("{}:{}".format(node.filename, node.linenr)
+                  for node in sc.nodes))
+
+
+def _print_help(node):
+    print("\n" + (node.help or "No help text\n"))
+
+
+def _default_value_str(sym):
+    # Returns the "m/M/y" string in e.g.
+    #
+    #   TRISTATE_SYM prompt (TRISTATE_SYM, defined at Kconfig:9) [n/M/y]:
+    #
+    # For string/int/hex, returns the default value as-is.
+
+    if sym.type in (BOOL, TRISTATE):
+        return "/".join(("NMY" if sym.tri_value == tri else "nmy")[tri]
+                        for tri in sym.assignable)
+
+    # string/int/hex
+    return sym.str_value
+
+
+if __name__ == "__main__":
+    _main()
diff --git a/scripts/Kconfiglib/olddefconfig.py b/scripts/Kconfiglib/olddefconfig.py
new file mode 100755
index 0000000..2dadfb4
--- /dev/null
+++ b/scripts/Kconfiglib/olddefconfig.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Updates an old .config file or creates a new one, by filling in default values
+for all new symbols. This is the same as picking the default selection for all
+symbols in oldconfig, or entering the menuconfig interface and immediately
+saving.
+
+The default input/output filename is '.config'. A different filename can be
+passed in the KCONFIG_CONFIG environment variable.
+
+When overwriting a configuration file, the old version is saved to
+<filename>.old (e.g. .config.old).
+"""
+import kconfiglib
+
+
+def main():
+    kconf = kconfiglib.standard_kconfig(__doc__)
+    print(kconf.load_config())
+    print(kconf.write_config())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/Kconfiglib/savedefconfig.py b/scripts/Kconfiglib/savedefconfig.py
new file mode 100755
index 0000000..0f36bde
--- /dev/null
+++ b/scripts/Kconfiglib/savedefconfig.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Saves a minimal configuration file that only lists symbols that differ in value
+from their defaults. Loading such a configuration file is equivalent to loading
+the "full" configuration file.
+
+Minimal configuration files are handy to start from when editing configuration
+files by hand.
+
+The default input configuration file is '.config'. A different input filename
+can be passed in the KCONFIG_CONFIG environment variable.
+
+Note: Minimal configurations can also be generated from within the menuconfig
+interface.
+"""
+import argparse
+
+import kconfiglib
+
+
+def main():
+    parser = argparse.ArgumentParser(
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        description=__doc__)
+
+    parser.add_argument(
+        "--kconfig",
+        default="Kconfig",
+        help="Top-level Kconfig file (default: Kconfig)")
+
+    parser.add_argument(
+        "--out",
+        metavar="MINIMAL_CONFIGURATION",
+        default="defconfig",
+        help="Output filename for minimal configuration (default: defconfig)")
+
+    args = parser.parse_args()
+
+    kconf = kconfiglib.Kconfig(args.kconfig, suppress_traceback=True)
+    print(kconf.load_config())
+    print(kconf.write_min_config(args.out))
+
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/Kconfiglib/setconfig.py b/scripts/Kconfiglib/setconfig.py
new file mode 100755
index 0000000..f9cf5cd
--- /dev/null
+++ b/scripts/Kconfiglib/setconfig.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Simple utility for setting configuration values from the command line.
+
+Sample usage:
+
+  $ setconfig FOO_SUPPORT=y BAR_BITS=8
+
+Note: Symbol names should not be prefixed with 'CONFIG_'.
+
+The exit status on errors is 1.
+
+The default input/output configuration file is '.config'. A different filename
+can be passed in the KCONFIG_CONFIG environment variable.
+
+When overwriting a configuration file, the old version is saved to
+<filename>.old (e.g. .config.old).
+"""
+import argparse
+import sys
+
+import kconfiglib
+
+
+def main():
+    parser = argparse.ArgumentParser(
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        description=__doc__)
+
+    parser.add_argument(
+        "--kconfig",
+        default="Kconfig",
+        help="Top-level Kconfig file (default: Kconfig)")
+
+    parser.add_argument(
+        "--no-check-exists",
+        dest="check_exists",
+        action="store_false",
+        help="Ignore assignments to non-existent symbols instead of erroring "
+             "out")
+
+    parser.add_argument(
+        "--no-check-value",
+        dest="check_value",
+        action="store_false",
+        help="Ignore assignments that didn't \"take\" (where the symbol got a "
+             "different value, e.g. due to unsatisfied dependencies) instead "
+             "of erroring out")
+
+    parser.add_argument(
+        "assignments",
+        metavar="ASSIGNMENT",
+        nargs="*",
+        help="A 'NAME=value' assignment")
+
+    args = parser.parse_args()
+
+    kconf = kconfiglib.Kconfig(args.kconfig, suppress_traceback=True)
+    print(kconf.load_config())
+
+    for arg in args.assignments:
+        if "=" not in arg:
+            sys.exit("error: no '=' in assignment: '{}'".format(arg))
+        name, value = arg.split("=", 1)
+
+        if name not in kconf.syms:
+            if not args.check_exists:
+                continue
+            sys.exit("error: no symbol '{}' in configuration".format(name))
+
+        sym = kconf.syms[name]
+
+        if not sym.set_value(value):
+            sys.exit("error: '{}' is an invalid value for the {} symbol {}"
+                     .format(value, kconfiglib.TYPE_TO_STR[sym.orig_type],
+                             name))
+
+        if args.check_value and sym.str_value != value:
+            sys.exit("error: {} was assigned the value '{}', but got the "
+                     "value '{}'. Check the symbol's dependencies, and make "
+                     "sure that it has a prompt."
+                     .format(name, value, sym.str_value))
+
+    print(kconf.write_config())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/Kconfiglib/setup.cfg b/scripts/Kconfiglib/setup.cfg
new file mode 100644
index 0000000..185845e
--- /dev/null
+++ b/scripts/Kconfiglib/setup.cfg
@@ -0,0 +1,7 @@
+[bdist_wheel]
+# We support both Python 2 and Python 3
+universal = 1
+
+[metadata]
+# Include the license file in wheels
+license_file = LICENSE.txt
diff --git a/scripts/Kconfiglib/setup.py b/scripts/Kconfiglib/setup.py
new file mode 100644
index 0000000..8aa2cc4
--- /dev/null
+++ b/scripts/Kconfiglib/setup.py
@@ -0,0 +1,96 @@
+import io
+import os
+
+import setuptools
+
+
+setuptools.setup(
+    name="kconfiglib",
+    # MAJOR.MINOR.PATCH, per http://semver.org
+    version="14.1.0",
+    description="A flexible Python Kconfig implementation",
+
+    # Make sure that README.rst decodes on Python 3 in environments that use
+    # the C locale (which implies ASCII), by explicitly giving the encoding.
+    #
+    # io.open() has the 'encoding' parameter on both Python 2 and 3. open()
+    # doesn't have it on Python 2. This lets us use the same code for both.
+    long_description=io.open(
+        os.path.join(os.path.dirname(__file__), "README.rst"),
+        encoding="utf-8"
+    ).read(),
+
+    url="https://github.com/ulfalizer/Kconfiglib",
+    author='Ulf "Ulfalizer" Magnusson',
+    author_email="ulfalizer at gmail.com",
+    keywords="kconfig, kbuild, menuconfig, configuration-management",
+    license="ISC",
+
+    py_modules=(
+        "kconfiglib",
+        "menuconfig",
+        "guiconfig",
+        "genconfig",
+        "oldconfig",
+        "olddefconfig",
+        "savedefconfig",
+        "defconfig",
+        "alldefconfig",
+        "allnoconfig",
+        "allmodconfig",
+        "allyesconfig",
+        "listnewconfig",
+        "setconfig",
+    ),
+
+    entry_points={
+        "console_scripts": (
+            "menuconfig = menuconfig:_main",
+            "guiconfig = guiconfig:_main",
+            "genconfig = genconfig:main",
+            "oldconfig = oldconfig:_main",
+            "olddefconfig = olddefconfig:main",
+            "savedefconfig = savedefconfig:main",
+            "defconfig = defconfig:main",
+            "alldefconfig = alldefconfig:main",
+            "allnoconfig = allnoconfig:main",
+            "allmodconfig = allmodconfig:main",
+            "allyesconfig = allyesconfig:main",
+            "listnewconfig = listnewconfig:main",
+            "setconfig = setconfig:main",
+        )
+    },
+
+    # Note: windows-curses is not automatically installed on Windows anymore,
+    # because it made Kconfiglib impossible to install on MSYS2 with pip
+
+    # Needs support for unnumbered {} in format() and argparse
+    python_requires=">=2.7,!=3.0.*,!=3.1.*",
+
+    project_urls={
+        "GitHub repository": "https://github.com/ulfalizer/Kconfiglib",
+        "Examples": "https://github.com/ulfalizer/Kconfiglib/tree/master/examples",
+    },
+
+    classifiers=[
+        "Development Status :: 5 - Production/Stable",
+        "Intended Audience :: Developers",
+        "Topic :: Software Development :: Build Tools",
+        "Topic :: System :: Operating System Kernels :: Linux",
+        "License :: OSI Approved :: ISC License (ISCL)",
+        "Operating System :: POSIX",
+        "Operating System :: Microsoft :: Windows",
+        "Programming Language :: Python :: 2",
+        "Programming Language :: Python :: 2.7",
+        "Programming Language :: Python :: 3",
+        "Programming Language :: Python :: 3.2",
+        "Programming Language :: Python :: 3.3",
+        "Programming Language :: Python :: 3.4",
+        "Programming Language :: Python :: 3.5",
+        "Programming Language :: Python :: 3.6",
+        "Programming Language :: Python :: 3.7",
+        "Programming Language :: Python :: 3.8",
+        "Programming Language :: Python :: Implementation :: CPython",
+        "Programming Language :: Python :: Implementation :: PyPy",
+    ]
+)
diff --git a/scripts/Kconfiglib/tests/Kappend b/scripts/Kconfiglib/tests/Kappend
new file mode 100644
index 0000000..ce1478f
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kappend
@@ -0,0 +1,12 @@
+config MODULES
+    def_bool y
+
+config BOOL
+    bool "bool 1"
+
+config STRING
+    string "string"
+
+config IGNOREME
+    bool "ignore me"
+    default y
diff --git a/scripts/Kconfiglib/tests/Kassignable b/scripts/Kconfiglib/tests/Kassignable
new file mode 100644
index 0000000..26b4177
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kassignable
@@ -0,0 +1,230 @@
+config MODULES
+	bool "modules"
+	option modules
+
+
+# Things that should never be .assignable
+
+if UNDEFINED && "const"
+endif
+
+config NO_PROMPT
+	bool
+
+config STRING
+	string "string"
+
+config INT
+	int "int"
+
+config HEX
+	hex "hex"
+
+
+# Non-selected symbols
+
+config Y_VIS_BOOL
+	bool "y-vis bool"
+
+config M_VIS_BOOL
+	bool "m-vis bool" if m
+
+config N_VIS_BOOL
+	bool "n-vis bool" if n
+
+config Y_VIS_TRI
+	tristate "y-vis tri"
+
+config M_VIS_TRI
+	tristate "m-vis tri" if m
+
+config N_VIS_TRI
+	tristate "n-vis tri" if n
+
+
+# Symbols selected to y
+
+config Y_SELECTOR
+	def_tristate y
+
+	select Y_SEL_Y_VIS_BOOL
+	select Y_SEL_M_VIS_BOOL
+	select Y_SEL_N_VIS_BOOL
+
+	select Y_SEL_Y_VIS_TRI
+	select Y_SEL_M_VIS_TRI
+	select Y_SEL_N_VIS_TRI
+
+config Y_SEL_Y_VIS_BOOL
+	bool "y-sel y-vis bool"
+
+config Y_SEL_M_VIS_BOOL
+	bool "y-sel m-vis bool" if m
+
+config Y_SEL_N_VIS_BOOL
+	bool "y-sel n-vis bool" if n
+
+config Y_SEL_Y_VIS_TRI
+	tristate "y-sel y-vis tri"
+
+config Y_SEL_M_VIS_TRI
+	tristate "y-sel m-vis tri" if m
+
+config Y_SEL_N_VIS_TRI
+	tristate "y-sel n-vis tri" if n
+
+
+# Symbols selected to m
+
+config M_SELECTOR
+	def_tristate m
+
+	select M_SEL_Y_VIS_BOOL
+	select M_SEL_M_VIS_BOOL
+	select M_SEL_N_VIS_BOOL
+
+	select M_SEL_Y_VIS_TRI
+	select M_SEL_M_VIS_TRI
+	select M_SEL_N_VIS_TRI
+
+config M_SEL_Y_VIS_BOOL
+	bool "m-sel y-vis bool"
+
+config M_SEL_M_VIS_BOOL
+	bool "m-sel m-vis bool" if m
+
+config M_SEL_N_VIS_BOOL
+	bool "m-sel n-vis bool" if n
+
+config M_SEL_Y_VIS_TRI
+	tristate "m-sel y-vis tri"
+
+config M_SEL_M_VIS_TRI
+	tristate "m-sel m-vis tri" if m
+
+config M_SEL_N_VIS_TRI
+	tristate "m-sel n-vis tri" if n
+
+
+# Symbols implied to y
+
+config Y_IMPLIER
+	def_tristate y
+
+	imply Y_IMP_Y_VIS_BOOL
+	imply Y_IMP_M_VIS_BOOL
+	imply Y_IMP_N_VIS_BOOL
+
+	imply Y_IMP_Y_VIS_TRI
+	imply Y_IMP_M_VIS_TRI
+	imply Y_IMP_N_VIS_TRI
+
+config Y_IMP_Y_VIS_BOOL
+	bool "y-imp y-vis bool"
+
+config Y_IMP_M_VIS_BOOL
+	bool "y-imp m-vis bool" if m
+
+config Y_IMP_N_VIS_BOOL
+	bool "y-imp n-vis bool" if n
+
+config Y_IMP_Y_VIS_TRI
+	tristate "y-imp y-vis tri"
+
+config Y_IMP_M_VIS_TRI
+	tristate "y-imp m-vis tri" if m
+
+config Y_IMP_N_VIS_TRI
+	tristate "y-imp n-vis tri" if n
+
+
+# Symbols implied to m (never affects assignable values)
+
+config M_IMPLIER
+	def_tristate m
+
+	imply M_IMP_Y_VIS_BOOL
+	imply M_IMP_M_VIS_BOOL
+	imply M_IMP_N_VIS_BOOL
+
+	imply M_IMP_Y_VIS_TRI
+	imply M_IMP_M_VIS_TRI
+	imply M_IMP_N_VIS_TRI
+
+config M_IMP_Y_VIS_BOOL
+	bool "m-imp y-vis bool"
+
+config M_IMP_M_VIS_BOOL
+	bool "m-imp m-vis bool" if m
+
+config M_IMP_N_VIS_BOOL
+	bool "m-imp n-vis bool" if n
+
+config M_IMP_Y_VIS_TRI
+	tristate "m-imp y-vis tri"
+
+config M_IMP_M_VIS_TRI
+	tristate "m-imp m-vis tri" if m
+
+config M_IMP_N_VIS_TRI
+	tristate "m-imp n-vis tri" if n
+
+
+# Symbols in y-mode choice
+
+choice Y_CHOICE
+	bool "y-mode choice"
+
+config Y_CHOICE_BOOL
+	bool "y-mode choice bool"
+
+config Y_CHOICE_TRISTATE
+	tristate "y-mode choice tristate"
+
+config Y_CHOICE_N_VIS_TRISTATE
+	tristate "y-mode choice tristate invisible" if n
+
+endchoice
+
+
+# Symbols in m/y-mode choice
+
+choice MY_CHOICE
+	tristate "m/y-mode choice"
+
+config MY_CHOICE_BOOL
+	bool "m/y-mode choice bool"
+
+config MY_CHOICE_TRISTATE
+	tristate "m/y-mode choice tristate"
+
+config MY_CHOICE_N_VIS_TRISTATE
+	tristate "m/y-mode choice tristate invisible" if n
+
+endchoice
+
+
+# Choices with some other possible modes
+
+choice NMY_CHOICE
+	tristate "n/m/y-mode choice"
+	optional
+endchoice
+
+choice NY_CHOICE
+	bool "n/y-mode choice"
+	optional
+endchoice
+
+choice NM_CHOICE
+	tristate "n/m-mode choice" if m
+	optional
+endchoice
+
+choice M_CHOICE
+	tristate "m-mode choice" if m
+endchoice
+
+choice N_CHOICE
+	tristate "n-mode choice" if n
+endchoice
diff --git a/scripts/Kconfiglib/tests/Kchoice b/scripts/Kconfiglib/tests/Kchoice
new file mode 100644
index 0000000..16b38d4
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kchoice
@@ -0,0 +1,198 @@
+config MODULES
+    bool "modules"
+
+# bool/tristate and optional
+
+choice BOOL
+    bool "bool"
+config B_1
+    tristate "B_1"
+config B_2
+    tristate "B_2"
+endchoice
+
+choice BOOL_OPT
+    bool "bool optional"
+    optional
+config BO_1
+    tristate "BO_1"
+config BO_2
+    tristate "BO_2"
+endchoice
+
+choice TRISTATE
+    tristate "tristate"
+config T_1
+    tristate "T_1"
+config T_2
+    tristate "T_2"
+endchoice
+
+choice TRISTATE_OPT
+    tristate "tristate optional"
+    optional
+config TO_1
+    tristate "TO_1"
+config TO_2
+    tristate "TO_2"
+endchoice
+
+# m-visibility
+
+choice BOOL_M
+    bool "bool m" if m
+config BM_1
+    tristate "BM_1"
+config BM_2
+    tristate "BM_2"
+endchoice
+
+choice TRISTATE_M
+    tristate "tristate m" if m
+config TM_1
+    tristate "TM_1"
+config TM_2
+    tristate "TM_2"
+endchoice
+
+# Defaults
+
+config TRISTATE_SYM
+    tristate "tristate"
+
+choice DEFAULTS
+    bool "defaults"
+    default OPT_1 if n
+    default OPT_2 if TRISTATE_SYM
+    default OPT_4
+config OPT_1
+    tristate "OPT_1"
+config OPT_2
+    tristate "OPT_2"
+config OPT_3
+    tristate "OPT_3"
+config OPT_4
+    tristate "OPT_4"
+endchoice
+
+choice DEFAULTS_NOT_VISIBLE
+    bool "defaults not visible"
+    # Skipped due to condition
+    default OPT_6 if n
+    # Skipped because OPT_7 is not visible
+    default OPT_7
+    # This one should apply
+    default OPT_8
+config OPT_5
+    tristate "OPT_5"
+config OPT_6
+    tristate "OPT_6"
+config OPT_7
+    tristate "OPT_7" if n
+config OPT_8
+    tristate "OPT_8"
+config OPT_9
+    tristate "OPT_9"
+endchoice
+
+# Choices without an explicitly specified type should get the type of the first
+# symbol with a type
+
+choice NO_TYPE_BOOL
+    prompt "no type bool"
+config NTB_1
+    bool "NTB_1"
+config NTB_2
+    tristate "NTB_2"
+endchoice
+
+choice NO_TYPE_TRISTATE
+    prompt "no type tristate"
+config NTT_1
+config NTT_2
+    tristate "NTB_2"
+config NTT_3
+    bool "NTT_3"
+endchoice
+
+# Choice items without an explicitly specified type should get the type of the
+# choice
+
+choice MISSING_MEMBER_TYPES_1
+    bool "missing member types"
+config MMT_1
+config MMT_2
+config MMT_3
+    tristate
+endchoice
+
+choice MISSING_MEMBER_TYPES_2
+config MMT_4
+config MMT_5
+    bool
+endchoice
+
+# Choice where the default selection (the first symbol) depends on another
+# symbol. If that symbol becomes 'n', the default selection should change to
+# the first visible symbol in the choice.
+
+choice DEFAULT_WITH_DEP
+    bool "default with dep"
+
+config A
+    bool "A"
+    depends on DEP
+
+config B
+    bool "B"
+
+endchoice
+
+config DEP
+    bool "dep"
+
+# Choice with symbols that shouldn't be considered choice symbols because they
+# depend on the preceding symbol. This might be a kconfig bug, but some things
+# use it, so we need to emulate it.
+
+choice WEIRD_SYMS
+    bool "weird symbols that aren't considered part of the choice"
+
+# Only WS1 is part of the choice
+config WS1
+    bool "WS1"
+
+config WS2
+    bool "WS2"
+    depends on WS1
+
+config WS3
+    bool
+    depends on WS2
+
+config WS4
+    bool
+    depends on WS1
+
+config WS5
+    bool "WS5" if WS1
+
+# 'if' has the same effect, so only WS6 is part of the choice
+config WS6
+    bool "WS6"
+
+if WS6
+
+config WS7
+    bool
+
+config WS8
+    bool "WS8"
+
+endif
+
+# Should also be part of the choice
+config WS9
+    bool "WS9"
+
+endchoice
diff --git a/scripts/Kconfiglib/tests/Kdefconfig_existent b/scripts/Kconfiglib/tests/Kdefconfig_existent
new file mode 100644
index 0000000..304cae6
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kdefconfig_existent
@@ -0,0 +1,8 @@
+# $FOO is "defconfig_2"
+
+config A
+    string
+    option defconfig_list
+    default "Kconfiglib/tests/defconfig_1" if y && !n && n
+    default "Kconfiglib/tests/$FOO"
+    default "Kconfiglib/tests/defconfig_1"
diff --git a/scripts/Kconfiglib/tests/Kdefconfig_existent_but_n b/scripts/Kconfiglib/tests/Kdefconfig_existent_but_n
new file mode 100644
index 0000000..2fdaaa9
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kdefconfig_existent_but_n
@@ -0,0 +1,10 @@
+# $FOO is "defconfig_2"
+# Should produce None due to the "depends on n"
+
+config A
+    string
+    depends on n
+    option defconfig_list
+    default "Kconfiglib/tests/defconfig_1" if y && !n && n
+    default "Kconfiglib/tests/$FOO"
+    default "Kconfiglib/tests/defconfig_1"
diff --git a/scripts/Kconfiglib/tests/Kdefconfig_nonexistent b/scripts/Kconfiglib/tests/Kdefconfig_nonexistent
new file mode 100644
index 0000000..5b7230f
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kdefconfig_nonexistent
@@ -0,0 +1,5 @@
+config A
+    string
+    option defconfig_list
+    default "Kconfiglib/tests/non_existent_1"
+    default "Kconfiglib/tests/non_existent_2"
diff --git a/scripts/Kconfiglib/tests/Kdefconfig_srctree b/scripts/Kconfiglib/tests/Kdefconfig_srctree
new file mode 100644
index 0000000..d2591fa
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kdefconfig_srctree
@@ -0,0 +1,5 @@
+config A
+    string
+    option defconfig_list
+    default "sub/defconfig_in_sub" # Assume this doesn't exist
+    default "Kconfiglib/tests/defconfig_2"
diff --git a/scripts/Kconfiglib/tests/Kdepcopy b/scripts/Kconfiglib/tests/Kdepcopy
new file mode 100644
index 0000000..6abe898
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kdepcopy
@@ -0,0 +1,173 @@
+# We verify that the properties below end up in definition order
+
+config MULTIDEF
+    bool
+    default A
+    default B
+    select AA
+    imply AA
+
+if FOO
+
+config MULTIDEF
+    default C
+    default D
+    select BB
+    imply BB
+
+if BAR
+
+config MULTIDEF
+    default E
+    default F
+    select CC
+    imply CC
+
+menu "menu"
+
+config MULTIDEF
+    default G
+    default H
+    select DD
+    imply DD
+
+config MULTIDEF
+    default I
+    default J
+    select EE
+    imply EE
+
+endmenu
+
+config MULTIDEF
+    default K
+    default L
+    select FF
+    imply FF
+
+config MULTIDEF
+    default M
+    default N
+    select GG
+    imply GG
+
+endif
+
+config MULTIDEF
+    default O
+    default P
+    select HH
+    select II
+    imply HH
+    imply II
+
+endif
+
+config MULTIDEF
+    default Q
+    default R
+    select JJ
+    imply JJ
+
+
+# Same test with choice symbols involved
+
+config MULTIDEF_CHOICE
+    bool
+    select A
+
+choice
+    bool "choice"
+
+config MULTIDEF_CHOICE
+    bool "multidef choice"
+    select B
+
+endchoice
+
+config MULTIDEF_CHOICE
+    bool
+    select C
+
+
+# Same test with ranges involved
+
+config MULTIDEF_RANGE
+    int
+    range A _
+
+menu "menu"
+
+config MULTIDEF_RANGE
+    int
+    range B _
+
+if FOO
+
+config MULTIDEF_RANGE
+    int
+    range C _
+
+endif
+
+config MULTIDEF_RANGE
+    int
+    range D _
+
+endmenu
+
+config MULTIDEF_RANGE
+    int
+    range E _
+
+config MULTIDEF_RANGE
+    int
+    range F _
+
+
+# Same test for a choice
+
+choice MULTICHOICE
+    bool "choice"
+    default A
+
+config A
+    bool "A"
+
+config B
+    bool "B"
+
+config C
+    bool "C"
+
+config D
+    bool "C"
+
+config E
+    bool "C"
+
+endchoice
+
+if FOO
+
+choice MULTICHOICE
+    default B
+endchoice
+
+menu "menu"
+
+choice MULTICHOICE
+    default C
+endchoice
+
+endmenu
+
+choice MULTICHOICE
+    default D
+endchoice
+
+endif
+
+choice MULTICHOICE
+    default E
+endchoice
diff --git a/scripts/Kconfiglib/tests/Kdeploop0 b/scripts/Kconfiglib/tests/Kdeploop0
new file mode 100644
index 0000000..98d3e3c
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kdeploop0
@@ -0,0 +1,3 @@
+config FOO
+    bool
+    depends on FOO
diff --git a/scripts/Kconfiglib/tests/Kdeploop1 b/scripts/Kconfiglib/tests/Kdeploop1
new file mode 100644
index 0000000..134cd29
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kdeploop1
@@ -0,0 +1,3 @@
+config FOO
+    bool
+    select FOO
diff --git a/scripts/Kconfiglib/tests/Kdeploop10 b/scripts/Kconfiglib/tests/Kdeploop10
new file mode 100644
index 0000000..2e616ae
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kdeploop10
@@ -0,0 +1,48 @@
+config A
+    bool
+    depends on B
+
+config B
+    bool
+    depends on C = 7
+
+config C
+    int
+    range D 8
+
+config D
+    int
+    default 3 if E
+    default 8
+
+config E
+    bool
+
+config F
+    bool
+    select E if G
+
+config G
+    bool
+    depends on H
+
+choice
+    bool "choice"
+
+config H
+    bool "H"
+    depends on I
+
+endchoice
+
+choice
+    bool "choice" if J
+
+config I
+    bool "I"
+
+endchoice
+
+config J
+    bool
+    depends on A
diff --git a/scripts/Kconfiglib/tests/Kdeploop2 b/scripts/Kconfiglib/tests/Kdeploop2
new file mode 100644
index 0000000..c997243
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kdeploop2
@@ -0,0 +1,3 @@
+config FOO
+    bool
+    default FOO
diff --git a/scripts/Kconfiglib/tests/Kdeploop3 b/scripts/Kconfiglib/tests/Kdeploop3
new file mode 100644
index 0000000..90c83d5
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kdeploop3
@@ -0,0 +1,3 @@
+config FOO
+    bool
+    default y if FOO
diff --git a/scripts/Kconfiglib/tests/Kdeploop4 b/scripts/Kconfiglib/tests/Kdeploop4
new file mode 100644
index 0000000..789d8b7
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kdeploop4
@@ -0,0 +1,7 @@
+config FOO
+    bool
+    depends on BAR
+
+config BAR
+    bool
+    depends on FOO
diff --git a/scripts/Kconfiglib/tests/Kdeploop5 b/scripts/Kconfiglib/tests/Kdeploop5
new file mode 100644
index 0000000..f12fe6b
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kdeploop5
@@ -0,0 +1,7 @@
+config FOO
+    bool
+    select BAR
+
+config BAR
+    bool
+    select FOO
diff --git a/scripts/Kconfiglib/tests/Kdeploop6 b/scripts/Kconfiglib/tests/Kdeploop6
new file mode 100644
index 0000000..cb1e701
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kdeploop6
@@ -0,0 +1,6 @@
+config FOO
+    bool
+
+config BAR
+    bool
+    select FOO if FOO
diff --git a/scripts/Kconfiglib/tests/Kdeploop7 b/scripts/Kconfiglib/tests/Kdeploop7
new file mode 100644
index 0000000..63d2c57
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kdeploop7
@@ -0,0 +1,11 @@
+choice
+    bool "choice"
+
+config FOO
+    bool "foo"
+    depends on BAR
+
+config BAR
+    bool "bar"
+
+endchoice
diff --git a/scripts/Kconfiglib/tests/Kdeploop8 b/scripts/Kconfiglib/tests/Kdeploop8
new file mode 100644
index 0000000..84efd8d
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kdeploop8
@@ -0,0 +1,8 @@
+choice
+    bool "choice"
+    default FOO if FOO
+
+config FOO
+    bool "foo"
+
+endchoice
diff --git a/scripts/Kconfiglib/tests/Kdeploop9 b/scripts/Kconfiglib/tests/Kdeploop9
new file mode 100644
index 0000000..939f7f4
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kdeploop9
@@ -0,0 +1,7 @@
+choice
+    bool "choice" if FOO
+
+config FOO
+    bool "foo"
+
+endchoice
diff --git a/scripts/Kconfiglib/tests/Kdirdep b/scripts/Kconfiglib/tests/Kdirdep
new file mode 100644
index 0000000..cbb88b9
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kdirdep
@@ -0,0 +1,30 @@
+config NO_DEP_SYM
+    bool
+
+config DEP_SYM
+    bool
+    depends on A
+
+config DEP_SYM
+    depends on B && C
+
+config DEP_SYM
+    depends on !D
+
+
+choice NO_DEP_CHOICE
+    bool "no dep. choice"
+endchoice
+
+choice DEP_CHOICE
+    bool "dep. choice"
+    depends on A
+endchoice
+
+choice DEP_CHOICE
+    depends on B
+endchoice
+
+choice DEP_CHOICE
+    depends on C
+endchoice
diff --git a/scripts/Kconfiglib/tests/Kescape b/scripts/Kconfiglib/tests/Kescape
new file mode 100644
index 0000000..ee6b2b6
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kescape
@@ -0,0 +1,3 @@
+config STRING
+    string "string"
+    default "\"\\"
diff --git a/scripts/Kconfiglib/tests/Keval b/scripts/Kconfiglib/tests/Keval
new file mode 100644
index 0000000..1e64b83
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Keval
@@ -0,0 +1,34 @@
+# Enabled/disabled in the test
+config MODULES
+    bool "modules"
+    option modules
+
+config N
+    def_tristate n
+
+config M
+    def_tristate m
+
+menuconfig Y
+    def_tristate y
+    prompt "foo"
+
+config Y_STRING
+    string
+    default "y"
+
+config FOO_BAR_STRING
+    string
+    default "foo bar"
+
+config INT_37
+    int
+    default 37
+
+config HEX_0X37
+    hex
+    default 0x37
+
+config HEX_37
+    hex
+    default 37
diff --git a/scripts/Kconfiglib/tests/Kexpr_items b/scripts/Kconfiglib/tests/Kexpr_items
new file mode 100644
index 0000000..fb97ace
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kexpr_items
@@ -0,0 +1,11 @@
+config TEST
+    bool
+    default A && (B || !C && D = "E") || F > G || !!!H
+
+choice CHOICE
+    bool "choice"
+
+config TEST_CHOICE
+    bool "test choice" if A
+
+endchoice
diff --git a/scripts/Kconfiglib/tests/Kheader b/scripts/Kconfiglib/tests/Kheader
new file mode 100644
index 0000000..495afc6
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kheader
@@ -0,0 +1,5 @@
+# Used to test headers in .config and header files
+
+config FOO
+	bool "foo"
+	default y
diff --git a/scripts/Kconfiglib/tests/Khelp b/scripts/Kconfiglib/tests/Khelp
new file mode 100644
index 0000000..b80c2eb
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Khelp
@@ -0,0 +1,50 @@
+config TWO_HELP_STRINGS
+    bool
+    help
+      first help string
+
+
+
+
+config TWO_HELP_STRINGS
+    help
+      second help string
+
+config NO_BLANK_AFTER_HELP
+    bool
+    help
+      help for
+      NO_BLANK_AFTER_HELP
+choice CHOICE_HELP
+    bool "choice with help"
+    help
+  help for
+  CHOICE_HELP
+endchoice
+
+config HELP_TERMINATED_BY_COMMENT
+  bool
+  help
+ a
+ b
+ c
+#
+
+config TRICKY_HELP
+  bool
+  -help---
+
+
+  a
+   b
+    c
+
+   d
+    e
+     f
+
+
+  g
+   h
+    i
+#
diff --git a/scripts/Kconfiglib/tests/Kifremoval b/scripts/Kconfiglib/tests/Kifremoval
new file mode 100644
index 0000000..f94472c
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kifremoval
@@ -0,0 +1,99 @@
+# Test some tricky cases that give consecutive 'if' nodes even after
+# flattening. Simple cases are exercised a ton elsewhere.
+
+if X
+endif
+if X
+endif
+
+config A
+
+if X
+endif
+if X
+endif
+
+config B
+
+if X
+endif
+if X
+endif
+if X
+endif
+
+config C
+
+if X
+  if X
+    if X
+    endif
+    if X
+    endif
+  endif
+  if X
+    if X
+    endif
+    if X
+    endif
+  endif
+  config D
+endif
+if X
+endif
+
+menu "E"
+  if X
+    if X
+    endif
+  endif
+  if X
+    if X
+    endif
+  endif
+endmenu
+
+menu "F"
+  if X
+  endif
+  if X
+  endif
+  if X
+    if X
+    endif
+    if X
+    endif
+    menu "G"
+    endmenu
+  endif
+endmenu
+
+choice H
+  if X
+    if X
+    endif
+  endif
+  if X
+    if X
+    endif
+  endif
+endchoice
+
+choice I
+  if X
+  endif
+  if X
+  endif
+  if X
+    if X
+    endif
+    if X
+    endif
+    config J
+  endif
+endchoice
+
+if X
+endif
+if X
+endif
diff --git a/scripts/Kconfiglib/tests/Kimply b/scripts/Kconfiglib/tests/Kimply
new file mode 100644
index 0000000..3ce346f
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kimply
@@ -0,0 +1,145 @@
+config MODULES
+    def_bool y
+    option modules
+
+#
+# Implied symbols with unmet and met direct dependencies
+#
+
+config IMPLY_DIRECT_DEPS
+    def_tristate y
+    imply UNMET_DIRECT_1
+    imply UNMET_DIRECT_2
+    imply UNMET_DIRECT_3
+    imply MET_DIRECT_1
+    imply MET_DIRECT_2
+    imply MET_DIRECT_3
+    imply MET_DIRECT_4
+
+config UNMET_DIRECT_1
+    tristate
+    depends on n
+
+if n
+config UNMET_DIRECT_2
+    tristate
+endif
+
+menu "menu"
+    depends on n
+
+config UNMET_DIRECT_3
+    tristate
+
+endmenu
+
+config MET_DIRECT_1
+    tristate
+
+config MET_DIRECT_2
+    depends on y
+    tristate
+
+if y
+config MET_DIRECT_3
+    tristate
+endif
+
+menu "menu"
+    depends on y
+
+config MET_DIRECT_4
+    tristate
+
+endmenu
+
+#
+# 'imply' with condition
+#
+
+config IMPLY_COND
+    def_tristate y
+    tristate
+    imply IMPLIED_N_COND if n
+    imply IMPLIED_M_COND if m
+    imply IMPLIED_Y_COND if y
+
+config IMPLIED_N_COND
+    tristate
+
+config IMPLIED_M_COND
+    tristate
+
+config IMPLIED_Y_COND
+    tristate
+
+#
+# Implying from symbol with value n
+#
+
+# Will default to 'n'
+config IMPLY_N_1
+    tristate
+    imply IMPLIED_FROM_N_1
+
+# This test also disables the imply, so it's kinda redundant, but why not
+if n
+config IMPLY_N_2
+    tristate
+    imply IMPLIED_FROM_N_2
+endif
+
+config IMPLIED_FROM_N_1
+    tristate
+
+config IMPLIED_FROM_N_2
+    tristate
+
+#
+# Implying from symbol with value m
+#
+
+config IMPLY_M
+    def_tristate m
+    imply IMPLIED_M
+    # Implying a bool to 'm' makes it default to 'y'
+    imply IMPLIED_M_BOOL
+
+config IMPLIED_M
+    tristate
+
+config IMPLIED_M_BOOL
+    bool
+
+#
+# 'imply' which should raise an 'm' default to 'y'
+#
+
+config IMPLY_M_TO_Y
+    tristate
+    default y
+    imply IMPLIED_M_TO_Y
+
+config IMPLIED_M_TO_Y
+    tristate
+    default m
+
+#
+# Used for testing user values
+#
+
+config DIRECT_DEP
+    tristate "direct dep"
+
+config IMPLY
+    tristate "imply"
+    imply IMPLIED_TRISTATE
+    imply IMPLIED_BOOL
+
+config IMPLIED_TRISTATE
+    tristate "implied tristate"
+    depends on DIRECT_DEP
+
+config IMPLIED_BOOL
+    bool "implied bool"
+    depends on DIRECT_DEP
diff --git a/scripts/Kconfiglib/tests/Kinclude_path b/scripts/Kconfiglib/tests/Kinclude_path
new file mode 100644
index 0000000..7a3badb
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kinclude_path
@@ -0,0 +1,12 @@
+config TOP
+	bool
+
+source "Kinclude_path_sourced_1"
+
+config TOP
+	bool
+
+source "Kinclude_path_sourced_1"
+
+config TOP
+	bool
diff --git a/scripts/Kconfiglib/tests/Kinclude_path_sourced_1 b/scripts/Kconfiglib/tests/Kinclude_path_sourced_1
new file mode 100644
index 0000000..f4dee98
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kinclude_path_sourced_1
@@ -0,0 +1,12 @@
+config ONE_DOWN
+	bool
+
+source "Kinclude_path_sourced_2"
+
+config ONE_DOWN
+	bool
+
+source "Kinclude_path_sourced_2"
+
+config ONE_DOWN
+	bool
diff --git a/scripts/Kconfiglib/tests/Kinclude_path_sourced_2 b/scripts/Kconfiglib/tests/Kinclude_path_sourced_2
new file mode 100644
index 0000000..068f18d
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kinclude_path_sourced_2
@@ -0,0 +1,11 @@
+config TWO_DOWN
+	bool
+
+menu "menu"
+endmenu
+
+comment "comment"
+
+choice
+	bool "choice"
+endchoice
diff --git a/scripts/Kconfiglib/tests/Kitemlists b/scripts/Kconfiglib/tests/Kitemlists
new file mode 100644
index 0000000..8aa7107
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kitemlists
@@ -0,0 +1,36 @@
+comment "comment 1"
+
+choice
+    bool "choice 1"
+endchoice
+
+menu "menu 1"
+
+choice NAMED
+    bool "choice 2"
+endchoice
+
+menu "menu 2"
+menu "menu 3"
+comment "comment 2"
+endmenu
+
+choice
+    bool "choice 3"
+endchoice
+
+choice NAMED
+    bool
+endchoice
+
+endmenu
+
+menu "menu 4"
+endmenu
+
+comment "comment 3"
+
+endmenu
+
+menu "menu 5"
+endmenu
diff --git a/scripts/Kconfiglib/tests/Klocation b/scripts/Kconfiglib/tests/Klocation
new file mode 100644
index 0000000..3820a7b
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Klocation
@@ -0,0 +1,78 @@
+if UNDEFINED
+endif
+
+config ONE_DEF
+    bool
+
+config TWO_DEF
+    bool
+
+config TWO_DEF
+    bool
+
+config MANY_DEF
+    bool
+
+# Throw in some line continuations too to make sure it doesn't mess up the line
+# numbers
+if y && \
+   y
+if y && \
+   y && \
+   y
+
+# Throw in some help texts too
+
+config HELP_1
+    bool "help 1"
+    help
+config HELP_2
+    bool "help 2"
+    help
+      foo
+      bar
+
+        baz
+
+config HELP_3
+    help
+      foo
+      bar
+    bool
+config \
+MANY_DEF
+
+config MANY_DEF
+
+endif
+endif
+
+# Expands to "tests/Klocation_sourced"
+source "$TESTS_DIR_FROM_ENV/Klocation$_SOURCED"
+
+# Expands to "sub/Klocation_rsourced"
+rsource "$SUB_DIR_FROM_ENV/Klocation$_RSOURCED"
+
+# Expands to "tests/*ub/Klocation_gsourced[12]", matching
+# tests/sub/Klocation_gsourced{1,2}
+source "$TESTS_DIR_FROM_ENV/*ub/Klocation$_GSOURCED[12]"
+# Test old syntax too
+gsource "$TESTS_DIR_FROM_ENV/*ub/Klocation$_GSOURCED[12]"
+
+# Expands to "sub/Klocation_grsourced[12]", matching
+# tests/sub/Klocation_grsourced{1,2}
+rsource "$SUB_DIR_FROM_ENV/Klocation$_GRSOURCED[12]"
+# Test old syntax too
+grsource "$SUB_DIR_FROM_ENV/Klocation$_GRSOURCED[12]"
+
+# No-ops
+osource "nonexistent"
+osource "nonexistent*"
+gsource "nonexistent"
+gsource "nonexistent*"
+orsource "nonexistent"
+orsource "nonexistent*"
+grsource "nonexistent"
+grsource "nonexistent*"
+
+config MANY_DEF
diff --git a/scripts/Kconfiglib/tests/Klocation_sourced b/scripts/Kconfiglib/tests/Klocation_sourced
new file mode 100644
index 0000000..2ad8481
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Klocation_sourced
@@ -0,0 +1,26 @@
+
+
+config MANY_DEF
+
+choice CHOICE_ONE_DEF
+	bool "one-def choice"
+endchoice
+
+choice CHOICE_TWO_DEF
+	bool "two-def choice 1"
+endchoice
+
+choice CHOICE_TWO_DEF
+	bool "two-def choice 2"
+endchoice
+
+config MENU_HOOK
+	bool
+
+menu "menu"
+endmenu
+
+config COMMENT_HOOK
+	bool
+
+comment "comment"
diff --git a/scripts/Kconfiglib/tests/Kmainmenu b/scripts/Kconfiglib/tests/Kmainmenu
new file mode 100644
index 0000000..80713c0
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kmainmenu
@@ -0,0 +1,5 @@
+config FOO
+    string
+    option env="FOO"
+
+mainmenu "---$FOO---"
diff --git a/scripts/Kconfiglib/tests/Kmenuconfig b/scripts/Kconfiglib/tests/Kmenuconfig
new file mode 100644
index 0000000..9a4cc11
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kmenuconfig
@@ -0,0 +1,37 @@
+# Menu nodes with is_menuconfig False
+
+config NOT_MENUCONFIG_1
+	bool
+
+config NOT_MENUCONFIG_2
+	bool "not menuconfig 2"
+
+config MENUCONFIG_MULTI_DEF
+	bool "menuconfig multi def 1"
+
+config COMMENT_HOOK
+	bool
+
+comment "not menuconfig 3"
+
+
+# Menu nodes with is_menuconfig True
+
+menuconfig MENUCONFIG_1
+	bool "menuconfig 1"
+
+menuconfig MENUCONFIG_MULTI_DEF
+	bool "menuconfig multi def 2"
+
+config MENU_HOOK
+	bool
+
+menu "menuconfig 2"
+endmenu
+
+config CHOICE_HOOK
+	bool
+
+choice
+	bool "menuconfig 3"
+endchoice
diff --git a/scripts/Kconfiglib/tests/Kmisc b/scripts/Kconfiglib/tests/Kmisc
new file mode 100644
index 0000000..d9a799d
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kmisc
@@ -0,0 +1,82 @@
+# For testing various minor APIs
+
+# optional choices
+
+choice NOT_OPTIONAL
+    bool "not optional"
+config A
+    bool "A"
+config B
+    bool "B"
+endchoice
+
+choice OPTIONAL
+    tristate "optional"
+    optional
+config C
+    tristate "C"
+config D
+    tristate "D"
+# Quirky symbols - not proper choice symbol
+
+config Q1
+    tristate "Q1"
+    depends on D
+
+config Q2
+    tristate "Q2"
+    depends on Q1
+
+config Q3
+    tristate "Q3"
+    depends on D
+
+endchoice
+
+# User values
+
+config BOOL
+    bool "bool" if NOT_DEFINED_1
+
+config TRISTATE
+    tristate # Visibility should not affect user value
+
+config STRING
+    string "string"
+
+config INT
+    int # Visibility should not affect user value
+
+config HEX
+    hex "hex"
+    depends on NOT_DEFINED_2
+
+config COMMENT_HOOK
+comment "comment"
+
+config MENU_HOOK
+menu "menu"
+    depends on NOT_DEFINED_3 || NOT_DEFINED_2
+    depends on !NOT_DEFINED_4
+endmenu
+
+config FROM_ENV
+    string "from env"
+    option env="ENV_VAR"
+
+config FROM_ENV_MISSING
+    string "from env missing"
+    option env="MISSING_ENV_VAR"
+    default "missing"
+
+config FROM_ENV_WEIRD
+    string
+    default "weird"
+    option env="ENV_VAR"
+
+config NOT_ALLNOCONFIG_Y
+    bool "not allnoconfig_y"
+
+config ALLNOCONFIG_Y
+    bool "allnoconfig_y"
+    option allnoconfig_y
diff --git a/scripts/Kconfiglib/tests/Kmissingrsource b/scripts/Kconfiglib/tests/Kmissingrsource
new file mode 100644
index 0000000..924b0b3
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kmissingrsource
@@ -0,0 +1 @@
+rsource "nonexistent"
diff --git a/scripts/Kconfiglib/tests/Kmissingsource b/scripts/Kconfiglib/tests/Kmissingsource
new file mode 100644
index 0000000..a3a6c25
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kmissingsource
@@ -0,0 +1 @@
+source "nonexistent"
diff --git a/scripts/Kconfiglib/tests/Korder b/scripts/Kconfiglib/tests/Korder
new file mode 100644
index 0000000..3a8dffa
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Korder
@@ -0,0 +1,35 @@
+config O
+	int "O"
+	default 0
+
+config R
+	int "R"
+	default 1
+
+config D
+	int "D"
+	default 2
+
+config E
+	int "E"
+	default 3
+
+# Defined twice
+config R
+	int "R"
+
+config R2
+	int "R2"
+	default 4
+
+config I
+	int "I"
+	default 5
+
+config N
+	int "N"
+	default 6
+
+config G
+	int "G"
+	default 7
diff --git a/scripts/Kconfiglib/tests/Kpreprocess b/scripts/Kconfiglib/tests/Kpreprocess
new file mode 100644
index 0000000..283a988
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kpreprocess
@@ -0,0 +1,151 @@
+# Simple assignments (with bad formatting, as an additional test)
+
+simple-recursive=foo
+simple-immediate:=bar
+# Should become recursive
+simple-recursive-2+=baz
+
+	 whitespaced	 =	 foo
+
+
+# Simple += test. += should preserve the flavor of the variable (simple vs.
+# recursive).
+
+preserve-recursive = foo
+preserve-recursive += bar
+
+preserve-immediate := foo
+preserve-immediate += bar
+
+
+# Recursive substitution
+
+recursive = $(foo) $(bar) $($(b-char)a$(z-char))
+recursive += $(indir)
+
+foo = abc
+bar = def
+baz = ghi
+
+b-char = b
+z-char = z
+
+indir = jkl $(indir-2)
+indir-2 = mno
+
+
+# Immediate substitution
+
+def = foo
+immediate := $(undef)$(def)$(undef)$(def)
+def = bar
+undef = bar
+
+
+# Function calls
+
+# Chained function call
+quote = "$(1)" "$(2)"
+rev-quote = $(quote,$(2),$(1))
+surround-rev-quote = $(0) $(rev-quote,$(1),$(2)) $(0)
+surround-rev-quote-unused-arg = $(surround-rev-quote,$(1),$(2)) $(3)
+# No value is passed for $(3), so it expands to nothing
+fn-indir = surround-rev-quote
+messy-fn-res = $($(fn-indir)-unused-arg, a  b (,) , c  d )
+
+# Special characters in function call
+comma = ,
+right-paren = )
+dollar = $
+left-paren = (
+fn = "$(1)"
+special-chars-fn-res = $(fn,$(comma)$(dollar)$(left-paren)foo$(right-paren))
+
+
+# Variable expansions in various locations (verified by checking how the symbol
+# prints)
+
+qaz = QAZ
+echo = $(1)
+ignore-first = $(2)
+
+config PRINT_ME
+	string "$(ENV_1)" if ($(echo,FOO) && $(echo,BAR)) || !$(echo,BAZ) || !(($(qaz)))
+	default "$(echo,"foo")" if "foo $(echo,"bar") baz" = "$(undefined)"
+
+# Expansion within a symbol token, with deliberate sloppiness
+config PRINT_$(ignore-first,  ,ME)_TOO
+	bool "foo"
+	default FOO$(ignore-first,    ,BAR)BAZ$(qaz) if $(qaz)&&$(qaz)FOO&&x$(ignore-first,  ,xx)
+
+
+# Recursive expansion (throws an exception)
+
+rec-1 = x $(rec-2) y
+rec-2 = x $(rec-3) y
+rec-3 = x $(rec-1) y
+
+# Functions are allowed to reference themselves, but an exception is thrown if
+# the function seems to be stuck (the recursion gets too deep)
+safe-fn-rec = $($(1))
+safe-fn-rec-2 = $(safe-fn-rec,safe-fn-rec-3)
+safe-fn-rec-3 = foo
+safe-fn-rec-res = $(safe-fn-rec,safe-fn-rec-2)
+
+unsafe-fn-rec = $(unsafe-fn-rec,$(1))
+
+
+# Expansion in the left-hand side of assignments
+
+dummy-arg-fn = bar
+lhs-indir-1 = lhs-indir-2
+lhs-indir-2 = -baz
+rhs = value
+# LHS expands to foo-bar-baz
+foo-$(dummy-arg-fn, ignored argument )$($(lhs-indir-1)) = $(rhs)
+# Expands to empty string, accepted
+  $(undefined)  
+
+# Variable with a space in its name
+empty =
+space = $(empty) $(empty)
+foo$(space)bar = value
+space-var-res = $(foo bar)
+
+
+# Built-in functions
+
+# Expands to "baz qaz"
+shell-res = $(shell,false && echo foo bar || echo baz qaz)
+
+# Warns about output on stderr, expands to nothing
+shell-stderr-res := $(shell,echo message on stderr >&2)
+
+# Nested parens in macro call. Should give a single argument. Test it with
+# $(shell) to get a free argument number check.
+parens-res = pre-$(shell,echo '(a,$(b-char),(c,d),e)')-post
+
+# Expands to the current location
+location-res := $(filename):$(lineno)
+
+# Adds one warning, expands to nothing
+$(warning-if,,no warning)
+$(warning-if,n,no warning)
+warning-res := $(warning-if,y,a warning)
+
+# Does not cause an error, expands to nothing
+error-n-res := $(error-if,n,oops)
+
+# Causes an error when expanded
+error-y-res = $(error-if,y,oops)
+
+
+# Environment variables (for testing Kconfig.env_vars). ENV_1 is already
+# referenced above.
+env_ref_1 := xxx $(ENV_2) xxx
+env_ref_2 := $(shell,echo $(ENV_3))
+env_ref_3 :=
+env_ref_3 += $(ENV_4)
+$(warning-if,$(ENV_5),$(ENV_UNDEFINED))
+source "$(ENV_6)"
+env_ref_4 = $(ENV_7)  # Never evaluated
diff --git a/scripts/Kconfiglib/tests/Krange b/scripts/Kconfiglib/tests/Krange
new file mode 100644
index 0000000..3057483
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Krange
@@ -0,0 +1,133 @@
+#
+# No ranges
+#
+
+config HEX_NO_RANGE
+    hex "hex no range"
+
+config INT_NO_RANGE
+    int "int no range"
+
+#
+# Disabled ranges
+#
+
+config HEX_ALL_RANGES_DISABLED
+    hex "hex all ranges disabled"
+    range 0x10 0x20 if n
+    range 0x30 0x40 if n
+
+config INT_ALL_RANGES_DISABLED
+    int "int all ranges disabled"
+    range 10 20 if n
+    range 30 40 if n
+
+#
+# Ranges with defaults
+#
+
+# hex
+
+config HEX_RANGE_10_20_LOW_DEFAULT
+    hex "int range 10-20 low default"
+    range 0x10 0x20
+    default 0x9
+
+config HEX_RANGE_10_20_HIGH_DEFAULT
+    hex "int range 10-20 high default"
+    range 0x10 0x20
+    default 0x21
+
+config HEX_RANGE_10_20_OK_DEFAULT
+    hex "int range 10-20 ok default"
+    range 0x10 0x20
+    default 0x15
+
+config HEX_RANGE_10_20_OK_DEFAULT_ALTERNATE
+    hex "int range 10-20 ok default alternate"
+    range 0x10 0x20
+    default 15
+
+# int
+
+config INT_RANGE_10_20_LOW_DEFAULT
+    int "int range 10-20 low default"
+    range 10 20
+    default 9
+
+config INT_RANGE_10_20_HIGH_DEFAULT
+    int "int range 10-20 high default"
+    range 10 20
+    default 21
+
+config INT_RANGE_10_20_OK_DEFAULT
+    int "int range 10-20 ok default"
+    range 10 20
+    default 15
+
+#
+# Ranges with no defaults (should default to low end of range if > 0)
+#
+
+config HEX_RANGE_10_20
+    hex "hex range 10-20"    
+    range 0x10 0x20
+
+config HEX_RANGE_0_10
+    hex "hex range 0-10"
+    range 0x0 0x10
+
+config INT_RANGE_10_20
+    int "int range 10-20"    
+    range 10 20
+
+config INT_RANGE_0_10
+    int "int range 0-10"
+    range 0 10
+
+config INT_RANGE_NEG_10_10
+    int "int range -10-10"
+    range -10 10
+
+#
+# Dependent ranges
+#
+
+config HEX_40
+    hex
+    default 40
+
+config HEX_RANGE_10_40_DEPENDENT
+    hex "hex range 10-40 dependent"
+    range HEX_RANGE_10_20 HEX_40
+
+config INT_40
+    int
+    default 40
+
+config INT_RANGE_10_40_DEPENDENT
+    int "int range 10-40 dependent"
+    range INT_RANGE_10_20 INT_40
+
+#
+# Ranges on symbols defined in multiple locations
+#
+
+if n
+config INACTIVE_RANGE
+    range 0 1
+endif
+
+config INACTIVE_RANGE
+    int
+    # Default will apply and should not get clamped,
+    # because the range does not apply
+    default 2
+
+config ACTIVE_RANGE
+    range 0 1
+
+config ACTIVE_RANGE
+    int
+    # Default will apply and should be clamped to 1
+    default 2
diff --git a/scripts/Kconfiglib/tests/Krecursive1 b/scripts/Kconfiglib/tests/Krecursive1
new file mode 100644
index 0000000..35b521a
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Krecursive1
@@ -0,0 +1 @@
+source "tests/Krecursive2"
diff --git a/scripts/Kconfiglib/tests/Krecursive2 b/scripts/Kconfiglib/tests/Krecursive2
new file mode 100644
index 0000000..9f9f00e
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Krecursive2
@@ -0,0 +1 @@
+source "tests/Krecursive1"
diff --git a/scripts/Kconfiglib/tests/Kreferenced b/scripts/Kconfiglib/tests/Kreferenced
new file mode 100644
index 0000000..9da94c8
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kreferenced
@@ -0,0 +1,63 @@
+config NO_REFS
+    bool
+
+config JUST_DEPENDS_ON_REFS
+    bool
+    depends on A && B
+
+if A
+
+menu "menu"
+    depends on B
+    visible if C
+    visible if D
+
+config LOTS_OF_REFS
+    bool "lots" if C || D
+    default E || F if G || H
+    default I || J if K || L
+    select M if N || O
+    select P if Q || R
+    imply S if T || U
+    imply V if W || X
+    depends on Y || Z
+
+endmenu
+
+endif
+
+config INT_REFS
+    int "int"
+    range A B if C && D
+    range E F if G && H
+
+choice CHOICE
+    bool "choice"
+
+config CHOICE_REF
+    bool "choice ref"
+
+endchoice
+
+comment "comment"
+    depends on A || B
+
+
+config MULTI_DEF_SYM
+    def_bool A && B
+
+config MULTI_DEF_SYM
+    depends on C
+
+
+choice MULTI_DEF_CHOICE
+    bool "choice"
+    depends on A && B
+
+endchoice
+
+choice MULTI_DEF_CHOICE
+    bool "choice"
+    depends on C
+
+endchoice
diff --git a/scripts/Kconfiglib/tests/Krelation b/scripts/Kconfiglib/tests/Krelation
new file mode 100644
index 0000000..940eff8
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Krelation
@@ -0,0 +1,36 @@
+config A
+    bool
+    depends on UNDEFINED
+
+choice CHOICE_1
+    bool "C"
+config B
+    bool "B"
+config C
+    bool "C" if B
+config D
+    bool "D"
+endchoice
+
+menu "m1"
+config E
+    bool
+menu "m2"
+config F
+    bool
+choice CHOICE_2
+    tristate "foo"
+config G
+    bool "g"
+config H
+    bool "h"
+endchoice
+endmenu
+config I
+    bool
+endmenu
+
+menu "m3"
+endmenu
+menu "m4"
+endmenu
diff --git a/scripts/Kconfiglib/tests/Krepr b/scripts/Kconfiglib/tests/Krepr
new file mode 100644
index 0000000..fe6d8f0
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Krepr
@@ -0,0 +1,64 @@
+config MODULES
+    bool
+    option modules
+    default y
+
+if UNDEFINED
+endif
+
+config BASIC
+    bool
+    default y
+    ---help---
+
+config VISIBLE
+    bool "visible"
+
+config STRING
+    string "visible"
+
+config DIR_DEP_N
+    depends on n
+
+config OPTIONS
+    option allnoconfig_y
+    option defconfig_list
+    option env="ENV"
+
+config MULTI_DEF
+config MULTI_DEF
+
+menuconfig MENUCONFIG
+
+choice CHOICE
+    tristate "choice"
+
+config CHOICE_1
+    tristate "choice sym"
+
+config CHOICE_2
+    tristate "choice sym"
+
+endchoice
+
+config CHOICE_HOOK
+
+choice
+    tristate "optional choice" if n
+    optional
+endchoice
+
+config NO_VISIBLE_IF_HOOK
+
+menu "no visible if"
+endmenu
+
+config VISIBLE_IF_HOOK
+
+menu "visible if"
+    visible if m
+endmenu
+
+config COMMENT_HOOK
+
+comment "comment"
diff --git a/scripts/Kconfiglib/tests/Kstr b/scripts/Kconfiglib/tests/Kstr
new file mode 100644
index 0000000..f55c830
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kstr
@@ -0,0 +1,293 @@
+if UNDEFINED
+endif
+
+config NO_TYPE
+
+config BASIC_NO_PROMPT
+    bool
+    help
+      blah blah
+
+        blah blah blah
+
+       blah
+
+config BASIC_PROMPT
+    bool "basic"
+
+config ADVANCED
+    tristate "prompt" if DEP
+    default DEFAULT_1
+    default DEFAULT_2 if DEP
+    select SELECTED_1
+    select SELECTED_2 if DEP
+    imply IMPLIED_1
+    imply IMPLIED_2 if DEP
+    help
+      first help text
+
+config ADVANCED
+    prompt "prompt 2"
+
+menuconfig ADVANCED
+    prompt "prompt 3"
+
+if DEP3
+
+menu "foo"
+    depends on DEP4
+    visible if VIS
+
+config ADVANCED
+    help
+      second help text
+    depends on A || !B || (C && D) || !(E && F) || G = H || \
+              (I && !J && (K || L) && !(M || N) && O = P)
+
+config ADVANCED
+    # Used to verify that the direct dependencies appear to the right of VIS
+    prompt "prompt 4"
+
+endmenu
+
+endif
+
+config ONLY_DIRECT_DEPS
+    int
+    depends on DEP1
+    depends on DEP2
+
+config STRING
+    def_string "foo"
+    default "bar" if DEP
+    default STRING2
+    default STRING3 if DEP
+
+config INT
+    def_int 7 if DEP
+    range 1 2
+    range FOO BAR
+    range BAZ QAZ if DEP
+
+config HEX
+    def_hex 0x123
+    range 0x100 0x200
+    range FOO BAR
+    range BAZ QAZ if DEP
+
+config MODULES
+    bool "MODULES"
+    option modules
+
+config OPTIONS
+    option allnoconfig_y
+    option defconfig_list
+    option env="ENV"
+
+if LOC_1
+config CORRECT_PROP_LOCS_BOOL
+    prompt "prompt 1"
+    default DEFAULT_1
+    default DEFAULT_2
+    select SELECT_1
+    select SELECT_2
+    imply IMPLY_1
+    imply IMPLY_2
+    help
+      help 1
+endif
+
+if LOC_2
+menuconfig CORRECT_PROP_LOCS_BOOL
+    bool "prompt 2"
+    default DEFAULT_3
+    default DEFAULT_4
+    select SELECT_3
+    select SELECT_4
+    imply IMPLY_3
+    imply IMPLY_4
+    help
+      help 2
+endif
+
+if LOC_3
+config CORRECT_PROP_LOCS_BOOL
+    prompt "prompt 3"
+    default DEFAULT_5
+    default DEFAULT_6
+    select SELECT_5
+    select SELECT_6
+    imply IMPLY_5
+    imply IMPLY_6
+    help
+      help 2
+endif
+
+if LOC_1
+config CORRECT_PROP_LOCS_INT
+    int
+    range 1 2
+    range 3 4
+endif
+
+if LOC_2
+config CORRECT_PROP_LOCS_INT
+    range 5 6
+    range 7 8
+endif
+
+choice CHOICE
+    tristate "foo"
+    default CHOICE_1
+    default CHOICE_2 if dep
+
+config CHOICE_1
+    tristate "choice 1"
+
+config CHOICE_2
+    tristate "choice 2"
+
+endchoice
+
+choice
+    tristate "no name"
+    optional
+endchoice
+
+if LOC_1
+choice CORRECT_PROP_LOCS_CHOICE
+    bool
+    default CHOICE_3
+
+config CHOICE_3
+    bool "choice 3"
+
+config CHOICE_4
+    bool "choice 3"
+
+config CHOICE_5
+    bool "choice 3"
+
+endchoice
+endif
+
+if LOC_2
+choice CORRECT_PROP_LOCS_CHOICE
+    default CHOICE_4
+endchoice
+endif
+
+if LOC_3
+choice CORRECT_PROP_LOCS_CHOICE
+    default CHOICE_5
+endchoice
+endif
+
+config SIMPLE_MENU_HOOK
+menu "simple menu"
+endmenu
+
+config ADVANCED_MENU_HOOK
+menu "advanced menu"
+    depends on A
+    visible if B
+    visible if C || D
+endmenu
+
+config SIMPLE_COMMENT_HOOK
+comment "simple comment"
+
+config ADVANCED_COMMENT_HOOK
+comment "advanced comment"
+    depends on A
+    depends on B
+
+# Corner cases when removing direct dependencies
+
+config DEP_REM_CORNER_CASES
+    bool
+    default A
+    depends on n
+
+config DEP_REM_CORNER_CASES
+    default B if n
+
+config DEP_REM_CORNER_CASES
+    default C
+    depends on m
+
+config DEP_REM_CORNER_CASES
+    default D if A && y
+    depends on y
+
+config DEP_REM_CORNER_CASES
+    default E if !E1
+    default F if F1 = F2
+    default G if G1 || H1
+    depends on !H
+
+config DEP_REM_CORNER_CASES
+    default H
+    depends on "foo" = "bar"
+
+menu "menu"
+    visible if FOO || BAR
+
+config DEP_REM_CORNER_CASES
+    prompt "prompt"
+    depends on BAZ && QAZ
+
+endmenu
+
+# Only prompt, no type
+config PROMPT_ONLY
+    prompt "prompt only"
+
+# {Symbol,Choice}.orig_*
+
+if BASE_DEP
+
+config BOOL_SYM_ORIG
+    bool
+    default D1 if DEP
+    default D2
+    select S1
+    select S2 if DEP
+    imply I1
+    imply I1
+
+config BOOL_SYM_ORIG
+    default D3
+    select S3
+    imply I3 if DEP
+
+config INT_SYM_ORIG
+    int
+    range 1 2 if DEP
+    range 3 4
+
+config INT_SYM_ORIG
+    range 5 6 if DEP
+
+choice CHOICE_ORIG
+    bool "choice orig"
+    default A
+    default B if DEP
+
+config A
+    bool
+
+config B
+    bool
+
+endchoice
+
+choice CHOICE_ORIG
+    default C if DEP
+
+config C
+    bool
+
+endchoice
+
+endif
diff --git a/scripts/Kconfiglib/tests/Kundef b/scripts/Kconfiglib/tests/Kundef
new file mode 100644
index 0000000..fae521a
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kundef
@@ -0,0 +1,23 @@
+config DEF
+	bool
+
+config BOOL
+	bool "foo" if DEF || !UNDEF_1
+	default UNDEF_2
+
+config INT
+	int
+	range UNDEF_2 8
+	default 10
+	range 5 15
+
+config HEX
+	hex
+	range 0x123 0X456
+	default 0x200
+
+menu "menu"
+	depends on UNDEF_1
+	visible if UNDEF_3
+
+endmenu
diff --git a/scripts/Kconfiglib/tests/Kuserfunctions b/scripts/Kconfiglib/tests/Kuserfunctions
new file mode 100644
index 0000000..b0bf630
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kuserfunctions
@@ -0,0 +1,14 @@
+add-zero  = $(add)
+add-one   = $(add,1)
+add-three = $(add,1,-1,2,1)
+
+one-zero = $(one)
+one-one  = $(one,foo bar)
+one-two  = $(one,foo bar,baz)
+
+one-or-more-zero  = $(one-or-more)
+one-or-more-one   = $(one-or-more,foo)
+one-or-more-three = $(one-or-more,foo,bar,baz)
+
+location-1 := $(location)
+location-2 := $(location)
diff --git a/scripts/Kconfiglib/tests/Kvisibility b/scripts/Kconfiglib/tests/Kvisibility
new file mode 100644
index 0000000..91def0a
--- /dev/null
+++ b/scripts/Kconfiglib/tests/Kvisibility
@@ -0,0 +1,342 @@
+config MODULES
+    bool "MODULES"
+    option modules
+
+#
+# Symbol visibility
+#
+
+config NO_PROMPT
+    bool
+
+# Not rewritten, so MOD will have the value 'y' when running without modules
+config MOD
+    def_tristate m
+
+config BOOL_N
+    bool "bool n" if n
+
+config BOOL_M
+    # Rewritten to m && MODULES
+    bool "bool m" if m
+
+config BOOL_MOD
+    bool "bool MOD"
+    # Not rewritten
+    depends on MOD
+
+config BOOL_Y
+    bool "bool y"
+    # Rewritten to m && MODULES
+    depends on y || m
+
+config TRISTATE_N
+    tristate "tristate n" if n
+
+config TRISTATE_M
+    # Rewritten to m && MODULES
+    tristate "tristate m" if m
+
+config TRISTATE_MOD
+    tristate "tristate MOD"
+    # Not rewritten
+    depends on MOD
+
+config TRISTATE_Y
+    bool "tristate y"
+    # Rewritten to m && MODULES
+    depends on y || m
+
+# Symbols nested in 'if'
+
+if n
+
+config BOOL_IF_N
+    bool "bool if n"
+
+config TRISTATE_IF_N
+    tristate "tristate if n"
+
+endif
+
+if m
+
+config BOOL_IF_M
+    bool "bool if m"
+
+config TRISTATE_IF_M
+    tristate "tristate if n"
+
+endif
+
+if y
+
+config BOOL_IF_Y
+    bool "bool if y"
+
+config TRISTATE_IF_Y
+    tristate "tristate if y"
+
+endif
+
+# Symbols nested in 'menu'
+
+menu "menu 1"
+    depends on n
+
+config BOOL_MENU_N
+    bool "bool menu n"
+
+config TRISTATE_MENU_N
+    tristate "tristate menu n"
+
+endmenu
+
+menu "menu 2"
+    depends on m
+
+config BOOL_MENU_M
+    bool "bool menu m"
+
+config TRISTATE_MENU_M
+    tristate "tristate menu n"
+
+endmenu
+
+menu "menu 3"
+    depends on y
+
+config BOOL_MENU_Y
+    bool "bool menu y"
+
+config TRISTATE_MENU_Y
+    tristate "tristate menu y"
+
+endmenu
+
+# Symbols nested in choices
+
+choice C1
+    tristate "choice n" if n
+
+config BOOL_CHOICE_N
+    bool "bool choice n"
+
+config TRISTATE_CHOICE_N
+    tristate "tristate choice n"
+
+endchoice
+
+choice C2
+    tristate "choice m" if m
+
+config BOOL_CHOICE_M
+    bool "bool choice m"
+
+config TRISTATE_CHOICE_M
+    tristate "tristate choice n"
+
+endchoice
+
+choice C3
+    tristate "choice y" if y
+
+config BOOL_CHOICE_Y
+    bool "bool choice y"
+
+config TRISTATE_CHOICE_Y
+    tristate "tristate choice y"
+
+endchoice
+
+#
+# Choice visibility
+#
+
+choice BOOL_CHOICE_N
+    bool "bool choice n" if n
+endchoice
+
+choice BOOL_CHOICE_M
+    bool "bool choice m" if m
+endchoice
+
+choice BOOL_CHOICE_Y
+    bool "bool choice y" if y
+endchoice
+
+choice TRISTATE_CHOICE_N
+    tristate "tristate choice n" if n
+endchoice
+
+choice TRISTATE_CHOICE_M
+    tristate "tristate choice m" if m
+endchoice
+
+choice TRISTATE_CHOICE_Y
+    tristate "tristate choice y" if y
+
+config K
+    tristate "K"
+
+config L
+    tristate "L"
+
+endchoice
+
+if m
+choice TRISTATE_CHOICE_IF_M_AND_Y
+    tristate "tristate choice if m and y" if y
+
+config M
+    bool "M"
+
+config N
+    bool "N"
+
+endchoice
+endif
+
+menu "choice-containing menu"
+    depends on n && y
+
+choice TRISTATE_CHOICE_MENU_N_AND_Y
+    tristate "tristate choice if n and y"
+
+config O
+    tristate "O"
+
+config P
+    tristate "P"
+
+endchoice
+
+endmenu
+
+#
+# Menu visibility
+#
+
+menu "menu n"
+    depends on n
+endmenu
+
+menu "menu m"
+    depends on m
+endmenu
+
+menu "menu y"
+    depends on y
+endmenu
+
+if n
+menu "menu if n"
+endmenu
+endif
+
+if m
+menu "menu if m"
+endmenu
+endif
+
+if y
+menu "menu if y"
+endmenu
+endif
+
+if m
+menu "menu if m and y"
+    depends on y
+endmenu
+endif
+
+#
+# Comment visibility
+#
+
+comment "comment n"
+    depends on n
+comment "comment m"
+    depends on m
+comment "comment y"
+    depends on y
+
+if n
+comment "comment if n"
+endif
+
+if m
+comment "comment if m"
+endif
+
+if y
+comment "comment if y"
+endif
+
+if "y"
+
+menu "menu with comment"
+    depends on m
+
+comment "double-nested m comment"
+    depends on y
+
+endmenu
+
+endif
+
+# Used to verify that string/int/hex symbols with m visibility accept a user
+# value
+
+if m
+
+config STRING_m
+    string "string"
+
+config INT_m
+    int "int"
+
+config HEX_m
+    hex "hex"
+
+endif
+
+# Menu 'visible if' visibility
+
+menu "n-visible menu"
+    visible if n
+
+config VISIBLE_IF_N
+    tristate "visible if n"
+
+endmenu
+
+menu "m-visible menu"
+    visible if m
+
+config VISIBLE_IF_M
+    tristate "visible if m"
+
+endmenu
+
+menu "y-visible menu"
+    visible if y
+
+config VISIBLE_IF_Y
+    tristate "visible if m"
+
+endmenu
+
+menu "m-visible menu 2"
+    visible if y || n
+    visible if m && y
+    visible if y
+
+if y
+
+config VISIBLE_IF_M_2
+    tristate "visible if m 2"
+
+endif
+
+endmenu
diff --git a/scripts/Kconfiglib/tests/config_indented b/scripts/Kconfiglib/tests/config_indented
new file mode 100644
index 0000000..d57d8b4
--- /dev/null
+++ b/scripts/Kconfiglib/tests/config_indented
@@ -0,0 +1,3 @@
+# Indented assignments should be ignored to be compatible with the C
+# implementation
+ CONFIG_IGNOREME=n
diff --git a/scripts/Kconfiglib/tests/config_set_bool b/scripts/Kconfiglib/tests/config_set_bool
new file mode 100644
index 0000000..d2dccbf
--- /dev/null
+++ b/scripts/Kconfiglib/tests/config_set_bool
@@ -0,0 +1 @@
+CONFIG_BOOL=y
diff --git a/scripts/Kconfiglib/tests/config_set_string b/scripts/Kconfiglib/tests/config_set_string
new file mode 100644
index 0000000..3a1250a
--- /dev/null
+++ b/scripts/Kconfiglib/tests/config_set_string
@@ -0,0 +1 @@
+CONFIG_STRING="foo bar"
diff --git a/scripts/Kconfiglib/tests/defconfig_1 b/scripts/Kconfiglib/tests/defconfig_1
new file mode 100644
index 0000000..e69de29
diff --git a/scripts/Kconfiglib/tests/defconfig_2 b/scripts/Kconfiglib/tests/defconfig_2
new file mode 100644
index 0000000..e69de29
diff --git a/scripts/Kconfiglib/tests/empty b/scripts/Kconfiglib/tests/empty
new file mode 100644
index 0000000..e69de29
diff --git a/scripts/Kconfiglib/tests/kconfigfunctions.py b/scripts/Kconfiglib/tests/kconfigfunctions.py
new file mode 100644
index 0000000..8f35511
--- /dev/null
+++ b/scripts/Kconfiglib/tests/kconfigfunctions.py
@@ -0,0 +1,22 @@
+def add(kconf, name, *args):
+    return str(sum(map(int, args)))
+
+
+def one(kconf, name, s):
+    return name + 2*s
+
+
+def one_or_more(kconf, name, arg, *args):
+    return arg + " + " + ",".join(args)
+
+
+def location(kconf, name):
+    return "{}:{}".format(kconf.filename, kconf.linenr)
+
+
+functions = {
+    "add":         (add,         0, None),
+    "one":         (one,         1,    1),
+    "one-or-more": (one_or_more, 1, None),
+    "location":    (location,    0,    0),
+}
diff --git a/scripts/Kconfiglib/tests/reltest b/scripts/Kconfiglib/tests/reltest
new file mode 100755
index 0000000..3e09e5f
--- /dev/null
+++ b/scripts/Kconfiglib/tests/reltest
@@ -0,0 +1,51 @@
+#!/usr/bin/env bash
+
+# Runs the test suite and all examples scripts with Python 2 and Python 3,
+# bailing immediately if anything fails. For the examples that aren't tested in
+# the test suite, we just confirm that they at least run.
+#
+# Should be run from the kernel root with  $ Kconfiglib/tests/reltest
+
+test_script() {
+    echo -e "\n================= $1 with $py =================\n"
+    if (($# == 1)); then
+        make scriptconfig PYTHONCMD=$py SCRIPT=$1
+    else
+        make scriptconfig PYTHONCMD=$py SCRIPT=$1 SCRIPT_ARG="$2"
+    fi
+
+    if (($?)); then
+        echo "$1 failed to run with $py"
+        exit 1
+    fi
+}
+
+for py in python2 python3; do
+    echo -e "\n================= Test suite with $py =================\n"
+
+    if ! $py Kconfiglib/testsuite.py; then
+        echo "test suite failed for $py"
+        exit 1
+    fi
+
+    # Check that the example scripts that aren't tested by the test suite run
+    # at least
+
+    test_script Kconfiglib/examples/defconfig_oldconfig.py
+    test_script Kconfiglib/examples/eval_expr.py MODULES
+    test_script Kconfiglib/examples/find_symbol.py X86
+    test_script Kconfiglib/examples/help_grep.py general
+    test_script Kconfiglib/examples/print_sym_info.py MODULES
+    test_script Kconfiglib/examples/print_tree.py
+
+    $py Kconfiglib/examples/menuconfig_example.py Kconfiglib/examples/Kmenuconfig <<END
+BOOL
+n
+END
+    if (($?)); then
+        echo "menuconfig_example.py failed with $py"
+        exit 1
+    fi
+done
+
+echo "everything okay"
diff --git a/scripts/Kconfiglib/tests/sub/Kconfig_symlink_2 b/scripts/Kconfiglib/tests/sub/Kconfig_symlink_2
new file mode 100644
index 0000000..aeba985
--- /dev/null
+++ b/scripts/Kconfiglib/tests/sub/Kconfig_symlink_2
@@ -0,0 +1 @@
+rsource "Kconfig_symlink_3"
diff --git a/scripts/Kconfiglib/tests/sub/Kconfig_symlink_3 b/scripts/Kconfiglib/tests/sub/Kconfig_symlink_3
new file mode 100644
index 0000000..20b4e06
--- /dev/null
+++ b/scripts/Kconfiglib/tests/sub/Kconfig_symlink_3
@@ -0,0 +1,2 @@
+config FOUNDME
+    bool
diff --git a/scripts/Kconfiglib/tests/sub/Klocation_grsourced1 b/scripts/Kconfiglib/tests/sub/Klocation_grsourced1
new file mode 100644
index 0000000..1e04ad6
--- /dev/null
+++ b/scripts/Kconfiglib/tests/sub/Klocation_grsourced1
@@ -0,0 +1 @@
+config MANY_DEF
diff --git a/scripts/Kconfiglib/tests/sub/Klocation_grsourced2 b/scripts/Kconfiglib/tests/sub/Klocation_grsourced2
new file mode 100644
index 0000000..1e04ad6
--- /dev/null
+++ b/scripts/Kconfiglib/tests/sub/Klocation_grsourced2
@@ -0,0 +1 @@
+config MANY_DEF
diff --git a/scripts/Kconfiglib/tests/sub/Klocation_gsourced1 b/scripts/Kconfiglib/tests/sub/Klocation_gsourced1
new file mode 100644
index 0000000..1e04ad6
--- /dev/null
+++ b/scripts/Kconfiglib/tests/sub/Klocation_gsourced1
@@ -0,0 +1 @@
+config MANY_DEF
diff --git a/scripts/Kconfiglib/tests/sub/Klocation_gsourced2 b/scripts/Kconfiglib/tests/sub/Klocation_gsourced2
new file mode 100644
index 0000000..1e04ad6
--- /dev/null
+++ b/scripts/Kconfiglib/tests/sub/Klocation_gsourced2
@@ -0,0 +1 @@
+config MANY_DEF
diff --git a/scripts/Kconfiglib/tests/sub/Klocation_rsourced b/scripts/Kconfiglib/tests/sub/Klocation_rsourced
new file mode 100644
index 0000000..12fdbc2
--- /dev/null
+++ b/scripts/Kconfiglib/tests/sub/Klocation_rsourced
@@ -0,0 +1,2 @@
+
+config MANY_DEF
diff --git a/scripts/Kconfiglib/tests/sub/defconfig_in_sub b/scripts/Kconfiglib/tests/sub/defconfig_in_sub
new file mode 100644
index 0000000..e69de29
diff --git a/scripts/Kconfiglib/tests/sub/sub/Kconfig_symlink_1 b/scripts/Kconfiglib/tests/sub/sub/Kconfig_symlink_1
new file mode 100644
index 0000000..bceec8b
--- /dev/null
+++ b/scripts/Kconfiglib/tests/sub/sub/Kconfig_symlink_1
@@ -0,0 +1,2 @@
+# Sources tests/sub/Kconfig_symlink_2, with an absolute path
+source "$(KCONFIG_SYMLINK_2)"
diff --git a/scripts/Kconfiglib/tests/symlink b/scripts/Kconfiglib/tests/symlink
new file mode 120000
index 0000000..565623e
--- /dev/null
+++ b/scripts/Kconfiglib/tests/symlink
@@ -0,0 +1 @@
+sub/sub
\ No newline at end of file
diff --git a/scripts/Kconfiglib/testsuite.py b/scripts/Kconfiglib/testsuite.py
new file mode 100644
index 0000000..fc61a16
--- /dev/null
+++ b/scripts/Kconfiglib/testsuite.py
@@ -0,0 +1,3203 @@
+# Copyright (c) 2011-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+# This is the Kconfiglib test suite. It runs selftests on Kconfigs provided by
+# us and tests compatibility with the C Kconfig implementation by comparing the
+# output of Kconfiglib with the output of the scripts/kconfig/*conf utilities
+# for different targets and defconfigs. It should be run from the top-level
+# kernel directory with
+#
+#   $ python Kconfiglib/testsuite.py
+#
+# Some additional options can be turned on by passing them as arguments. They
+# default to off.
+#
+#  - obsessive:
+#    By default, only valid arch/defconfig pairs are tested. In obsessive mode,
+#    every arch will be tested with every defconfig. Increases the testing time
+#    by an order of magnitude. Occasionally finds (usually obscure) bugs, and I
+#    make sure everything passes with it.
+#
+#  - obsessive-min-config:
+#    Like obsessive, for the minimal configuation (defconfig) tests.
+#
+#  - log:
+#    Log timestamped defconfig test failures to the file test_defconfig_fails.
+#    Handy in obsessive mode.
+#
+# For example, this commands runs the test suite in obsessive mode with logging
+# enabled:
+#
+#   $ python(3) Kconfiglib/testsuite.py obsessive log
+#
+# pypy works too, and runs most tests much faster than CPython.
+#
+# All tests should pass. Report regressions to ulfalizer a.t Google's email
+# service.
+
+import difflib
+import errno
+import os
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+import textwrap
+
+from kconfiglib import Kconfig, Symbol, Choice, COMMENT, MENU, MenuNode, \
+                       BOOL, TRISTATE, HEX, \
+                       TRI_TO_STR, \
+                       escape, unescape, \
+                       expr_str, expr_items, split_expr, \
+                       _ordered_unique, \
+                       OR, AND, \
+                       KconfigError
+
+
+def shell(cmd):
+    with open(os.devnull, "w") as devnull:
+        subprocess.call(cmd, shell=True, stdout=devnull, stderr=devnull)
+
+
+all_passed = True
+
+
+def fail(msg=None):
+    global all_passed
+    all_passed = False
+    if msg is not None:
+        print("fail: " + msg)
+
+
+def verify(cond, msg):
+    if not cond:
+        fail(msg)
+
+
+def verify_equal(x, y):
+    if x != y:
+        fail("'{}' does not equal '{}'".format(x, y))
+
+
+# Prevent accidental loading of configuration files by removing
+# KCONFIG_ALLCONFIG from the environment
+os.environ.pop("KCONFIG_ALLCONFIG", None)
+
+obsessive = False
+obsessive_min_config = False
+log = False
+
+
+def run_tests():
+    global obsessive, log
+    for s in sys.argv[1:]:
+        if s == "obsessive":
+            obsessive = True
+            print("Obsessive mode enabled")
+        elif s == "obsessive-min-config":
+            obsessive_min_config = True
+            print("Obsessive minimal config mode enabled")
+        elif s == "log":
+            log = True
+            print("Log mode enabled")
+        else:
+            print("Unrecognized option '{}'".format(s))
+            return
+
+    run_selftests()
+    run_compatibility_tests()
+
+
+def run_selftests():
+    #
+    # Common helper functions. These all expect 'c' to hold the current
+    # configuration.
+    #
+
+    def verify_value(sym_name, val):
+        # Verifies that a symbol has a particular value.
+
+        if isinstance(val, int):
+            val = TRI_TO_STR[val]
+
+        sym = c.syms[sym_name]
+        verify(sym.str_value == val,
+               'expected {} to have the value "{}", had the value "{}"'
+               .format(sym_name, val, sym.str_value))
+
+    def assign_and_verify_value(sym_name, val, new_val):
+        # Assigns 'val' to a symbol and verifies that its value becomes
+        # 'new_val'. Assumes (and tests) that 'val' is valid for the
+        # symbol type.
+
+        if isinstance(new_val, int):
+            new_val = TRI_TO_STR[new_val]
+
+        sym = c.syms[sym_name]
+        old_val = sym.str_value
+        verify(sym.set_value(val),
+               "assigning '{}' to {} unexpectedly failed"
+               .format(val, sym_name))
+        verify(sym.str_value == new_val,
+               "expected {} to have the value '{}' after being assigned the "
+               "value '{}'. Instead, the value is '{}'. The old value was "
+               "'{}'."
+               .format(sym_name, new_val, val, sym.str_value, old_val))
+
+    def assign_and_verify(sym_name, user_val):
+        # Like assign_and_verify_value(), with the expected value being the
+        # value just set.
+
+        assign_and_verify_value(sym_name, user_val, user_val)
+
+    def assign_and_verify_user_value(sym_name, val, user_val, valid):
+        # Assigns a user value to the symbol and verifies the new user value.
+        # If valid is True, the user value is valid for the type, otherwise
+        # not. This is used to test the set_value() return value.
+
+        sym = c.syms[sym_name]
+        sym_old_user_val = sym.user_value
+
+        verify(sym.set_value(val) == valid,
+               "expected the user value '{}' to be {} for {}, was not"
+               .format(val, "valid" if valid else "invalid", sym_name))
+        verify(sym.user_value == user_val,
+               "the assigned user value '{}' wasn't reflected in user_value "
+               "on the symbol {}. Instead, the new user_value was '{}'. The "
+               "old user value was '{}'."
+               .format(user_val, sym_name, sym.user_value, sym_old_user_val))
+
+    #
+    # Selftests
+    #
+
+    print("Testing string literal lexing")
+
+    # Dummy empty configuration just to get a Kconfig object
+    c = Kconfig("Kconfiglib/tests/empty")
+
+    def verify_string_lex(s, expected):
+        # Verifies that a constant symbol with the name 'res' is produced from
+        # lexing 's'
+
+        res = c._tokenize("if " + s)[1].name
+        verify(res == expected,
+               "expected <{}> to produced the constant symbol <{}>, "
+               'produced <{}>'.format(s[1:-1], expected, res))
+
+    verify_string_lex(r""" "" """, "")
+    verify_string_lex(r""" '' """, "")
+
+    verify_string_lex(r""" "a" """, "a")
+    verify_string_lex(r""" 'a' """, "a")
+    verify_string_lex(r""" "ab" """, "ab")
+    verify_string_lex(r""" 'ab' """, "ab")
+    verify_string_lex(r""" "abc" """, "abc")
+    verify_string_lex(r""" 'abc' """, "abc")
+
+    verify_string_lex(r""" "'" """, "'")
+    verify_string_lex(r""" '"' """, '"')
+
+    verify_string_lex(r""" "\"" """, '"')
+    verify_string_lex(r""" '\'' """, "'")
+
+    verify_string_lex(r""" "\"\"" """, '""')
+    verify_string_lex(r""" '\'\'' """, "''")
+
+    verify_string_lex(r""" "\'" """, "'")
+    verify_string_lex(r""" '\"' """, '"')
+
+    verify_string_lex(r""" "\\" """, "\\")
+    verify_string_lex(r""" '\\' """, "\\")
+
+    verify_string_lex(r""" "\a\\'\b\c\"'d" """, 'a\\\'bc"\'d')
+    verify_string_lex(r""" '\a\\"\b\c\'"d' """, "a\\\"bc'\"d")
+
+    def verify_string_bad(s):
+        # Verifies that tokenizing 's' throws a KconfigError. Strips the first
+        # and last characters from 's' so we can use readable raw strings as
+        # input.
+
+        try:
+            c.eval_string(s)
+        except KconfigError:
+            pass
+        else:
+            fail("expected tokenization of {} to fail, didn't".format(s[1:-1]))
+
+    verify_string_bad(r""" " """)
+    verify_string_bad(r""" ' """)
+    verify_string_bad(r""" "' """)
+    verify_string_bad(r""" '" """)
+    verify_string_bad(r""" "\" """)
+    verify_string_bad(r""" '\' """)
+    verify_string_bad(r""" "foo """)
+    verify_string_bad(r""" 'foo """)
+
+
+    print("Testing escape() and unescape()")
+
+    def verify_escape_unescape(s, sesc):
+        # Verify that 's' escapes to 'sesc' and that 'sesc' unescapes to 's'
+        verify_equal(escape(s), sesc)
+        verify_equal(unescape(sesc), s)
+
+    verify_escape_unescape(r''          , r''              )
+    verify_escape_unescape(r'foo'       , r'foo'           )
+    verify_escape_unescape(r'"'         , r'\"'            )
+    verify_escape_unescape(r'""'        , r'\"\"'          )
+    verify_escape_unescape('\\'         , r'\\'            )
+    verify_escape_unescape(r'\\'        , r'\\\\'          )
+    verify_escape_unescape(r'\"'        , r'\\\"'          )
+    verify_escape_unescape(r'"ab\cd"ef"', r'\"ab\\cd\"ef\"')
+
+    # Backslashes before any character should be unescaped, not just before "
+    # and \
+    verify_equal(unescape(r"\afoo\b\c\\d\\\e\\\\f"), r"afoobc\d\e\\f")
+
+
+    print("Testing _ordered_unique()")
+
+    verify_equal(_ordered_unique([]), [])
+    verify_equal(_ordered_unique([1]), [1])
+    verify_equal(_ordered_unique([1, 2]), [1, 2])
+    verify_equal(_ordered_unique([1, 1]), [1])
+    verify_equal(_ordered_unique([1, 1, 2]), [1, 2])
+    verify_equal(_ordered_unique([1, 2, 1]), [1, 2])
+    verify_equal(_ordered_unique([1, 2, 2]), [1, 2])
+    verify_equal(_ordered_unique([1, 2, 3, 2, 1, 2, 3, 4, 3, 2, 1, 0]),
+                                 [1, 2, 3, 4, 0])
+
+
+    print("Testing expression evaluation")
+
+    c = Kconfig("Kconfiglib/tests/Keval", warn=False)
+
+    def verify_eval(expr, val):
+        res = c.eval_string(expr)
+        verify(res == val,
+               "'{}' evaluated to {}, expected {}".format(expr, res, val))
+
+    # No modules
+    verify_eval("n", 0)
+    verify_eval("m", 0)
+    verify_eval("y", 2)
+    verify_eval("'n'", 0)
+    verify_eval("'m'", 0)
+    verify_eval("'y'", 2)
+    verify_eval("M", 2)
+
+    # Modules
+    c.modules.set_value(2)
+    verify_eval("n", 0)
+    verify_eval("m", 1)
+    verify_eval("y", 2)
+    verify_eval("'n'", 0)
+    verify_eval("'m'", 1)
+    verify_eval("'y'", 2)
+    verify_eval("M", 1)
+    verify_eval("(Y || N) && (m && y)", 1)
+
+    # Non-bool/non-tristate symbols are always n in a tristate sense
+    verify_eval("Y_STRING", 0)
+    verify_eval("Y_STRING || m", 1)
+
+    # As are all constants besides y and m
+    verify_eval('"foo"', 0)
+    verify_eval('"foo" || "bar"', 0)
+    verify_eval('"foo" || m', 1)
+
+    # Test equality for symbols
+
+    verify_eval("N = N", 2)
+    verify_eval("N = n", 2)
+    verify_eval("N = 'n'", 2)
+    verify_eval("N != N", 0)
+    verify_eval("N != n", 0)
+    verify_eval("N != 'n'", 0)
+
+    verify_eval("M = M", 2)
+    verify_eval("M = m", 2)
+    verify_eval("M = 'm'", 2)
+    verify_eval("M != M", 0)
+    verify_eval("M != m", 0)
+    verify_eval("M != 'm'", 0)
+
+    verify_eval("Y = Y", 2)
+    verify_eval("Y = y", 2)
+    verify_eval("Y = 'y'", 2)
+    verify_eval("Y != Y", 0)
+    verify_eval("Y != y", 0)
+    verify_eval("Y != 'y'", 0)
+
+    verify_eval("N != M", 2)
+    verify_eval("N != Y", 2)
+    verify_eval("M != Y", 2)
+
+    verify_eval("Y_STRING = y", 2)
+    verify_eval("Y_STRING = 'y'", 2)
+    verify_eval('FOO_BAR_STRING = "foo bar"', 2)
+    verify_eval('FOO_BAR_STRING != "foo bar baz"', 2)
+    verify_eval('INT_37 = 37', 2)
+    verify_eval("INT_37 = '37'", 2)
+    verify_eval('HEX_0X37 = 0x37', 2)
+    verify_eval("HEX_0X37 = '0x37'", 2)
+
+    # These should also hold after 31847b67 (kconfig: allow use of relations
+    # other than (in)equality)
+    verify_eval("HEX_0X37 = '0x037'", 2)
+    verify_eval("HEX_0X37 = '0x0037'", 2)
+
+    # Constant symbol comparisons
+    verify_eval('"foo" != "bar"', 2)
+    verify_eval('"foo" = "bar"', 0)
+    verify_eval('"foo" = "foo"', 2)
+
+    # Undefined symbols get their name as their value
+    c.warn = False
+    verify_eval("'not_defined' = not_defined", 2)
+    verify_eval("not_defined_2 = not_defined_2", 2)
+    verify_eval("not_defined_1 != not_defined_2", 2)
+
+    # Test less than/greater than
+
+    # Basic evaluation
+    verify_eval("INT_37 < 38", 2)
+    verify_eval("38 < INT_37", 0)
+    verify_eval("INT_37 < '38'", 2)
+    verify_eval("'38' < INT_37", 0)
+    verify_eval("INT_37 < 138", 2)
+    verify_eval("138 < INT_37", 0)
+    verify_eval("INT_37 < '138'", 2)
+    verify_eval("'138' < INT_37", 0)
+    verify_eval("INT_37 < -138", 0)
+    verify_eval("-138 < INT_37", 2)
+    verify_eval("INT_37 < '-138'", 0)
+    verify_eval("'-138' < INT_37", 2)
+    verify_eval("INT_37 < 37", 0)
+    verify_eval("37 < INT_37", 0)
+    verify_eval("INT_37 < 36", 0)
+    verify_eval("36 < INT_37", 2)
+
+    # Different formats in comparison
+    verify_eval("INT_37 < 0x26", 2) # 38
+    verify_eval("INT_37 < 0x25", 0) # 37
+    verify_eval("INT_37 < 0x24", 0) # 36
+    verify_eval("HEX_0X37 < 56", 2) # 0x38
+    verify_eval("HEX_0X37 < 55", 0) # 0x37
+    verify_eval("HEX_0X37 < 54", 0) # 0x36
+
+    # Other int comparisons
+    verify_eval("INT_37 <= 38", 2)
+    verify_eval("INT_37 <= 37", 2)
+    verify_eval("INT_37 <= 36", 0)
+    verify_eval("INT_37 >  38", 0)
+    verify_eval("INT_37 >  37", 0)
+    verify_eval("INT_37 >  36", 2)
+    verify_eval("INT_37 >= 38", 0)
+    verify_eval("INT_37 >= 37", 2)
+    verify_eval("INT_37 >= 36", 2)
+
+    # Other hex comparisons
+    verify_eval("HEX_0X37 <= 0x38", 2)
+    verify_eval("HEX_0X37 <= 0x37", 2)
+    verify_eval("HEX_0X37 <= 0x36", 0)
+    verify_eval("HEX_0X37 >  0x38", 0)
+    verify_eval("HEX_0X37 >  0x37", 0)
+    verify_eval("HEX_0X37 >  0x36", 2)
+    verify_eval("HEX_0X37 >= 0x38", 0)
+    verify_eval("HEX_0X37 >= 0x37", 2)
+    verify_eval("HEX_0X37 >= 0x36", 2)
+
+    # A hex holding a value without a "0x" prefix should still be treated as
+    # hexadecimal
+    verify_eval("HEX_37 < 0x38", 2)
+    verify_eval("HEX_37 < 0x37", 0)
+    verify_eval("HEX_37 < 0x36", 0)
+
+    # Symbol comparisons
+    verify_eval("INT_37   <  HEX_0X37", 2)
+    verify_eval("INT_37   >  HEX_0X37", 0)
+    verify_eval("HEX_0X37 <  INT_37  ", 0)
+    verify_eval("HEX_0X37 >  INT_37  ", 2)
+    verify_eval("INT_37   <  INT_37  ", 0)
+    verify_eval("INT_37   <= INT_37  ", 2)
+    verify_eval("INT_37   >  INT_37  ", 0)
+    verify_eval("INT_37   <= INT_37  ", 2)
+
+    # Tristate value comparisons
+    verify_eval("n < n", 0)
+    verify_eval("n < m", 2)
+    verify_eval("n < y", 2)
+    verify_eval("n < N", 0)
+    verify_eval("n < M", 2)
+    verify_eval("n < Y", 2)
+    verify_eval("0 > n", 0)
+    verify_eval("1 > n", 2)
+    verify_eval("2 > n", 2)
+    verify_eval("m < n", 0)
+    verify_eval("m < m", 0)
+    verify_eval("m < y", 2)
+
+    # Strings compare lexicographically
+    verify_eval("'aa' < 'ab'", 2)
+    verify_eval("'aa' > 'ab'", 0)
+    verify_eval("'ab' < 'aa'", 0)
+    verify_eval("'ab' > 'aa'", 2)
+
+    # Comparisons where one of the operands doesn't parse as a number also give
+    # a lexicographic comparison
+    verify_eval("INT_37 <  '37a' ", 2)
+    verify_eval("'37a'  >  INT_37", 2)
+    verify_eval("INT_37 <= '37a' ", 2)
+    verify_eval("'37a'  >= INT_37", 2)
+    verify_eval("INT_37 >= '37a' ", 0)
+    verify_eval("INT_37 >  '37a' ", 0)
+    verify_eval("'37a'  <  INT_37", 0)
+    verify_eval("'37a'  <= INT_37", 0)
+
+    def verify_eval_bad(expr):
+        try:
+            c.eval_string(expr)
+        except KconfigError:
+            pass
+        else:
+            fail('expected eval_string("{}") to throw KconfigError, '
+                 "didn't".format(expr))
+
+    # Verify that some bad stuff throws KconfigError's
+    verify_eval_bad("")
+    verify_eval_bad("&")
+    verify_eval_bad("|")
+    verify_eval_bad("!")
+    verify_eval_bad("(")
+    verify_eval_bad(")")
+    verify_eval_bad("=")
+    verify_eval_bad("(X")
+    verify_eval_bad("X)")
+    verify_eval_bad("X X")
+    verify_eval_bad("!X X")
+    verify_eval_bad("X !X")
+    verify_eval_bad("(X) X")
+    verify_eval_bad("X &&")
+    verify_eval_bad("&& X")
+    verify_eval_bad("X && && X")
+    verify_eval_bad("X && !&&")
+    verify_eval_bad("X ||")
+    verify_eval_bad("|| X")
+
+
+    print("Testing Symbol.__str__()/custom_str() and def_{int,hex,string}")
+
+    def verify_str(item, s):
+        verify_equal(str(item), s[1:-1])
+
+    def verify_custom_str(item, s):
+        verify_equal(item.custom_str(lambda sc: "[{}]".format(sc.name)),
+                     s[1:-1])
+
+    c = Kconfig("Kconfiglib/tests/Kstr", warn=False)
+
+    c.modules.set_value(2)
+
+    verify_str(c.syms["UNDEFINED"], """
+""")
+
+    verify_str(c.syms["BASIC_NO_PROMPT"], """
+config BASIC_NO_PROMPT
+	bool
+	help
+	  blah blah
+	  
+	    blah blah blah
+	  
+	   blah
+""")
+
+    verify_str(c.syms["BASIC_PROMPT"], """
+config BASIC_PROMPT
+	bool "basic"
+""")
+
+    verify_str(c.syms["ADVANCED"], """
+config ADVANCED
+	tristate "prompt" if DEP
+	default DEFAULT_1
+	default DEFAULT_2 if DEP
+	select SELECTED_1
+	select SELECTED_2 if DEP
+	imply IMPLIED_1
+	imply IMPLIED_2 if DEP
+	help
+	  first help text
+
+config ADVANCED
+	tristate "prompt 2"
+
+menuconfig ADVANCED
+	tristate "prompt 3"
+
+config ADVANCED
+	tristate
+	depends on (A || !B || (C && D) || !(E && F) || G = H || (I && !J && (K || L) && !(M || N) && O = P)) && DEP4 && DEP3
+	help
+	  second help text
+
+config ADVANCED
+	tristate "prompt 4" if VIS
+	depends on DEP4 && DEP3
+""")
+
+    verify_custom_str(c.syms["ADVANCED"], """
+config ADVANCED
+	tristate "prompt" if [DEP]
+	default [DEFAULT_1]
+	default [DEFAULT_2] if [DEP]
+	select [SELECTED_1]
+	select [SELECTED_2] if [DEP]
+	imply [IMPLIED_1]
+	imply [IMPLIED_2] if [DEP]
+	help
+	  first help text
+
+config ADVANCED
+	tristate "prompt 2"
+
+menuconfig ADVANCED
+	tristate "prompt 3"
+
+config ADVANCED
+	tristate
+	depends on ([A] || ![B] || ([C] && [D]) || !([E] && [F]) || [G] = [H] || ([I] && ![J] && ([K] || [L]) && !([M] || [N]) && [O] = [P])) && [DEP4] && [DEP3]
+	help
+	  second help text
+
+config ADVANCED
+	tristate "prompt 4" if [VIS]
+	depends on [DEP4] && [DEP3]
+""")
+
+
+    verify_str(c.syms["ONLY_DIRECT_DEPS"], """
+config ONLY_DIRECT_DEPS
+	int
+	depends on DEP1 && DEP2
+""")
+
+    verify_str(c.syms["STRING"], """
+config STRING
+	string
+	default "foo"
+	default "bar" if DEP
+	default STRING2
+	default STRING3 if DEP
+""")
+
+    verify_str(c.syms["INT"], """
+config INT
+	int
+	range 1 2
+	range FOO BAR
+	range BAZ QAZ if DEP
+	default 7 if DEP
+""")
+
+    verify_str(c.syms["HEX"], """
+config HEX
+	hex
+	range 0x100 0x200
+	range FOO BAR
+	range BAZ QAZ if DEP
+	default 0x123
+""")
+
+    verify_str(c.modules, """
+config MODULES
+	bool "MODULES"
+	option modules
+""")
+
+    verify_str(c.syms["OPTIONS"], """
+config OPTIONS
+	option allnoconfig_y
+	option defconfig_list
+	option env="ENV"
+""")
+
+    verify_str(c.syms["CORRECT_PROP_LOCS_BOOL"], """
+config CORRECT_PROP_LOCS_BOOL
+	bool "prompt 1"
+	default DEFAULT_1
+	default DEFAULT_2
+	select SELECT_1
+	select SELECT_2
+	imply IMPLY_1
+	imply IMPLY_2
+	depends on LOC_1
+	help
+	  help 1
+
+menuconfig CORRECT_PROP_LOCS_BOOL
+	bool "prompt 2"
+	default DEFAULT_3
+	default DEFAULT_4
+	select SELECT_3
+	select SELECT_4
+	imply IMPLY_3
+	imply IMPLY_4
+	depends on LOC_2
+	help
+	  help 2
+
+config CORRECT_PROP_LOCS_BOOL
+	bool "prompt 3"
+	default DEFAULT_5
+	default DEFAULT_6
+	select SELECT_5
+	select SELECT_6
+	imply IMPLY_5
+	imply IMPLY_6
+	depends on LOC_3
+	help
+	  help 2
+""")
+
+    verify_str(c.syms["CORRECT_PROP_LOCS_INT"], """
+config CORRECT_PROP_LOCS_INT
+	int
+	range 1 2
+	range 3 4
+	depends on LOC_1
+
+config CORRECT_PROP_LOCS_INT
+	int
+	range 5 6
+	range 7 8
+	depends on LOC_2
+""")
+
+    verify_str(c.syms["PROMPT_ONLY"], """
+config PROMPT_ONLY
+	prompt "prompt only"
+""")
+
+    verify_custom_str(c.syms["CORRECT_PROP_LOCS_INT"], """
+config CORRECT_PROP_LOCS_INT
+	int
+	range [1] [2]
+	range [3] [4]
+	depends on [LOC_1]
+
+config CORRECT_PROP_LOCS_INT
+	int
+	range [5] [6]
+	range [7] [8]
+	depends on [LOC_2]
+""")
+
+
+
+    print("Testing Choice.__str__()/custom_str()")
+
+    verify_str(c.named_choices["CHOICE"], """
+choice CHOICE
+	tristate "foo"
+	default CHOICE_1
+	default CHOICE_2 if dep
+""")
+
+    verify_str(c.named_choices["CHOICE"].nodes[0].next.item, """
+choice
+	tristate "no name"
+	optional
+""")
+
+    verify_str(c.named_choices["CORRECT_PROP_LOCS_CHOICE"], """
+choice CORRECT_PROP_LOCS_CHOICE
+	bool
+	default CHOICE_3
+	depends on LOC_1
+
+choice CORRECT_PROP_LOCS_CHOICE
+	bool
+	default CHOICE_4
+	depends on LOC_2
+
+choice CORRECT_PROP_LOCS_CHOICE
+	bool
+	default CHOICE_5
+	depends on LOC_3
+""")
+
+    verify_custom_str(c.named_choices["CORRECT_PROP_LOCS_CHOICE"], """
+choice CORRECT_PROP_LOCS_CHOICE
+	bool
+	default [CHOICE_3]
+	depends on [LOC_1]
+
+choice CORRECT_PROP_LOCS_CHOICE
+	bool
+	default [CHOICE_4]
+	depends on [LOC_2]
+
+choice CORRECT_PROP_LOCS_CHOICE
+	bool
+	default [CHOICE_5]
+	depends on [LOC_3]
+""")
+
+
+    print("Testing MenuNode.__str__()/custom_str() for menus and comments")
+
+    verify_str(c.syms["SIMPLE_MENU_HOOK"].nodes[0].next, """
+menu "simple menu"
+""")
+
+    verify_str(c.syms["ADVANCED_MENU_HOOK"].nodes[0].next, """
+menu "advanced menu"
+	depends on A
+	visible if B && (C || D)
+""")
+
+    verify_custom_str(c.syms["ADVANCED_MENU_HOOK"].nodes[0].next, """
+menu "advanced menu"
+	depends on [A]
+	visible if [B] && ([C] || [D])
+""")
+
+    verify_str(c.syms["SIMPLE_COMMENT_HOOK"].nodes[0].next, """
+comment "simple comment"
+""")
+
+    verify_str(c.syms["ADVANCED_COMMENT_HOOK"].nodes[0].next, """
+comment "advanced comment"
+	depends on A && B
+""")
+
+    verify_custom_str(c.syms["ADVANCED_COMMENT_HOOK"].nodes[0].next, """
+comment "advanced comment"
+	depends on [A] && [B]
+""")
+
+
+    print("Testing {MenuNode,Symbol,Choice}.orig_*")
+
+    # Just test some corner cases here re. MenuNode.orig_*. They are already
+    # indirectly tested above. Use MenuNode.__str__() as a proxy.
+
+    verify_str(c.syms["DEP_REM_CORNER_CASES"], """
+config DEP_REM_CORNER_CASES
+	bool
+	default A
+	depends on n
+
+config DEP_REM_CORNER_CASES
+	bool
+	default B if n
+
+config DEP_REM_CORNER_CASES
+	bool
+	default C
+	depends on m && MODULES
+
+config DEP_REM_CORNER_CASES
+	bool
+	default D if A
+
+config DEP_REM_CORNER_CASES
+	bool
+	default E if !E1
+	default F if F1 = F2
+	default G if G1 || H1
+	depends on !H
+
+config DEP_REM_CORNER_CASES
+	bool
+	default H
+	depends on "foo" = "bar"
+
+config DEP_REM_CORNER_CASES
+	bool "prompt" if FOO || BAR
+	depends on BAZ && QAZ
+""")
+
+    # Test {Symbol,Choice}.orig_*
+
+    def verify_deps(elms, dep_index, expected):
+        verify_equal(" ".join(expr_str(elm[dep_index]) for elm in elms),
+                     expected)
+
+    verify_deps(c.syms["BOOL_SYM_ORIG"].orig_defaults,        1, "DEP y y")
+    verify_deps(c.syms["BOOL_SYM_ORIG"].orig_selects,         1, "y DEP y")
+    verify_deps(c.syms["BOOL_SYM_ORIG"].orig_implies,         1, "y y DEP")
+    verify_deps(c.syms["INT_SYM_ORIG"].orig_ranges,           2, "DEP y DEP")
+    verify_deps(c.named_choices["CHOICE_ORIG"].orig_defaults, 1, "y DEP DEP")
+
+
+    print("Testing Symbol.__repr__()")
+
+    def verify_repr(item, s):
+        verify_equal(repr(item) + "\n", s[1:])
+
+    c = Kconfig("Kconfiglib/tests/Krepr", warn=False)
+
+    verify_repr(c.n, """
+<symbol n, tristate, value n, constant>
+""")
+
+    verify_repr(c.m, """
+<symbol m, tristate, value m, constant>
+""")
+
+    verify_repr(c.y, """
+<symbol y, tristate, value y, constant>
+""")
+
+    verify_repr(c.syms["UNDEFINED"], """
+<symbol UNDEFINED, unknown, value "UNDEFINED", visibility n, direct deps n, undefined>
+""")
+
+    verify_repr(c.syms["BASIC"], """
+<symbol BASIC, bool, value y, visibility n, direct deps y, Kconfiglib/tests/Krepr:9>
+""")
+
+    verify_repr(c.syms["VISIBLE"], """
+<symbol VISIBLE, bool, "visible", value n, visibility y, direct deps y, Kconfiglib/tests/Krepr:14>
+""")
+
+    c.syms["VISIBLE"].set_value(2)
+    c.syms["STRING"].set_value("foo")
+
+    verify_repr(c.syms["VISIBLE"], """
+<symbol VISIBLE, bool, "visible", value y, user value y, visibility y, direct deps y, Kconfiglib/tests/Krepr:14>
+""")
+
+    verify_repr(c.syms["STRING"], """
+<symbol STRING, string, "visible", value "foo", user value "foo", visibility y, direct deps y, Kconfiglib/tests/Krepr:17>
+""")
+
+    verify_repr(c.syms["DIR_DEP_N"], """
+<symbol DIR_DEP_N, unknown, value "DIR_DEP_N", visibility n, direct deps n, Kconfiglib/tests/Krepr:20>
+""")
+
+    verify_repr(c.syms["OPTIONS"], """
+<symbol OPTIONS, unknown, value "OPTIONS", visibility n, allnoconfig_y, is the defconfig_list symbol, from environment variable ENV, direct deps y, Kconfiglib/tests/Krepr:23>
+""")
+
+    verify_repr(c.syms["MULTI_DEF"], """
+<symbol MULTI_DEF, unknown, value "MULTI_DEF", visibility n, direct deps y, Kconfiglib/tests/Krepr:28, Kconfiglib/tests/Krepr:29>
+""")
+
+    verify_repr(c.syms["CHOICE_1"], """
+<symbol CHOICE_1, tristate, "choice sym", value n, visibility m, choice symbol, direct deps m, Kconfiglib/tests/Krepr:36>
+""")
+
+    verify_repr(c.modules, """
+<symbol MODULES, bool, value y, visibility n, is the modules symbol, direct deps y, Kconfiglib/tests/Krepr:1>
+""")
+
+
+    print("Testing Choice.__repr__()")
+
+    verify_repr(c.named_choices["CHOICE"], """
+<choice CHOICE, tristate, "choice", mode m, visibility y, Kconfiglib/tests/Krepr:33>
+""")
+
+    c.named_choices["CHOICE"].set_value(2)
+
+    verify_repr(c.named_choices["CHOICE"], """
+<choice CHOICE, tristate, "choice", mode y, user mode y, CHOICE_1 selected, visibility y, Kconfiglib/tests/Krepr:33>
+""")
+
+    c.syms["CHOICE_2"].set_value(2)
+
+    verify_repr(c.named_choices["CHOICE"], """
+<choice CHOICE, tristate, "choice", mode y, user mode y, CHOICE_2 selected, CHOICE_2 selected by user, visibility y, Kconfiglib/tests/Krepr:33>
+""")
+
+    c.named_choices["CHOICE"].set_value(1)
+
+    verify_repr(c.named_choices["CHOICE"], """
+<choice CHOICE, tristate, "choice", mode m, user mode m, CHOICE_2 selected by user (overridden), visibility y, Kconfiglib/tests/Krepr:33>
+""")
+
+    verify_repr(c.syms["CHOICE_HOOK"].nodes[0].next.item, """
+<choice, tristate, "optional choice", mode n, visibility n, optional, Kconfiglib/tests/Krepr:46>
+""")
+
+
+    print("Testing MenuNode.__repr__()")
+
+    verify_repr(c.syms["BASIC"].nodes[0], """
+<menu node for symbol BASIC, deps y, has help, has next, Kconfiglib/tests/Krepr:9>
+""")
+
+    verify_repr(c.syms["DIR_DEP_N"].nodes[0], """
+<menu node for symbol DIR_DEP_N, deps n, has next, Kconfiglib/tests/Krepr:20>
+""")
+
+    verify_repr(c.syms["MULTI_DEF"].nodes[0], """
+<menu node for symbol MULTI_DEF, deps y, has next, Kconfiglib/tests/Krepr:28>
+""")
+
+    verify_repr(c.syms["MULTI_DEF"].nodes[1], """
+<menu node for symbol MULTI_DEF, deps y, has next, Kconfiglib/tests/Krepr:29>
+""")
+
+    verify_repr(c.syms["MENUCONFIG"].nodes[0], """
+<menu node for symbol MENUCONFIG, is menuconfig, deps y, has next, Kconfiglib/tests/Krepr:31>
+""")
+
+    verify_repr(c.named_choices["CHOICE"].nodes[0], """
+<menu node for choice CHOICE, prompt "choice" (visibility y), deps y, has child, has next, Kconfiglib/tests/Krepr:33>
+""")
+
+    verify_repr(c.syms["CHOICE_HOOK"].nodes[0].next, """
+<menu node for choice, prompt "optional choice" (visibility n), deps y, has next, Kconfiglib/tests/Krepr:46>
+""")
+
+    verify_repr(c.syms["NO_VISIBLE_IF_HOOK"].nodes[0].next, """
+<menu node for menu, prompt "no visible if" (visibility y), deps y, 'visible if' deps y, has next, Kconfiglib/tests/Krepr:53>
+""")
+
+    verify_repr(c.syms["VISIBLE_IF_HOOK"].nodes[0].next, """
+<menu node for menu, prompt "visible if" (visibility y), deps y, 'visible if' deps m, has next, Kconfiglib/tests/Krepr:58>
+""")
+
+    verify_repr(c.syms["COMMENT_HOOK"].nodes[0].next, """
+<menu node for comment, prompt "comment" (visibility y), deps y, Kconfiglib/tests/Krepr:64>
+""")
+
+
+    print("Testing Kconfig.__repr__()")
+
+    verify_repr(c, """
+<configuration with 15 symbols, main menu prompt "Main menu", srctree is current directory, config symbol prefix "CONFIG_", warnings disabled, printing of warnings to stderr enabled, undef. symbol assignment warnings disabled, overriding symbol assignment warnings enabled, redundant symbol assignment warnings enabled>
+""")
+
+    os.environ["srctree"] = "Kconfiglib"
+    os.environ["CONFIG_"] = "CONFIG_ value"
+
+    c = Kconfig("tests/Krepr", warn=False)
+    c.warn = True
+    c.warn_to_stderr = False
+    c.warn_assign_override = False
+    c.warn_assign_redun = False
+    c.warn_assign_undef = True
+
+    verify_repr(c, """
+<configuration with 15 symbols, main menu prompt "Main menu", srctree "Kconfiglib", config symbol prefix "CONFIG_ value", warnings enabled, printing of warnings to stderr disabled, undef. symbol assignment warnings enabled, overriding symbol assignment warnings disabled, redundant symbol assignment warnings disabled>
+""")
+
+    os.environ.pop("srctree", None)
+    os.environ.pop("CONFIG_", None)
+
+
+    print("Testing tricky help strings")
+
+    c = Kconfig("Kconfiglib/tests/Khelp")
+
+    def verify_help(node, s):
+        verify_equal(node.help, s[1:-1])
+
+    verify_help(c.syms["TWO_HELP_STRINGS"].nodes[0], """
+first help string
+""")
+
+    verify_help(c.syms["TWO_HELP_STRINGS"].nodes[1], """
+second help string
+""")
+
+    verify_help(c.syms["NO_BLANK_AFTER_HELP"].nodes[0], """
+help for
+NO_BLANK_AFTER_HELP
+""")
+
+    verify_help(c.named_choices["CHOICE_HELP"].nodes[0], """
+help for
+CHOICE_HELP
+""")
+
+    verify_help(c.syms["HELP_TERMINATED_BY_COMMENT"].nodes[0], """
+a
+b
+c
+""")
+
+    verify_help(c.syms["TRICKY_HELP"].nodes[0], """
+a
+ b
+  c
+
+ d
+  e
+   f
+
+
+g
+ h
+  i
+""")
+
+
+    print("Testing locations, source/rsource/gsource/grsource, and "
+          "Kconfig.kconfig_filenames")
+
+    def verify_locations(nodes, *expected_locs):
+        verify(len(nodes) == len(expected_locs),
+               "Wrong number of locations for " + repr(nodes))
+
+        for node, expected_loc in zip(nodes, expected_locs):
+            node_loc = "{}:{}".format(node.filename, node.linenr)
+            verify(node_loc == expected_loc,
+                   "expected {} to have the location {}, had the location {}"
+                   .format(repr(node), expected_loc, node_loc))
+
+    # Expanded in the 'source' statement in Klocation
+
+    os.environ["TESTS_DIR_FROM_ENV"] = "tests"
+    os.environ["SUB_DIR_FROM_ENV"] = "sub"
+
+    os.environ["_SOURCED"] = "_sourced"
+    os.environ["_RSOURCED"] = "_rsourced"
+    os.environ["_GSOURCED"] = "_gsourced"
+    os.environ["_GRSOURCED"] = "_grsourced"
+
+    # Test twice, with $srctree as a relative and an absolute path,
+    # respectively
+    for srctree in "Kconfiglib", os.path.abspath("Kconfiglib"):
+        os.environ["srctree"] = srctree
+
+        # Has symbol with empty help text, so disable warnings
+        c = Kconfig("tests/Klocation", warn=False)
+
+        verify_locations(c.syms["UNDEFINED"].nodes)
+        verify_equal(c.syms["UNDEFINED"].name_and_loc, "UNDEFINED (undefined)")
+
+        verify_locations(c.syms["ONE_DEF"].nodes, "tests/Klocation:4")
+        verify_equal(c.syms["ONE_DEF"].name_and_loc,
+                     "ONE_DEF (defined at tests/Klocation:4)")
+
+        verify_locations(c.syms["TWO_DEF"].nodes,
+                         "tests/Klocation:7",
+                         "tests/Klocation:10")
+        verify_equal(c.syms["TWO_DEF"].name_and_loc,
+                     "TWO_DEF (defined at tests/Klocation:7, tests/Klocation:10)")
+
+        verify_locations(c.syms["MANY_DEF"].nodes,
+                         "tests/Klocation:13",
+                         "tests/Klocation:43",
+                         "tests/Klocation:45",
+                         "tests/Klocation_sourced:3",
+                         "tests/sub/Klocation_rsourced:2",
+                         "tests/sub/Klocation_gsourced1:1",
+                         "tests/sub/Klocation_gsourced2:1",
+                         "tests/sub/Klocation_gsourced1:1",
+                         "tests/sub/Klocation_gsourced2:1",
+                         "tests/sub/Klocation_grsourced1:1",
+                         "tests/sub/Klocation_grsourced2:1",
+                         "tests/sub/Klocation_grsourced1:1",
+                         "tests/sub/Klocation_grsourced2:1",
+                         "tests/Klocation:78")
+
+        verify_locations(c.named_choices["CHOICE_ONE_DEF"].nodes,
+                         "tests/Klocation_sourced:5")
+        verify_equal(c.named_choices["CHOICE_ONE_DEF"].name_and_loc,
+                     "<choice CHOICE_ONE_DEF> (defined at tests/Klocation_sourced:5)")
+
+        verify_locations(c.named_choices["CHOICE_TWO_DEF"].nodes,
+                         "tests/Klocation_sourced:9",
+                         "tests/Klocation_sourced:13")
+        verify_equal(c.named_choices["CHOICE_TWO_DEF"].name_and_loc,
+                     "<choice CHOICE_TWO_DEF> (defined at tests/Klocation_sourced:9, tests/Klocation_sourced:13)")
+
+        verify_locations([c.syms["MENU_HOOK"].nodes[0].next],
+                         "tests/Klocation_sourced:20")
+
+        verify_locations([c.syms["COMMENT_HOOK"].nodes[0].next],
+                         "tests/Klocation_sourced:26")
+
+        # Test Kconfig.kconfig_filenames
+
+        verify_equal(c.kconfig_filenames, [
+            "tests/Klocation",
+            "tests/Klocation_sourced",
+            "tests/sub/Klocation_rsourced",
+            "tests/sub/Klocation_gsourced1",
+            "tests/sub/Klocation_gsourced2",
+            "tests/sub/Klocation_gsourced1",
+            "tests/sub/Klocation_gsourced2",
+            "tests/sub/Klocation_grsourced1",
+            "tests/sub/Klocation_grsourced2",
+            "tests/sub/Klocation_grsourced1",
+            "tests/sub/Klocation_grsourced2"
+        ])
+
+        # Test recursive 'source' detection
+
+        try:
+            Kconfig("tests/Krecursive1")
+        except KconfigError as e:
+            verify_equal(str(e), """
+tests/Krecursive2:1: recursive 'source' of 'tests/Krecursive1' detected. Check that environment variables are set correctly.
+Include path:
+tests/Krecursive1:1
+tests/Krecursive2:1
+"""[:-1])
+        except:
+            fail("recursive 'source' raised wrong exception")
+        else:
+            fail("recursive 'source' did not raise exception")
+
+        # Verify that source and rsource throw exceptions for missing files
+
+        # TODO: Make an exception test helper
+
+        try:
+            Kconfig("tests/Kmissingsource")
+        except KconfigError as e:
+            if "not found" not in str(e):
+                fail("'source' with missing file raised wrong KconfigError")
+        except:
+            fail("'source' with missing file raised wrong exception")
+        else:
+            fail("'source' with missing file did not raise exception")
+
+        try:
+            Kconfig("tests/Kmissingrsource")
+        except KconfigError as e:
+            if "not found" not in str(e):
+                fail("'rsource' with missing file raised wrong KconfigError")
+        except:
+            fail("'rsource' with missing file raised wrong exception")
+        else:
+            fail("'rsource' with missing file did not raise exception")
+
+    # Test a tricky case involving symlinks. $srctree is tests/symlink, which
+    # points to tests/sub/sub, meaning tests/symlink/.. != tests/. Previously,
+    # using 'rsource' from a file sourced with an absolute path triggered an
+    # unsafe relpath() with tests/symlink/.. in it, crashing.
+
+    os.environ["srctree"] = "Kconfiglib/tests/symlink"
+    os.environ["KCONFIG_SYMLINK_2"] = os.path.abspath(
+        "Kconfiglib/tests/sub/Kconfig_symlink_2")
+    if not os.path.isabs(
+        Kconfig("Kconfig_symlink_1").syms["FOUNDME"].nodes[0].filename):
+
+        fail("Symlink + rsource issues")
+
+
+    print("Testing Kconfig.node_iter()")
+
+    # Reuse tests/Klocation. The node_iter(unique_syms=True) case already gets
+    # plenty of testing from write_config() as well.
+
+    os.environ["srctree"] = "Kconfiglib"
+    c = Kconfig("tests/Klocation", warn=False)
+
+    verify_equal(
+        [node.item.name for node in c.node_iter()
+         if isinstance(node.item, Symbol)],
+        ["ONE_DEF", "TWO_DEF", "TWO_DEF", "MANY_DEF", "HELP_1", "HELP_2",
+         "HELP_3", "MANY_DEF", "MANY_DEF", "MANY_DEF", "MENU_HOOK",
+         "COMMENT_HOOK"] + 10*["MANY_DEF"])
+
+    verify_equal(
+        [node.item.name for node in c.node_iter(True)
+         if isinstance(node.item, Symbol)],
+        ["ONE_DEF", "TWO_DEF", "MANY_DEF", "HELP_1", "HELP_2", "HELP_3",
+         "MENU_HOOK", "COMMENT_HOOK"])
+
+    verify_equal(
+        [node.prompt[0] for node in c.node_iter()
+         if not isinstance(node.item, Symbol)],
+        ["one-def choice", "two-def choice 1", "two-def choice 2",
+         "menu", "comment"])
+
+    verify_equal(
+        [node.prompt[0] for node in c.node_iter(True)
+         if not isinstance(node.item, Symbol)],
+        ["one-def choice", "two-def choice 1", "two-def choice 2",
+         "menu", "comment"])
+
+
+    print("Testing MenuNode.include_path")
+
+    os.environ["srctree"] = "Kconfiglib/tests"
+
+    c = Kconfig("Kinclude_path")
+
+    def verify_node_path(node, *expected):
+        if node.include_path != expected:
+            fail("Wrong include path for node {!r}. Got {}, expected {}."
+                 .format(node, node.include_path, expected))
+
+    def verify_sym_path(sym_name, node_i, *expected):
+        verify_node_path(c.syms[sym_name].nodes[node_i], *expected)
+
+    verify_sym_path("TOP", 0)
+    verify_sym_path("TOP", 1)
+    verify_sym_path("TOP", 2)
+
+    verify_sym_path("ONE_DOWN", 0, ("Kinclude_path", 4))
+    verify_sym_path("ONE_DOWN", 1, ("Kinclude_path", 4))
+    verify_sym_path("ONE_DOWN", 2, ("Kinclude_path", 4))
+    verify_sym_path("ONE_DOWN", 3, ("Kinclude_path", 9))
+    verify_sym_path("ONE_DOWN", 4, ("Kinclude_path", 9))
+    verify_sym_path("ONE_DOWN", 5, ("Kinclude_path", 9))
+
+    verify_sym_path("TWO_DOWN", 0,
+                    ("Kinclude_path", 4), ("Kinclude_path_sourced_1", 4))
+    verify_sym_path("TWO_DOWN", 1,
+                    ("Kinclude_path", 4), ("Kinclude_path_sourced_1", 9))
+    verify_sym_path("TWO_DOWN", 2,
+                    ("Kinclude_path", 9), ("Kinclude_path_sourced_1", 4))
+    verify_sym_path("TWO_DOWN", 3,
+                    ("Kinclude_path", 9), ("Kinclude_path_sourced_1", 9))
+
+    verify_node_path(c.top_node)
+    verify_node_path(c.menus[0], ("Kinclude_path", 4), ("Kinclude_path_sourced_1", 4))
+    verify_node_path(c.comments[0], ("Kinclude_path", 4), ("Kinclude_path_sourced_1", 4))
+    verify_node_path(c.choices[0].nodes[0], ("Kinclude_path", 4), ("Kinclude_path_sourced_1", 4))
+
+    os.environ.pop("srctree", None)
+
+
+    print("Testing Kconfig.choices/menus/comments")
+
+    c = Kconfig("Kconfiglib/tests/Kitemlists")
+
+    def verify_prompts(items, *expected_prompts):
+        verify(len(items) == len(expected_prompts),
+               "Wrong number of prompts for {}".format(items))
+
+        for item, expected_prompt in zip(items, expected_prompts):
+            if not isinstance(item, MenuNode):
+                item = item.nodes[0]
+
+            verify(item.prompt[0] == expected_prompt,
+                   "Wrong prompt for {}, expected '{}'"
+                   .format(repr(item), expected_prompt))
+
+    verify_prompts(c.choices, "choice 1", "choice 2", "choice 3", "choice 2")
+    verify_prompts(c.menus, "menu 1", "menu 2", "menu 3", "menu 4", "menu 5")
+    verify_prompts(c.comments, "comment 1", "comment 2", "comment 3")
+
+
+    print("Testing Symbol/Choice.direct_dep")
+
+    c = Kconfig("Kconfiglib/tests/Kdirdep")
+
+    verify_equal(expr_str(c.syms["NO_DEP_SYM"].direct_dep), 'y')
+    verify_equal(expr_str(c.syms["DEP_SYM"].direct_dep), "A || (B && C) || !D")
+
+    verify_equal(expr_str(c.named_choices["NO_DEP_CHOICE"].direct_dep), 'y')
+    verify_equal(expr_str(c.named_choices["DEP_CHOICE"].direct_dep),
+                 "A || B || C")
+
+
+    print("Testing expr_items()")
+
+    c = Kconfig("Kconfiglib/tests/Kexpr_items")
+
+    def verify_expr_items(expr, *sym_names):
+        verify_equal(tuple(sorted(item.name for item in expr_items(expr))),
+                     sym_names)
+
+    verify_expr_items(
+        c.syms["TEST"].defaults[0][0],
+        "A", "B", "C", "D", "E", "F", "G", "H"
+    )
+
+    verify_expr_items(
+        c.syms["TEST_CHOICE"].nodes[0].prompt[1],
+        "A", "CHOICE"
+    )
+
+
+    print("Testing MenuNode/Symbol/Choice.referenced")
+
+    c = Kconfig("Kconfiglib/tests/Kreferenced", warn=False)
+
+    def verify_deps(item, *dep_names):
+        verify_equal(tuple(sorted(item.name for item in item.referenced)),
+                     dep_names)
+
+    verify_deps(c.top_node, "y")
+
+    verify_deps(c.syms["NO_REFS"].nodes[0], "y")
+
+    verify_deps(c.syms["JUST_DEPENDS_ON_REFS"].nodes[0], "A", "B")
+
+    verify_deps(c.syms["LOTS_OF_REFS"].nodes[0],
+                *(chr(n) for n in range(ord("A"), ord("Z") + 1)))
+
+    verify_deps(c.syms["INT_REFS"].nodes[0],
+                "A", "B", "C", "D", "E", "F", "G", "H", "y")
+
+    verify_deps(c.syms["CHOICE_REF"].nodes[0], "CHOICE")
+
+    verify_deps(c.menus[0], "A", "B", "C", "D")
+
+    verify_deps(c.comments[0], "A", "B")
+
+    verify_deps(c.syms["MULTI_DEF_SYM"], "A", "B", "C", "y")
+    verify_deps(c.named_choices["MULTI_DEF_CHOICE"], "A", "B", "C")
+
+
+    print("Testing split_expr()")
+
+    c = Kconfig("Kconfiglib/tests/empty")
+    c.warn = False
+
+    def verify_split(to_split, op, operand_strs):
+        # The same hackage as in Kconfig.eval_string()
+        c._tokens = c._tokenize("if " + to_split)[1:]
+        c._tokens_i = 0
+
+        operands = split_expr(c._parse_expr(False), op)
+
+        verify(len(operands) == len(operand_strs),
+               "Wrong number of operands when {} was split by {}"
+               .format(to_split, "OR" if op == OR else "AND"))
+
+        for operand, operand_str in zip(operands, operand_strs):
+            verify_equal(expr_str(operand), operand_str)
+
+    verify_split("A",                    OR, ("A",                ))
+    verify_split("!A",                   OR, ("!A",               ))
+    verify_split("A = B",                OR, ("A = B",            ))
+    verify_split("A && B",               OR, ("A && B",           ))
+    verify_split("A || B",               OR, ("A", "B"            ))
+    verify_split("(A || B) || C",        OR, ("A", "B", "C"       ))
+    verify_split("A || (B || C)",        OR, ("A", "B", "C"       ))
+    verify_split("A || !(B || C)",       OR, ("A", "!(B || C)"    ))
+    verify_split("A || (B && (C || D))", OR, ("A", "B && (C || D)"))
+    verify_split("(A && (B || C)) || D", OR, ("A && (B || C)", "D"))
+
+    verify_split("A",                    AND, ("A",                ))
+    verify_split("!A",                   AND, ("!A",               ))
+    verify_split("A = B",                AND, ("A = B",            ))
+    verify_split("A || B",               AND, ("A || B",           ))
+    verify_split("A && B",               AND, ("A", "B"            ))
+    verify_split("(A && B) && C",        AND, ("A", "B", "C"       ))
+    verify_split("A && (B && C)",        AND, ("A", "B", "C"       ))
+    verify_split("A && !(B && C)",       AND, ("A", "!(B && C)"    ))
+    verify_split("A && (B || (C && D))", AND, ("A", "B || (C && D)"))
+    verify_split("(A || (B && C)) && D", AND, ("A || (B && C)", "D"))
+
+
+    print("Testing visibility")
+
+    c = Kconfig("Kconfiglib/tests/Kvisibility")
+
+    def verify_visibility(item, no_module_vis, module_vis):
+        c.modules.set_value(0)
+        verify(item.visibility == no_module_vis,
+               "expected {} to have visibility {} without modules, had "
+               "visibility {}".
+               format(repr(item), no_module_vis, item.visibility))
+
+        c.modules.set_value(2)
+        verify(item.visibility == module_vis,
+               "expected {} to have visibility {} with modules, had "
+               "visibility {}".
+               format(repr(item), module_vis, item.visibility))
+
+    # Symbol visibility
+
+    verify_visibility(c.syms["NO_PROMPT"],     0, 0)
+    verify_visibility(c.syms["BOOL_N"],        0, 0)
+    verify_visibility(c.syms["BOOL_M"],        0, 2)
+    verify_visibility(c.syms["BOOL_MOD"],      2, 2)
+    verify_visibility(c.syms["BOOL_Y"],        2, 2)
+    verify_visibility(c.syms["TRISTATE_M"],    0, 1)
+    verify_visibility(c.syms["TRISTATE_MOD"],  2, 1)
+    verify_visibility(c.syms["TRISTATE_Y"],    2, 2)
+    verify_visibility(c.syms["BOOL_IF_N"],     0, 0)
+    verify_visibility(c.syms["BOOL_IF_M"],     0, 2)
+    verify_visibility(c.syms["BOOL_IF_Y"],     2, 2)
+    verify_visibility(c.syms["BOOL_MENU_N"],   0, 0)
+    verify_visibility(c.syms["BOOL_MENU_M"],   0, 2)
+    verify_visibility(c.syms["BOOL_MENU_Y"],   2, 2)
+    verify_visibility(c.syms["BOOL_CHOICE_N"], 0, 0)
+
+    # Non-tristate symbols in tristate choices are only visible if the choice
+    # is in y mode
+
+    # The choice can't be brought to y mode because of the 'if m'
+    verify_visibility(c.syms["BOOL_CHOICE_M"], 0, 0)
+    c.syms["BOOL_CHOICE_M"].choice.set_value(2)
+    verify_visibility(c.syms["BOOL_CHOICE_M"], 0, 0)
+
+    # The choice gets y mode only when running without modules, because it
+    # defaults to m mode
+    verify_visibility(c.syms["BOOL_CHOICE_Y"], 2, 0)
+    c.syms["BOOL_CHOICE_Y"].choice.set_value(2)
+    # When set to y mode, the choice symbol becomes visible both with and
+    # without modules
+    verify_visibility(c.syms["BOOL_CHOICE_Y"], 2, 2)
+
+    verify_visibility(c.syms["TRISTATE_IF_N"],     0, 0)
+    verify_visibility(c.syms["TRISTATE_IF_M"],     0, 1)
+    verify_visibility(c.syms["TRISTATE_IF_Y"],     2, 2)
+    verify_visibility(c.syms["TRISTATE_MENU_N"],   0, 0)
+    verify_visibility(c.syms["TRISTATE_MENU_M"],   0, 1)
+    verify_visibility(c.syms["TRISTATE_MENU_Y"],   2, 2)
+    verify_visibility(c.syms["TRISTATE_CHOICE_N"], 0, 0)
+    verify_visibility(c.syms["TRISTATE_CHOICE_M"], 0, 1)
+    verify_visibility(c.syms["TRISTATE_CHOICE_Y"], 2, 2)
+
+    verify_visibility(c.named_choices["BOOL_CHOICE_N"],     0, 0)
+    verify_visibility(c.named_choices["BOOL_CHOICE_M"],     0, 2)
+    verify_visibility(c.named_choices["BOOL_CHOICE_Y"],     2, 2)
+    verify_visibility(c.named_choices["TRISTATE_CHOICE_N"], 0, 0)
+    verify_visibility(c.named_choices["TRISTATE_CHOICE_M"], 0, 1)
+    verify_visibility(c.named_choices["TRISTATE_CHOICE_Y"], 2, 2)
+
+    verify_visibility(c.named_choices["TRISTATE_CHOICE_IF_M_AND_Y"],   0, 1)
+    verify_visibility(c.named_choices["TRISTATE_CHOICE_MENU_N_AND_Y"], 0, 0)
+
+    # Verify that 'visible if' visibility gets propagated to prompts
+
+    verify_visibility(c.syms["VISIBLE_IF_N"], 0, 0)
+    verify_visibility(c.syms["VISIBLE_IF_M"], 0, 1)
+    verify_visibility(c.syms["VISIBLE_IF_Y"], 2, 2)
+    verify_visibility(c.syms["VISIBLE_IF_M_2"], 0, 1)
+
+    # Verify that string/int/hex symbols with m visibility accept a user value
+
+    assign_and_verify("STRING_m", "foo bar")
+    assign_and_verify("INT_m", "123")
+    assign_and_verify("HEX_m", "0x123")
+
+
+    print("Testing .assignable")
+
+    c = Kconfig("Kconfiglib/tests/Kassignable")
+
+    def verify_assignable_imp(item, assignable_no_modules, assignable_modules):
+        # Verifies the assignable values for 'item', with and without modules.
+
+        for modules_val, assignable in (0, assignable_no_modules), \
+                                       (2, assignable_modules):
+
+            c.modules.set_value(modules_val)
+            module_msg = "without modules" if modules_val == 0 else \
+                         "with modules"
+
+            verify(item.assignable == assignable,
+                   "Incorrect assignable values for {} {}. Should be {}, "
+                   "was {}."
+                   .format(item.name, module_msg, assignable, item.assignable))
+
+            # Verify that the values can actually be assigned too
+
+            for val in item.assignable:
+                item.set_value(val)
+                verify(item.tri_value == val,
+                       "Unable to set {} to {} {}, even though it was in "
+                       ".assignable".format(item.name, val, module_msg))
+
+    def verify_assignable(sym_name, assignable_no_modules, assignable_modules):
+        verify_assignable_imp(c.syms[sym_name],
+                              assignable_no_modules,
+                              assignable_modules)
+
+    def verify_const_unassignable(sym_name):
+        verify_assignable_imp(c.const_syms[sym_name], (), ())
+
+    # Things that shouldn't be .assignable
+    verify_const_unassignable("n")
+    verify_const_unassignable("m")
+    verify_const_unassignable("y")
+    verify_const_unassignable("const")
+    verify_assignable("UNDEFINED", (), ())
+    verify_assignable("NO_PROMPT", (), ())
+    verify_assignable("STRING", (), ())
+    verify_assignable("INT", (), ())
+    verify_assignable("HEX", (), ())
+
+    # Non-selected symbols
+    verify_assignable("Y_VIS_BOOL", (0, 2), (0,    2))
+    verify_assignable("M_VIS_BOOL", (    ), (0,    2))  # Vis. promoted
+    verify_assignable("N_VIS_BOOL", (    ), (       ))
+    verify_assignable("Y_VIS_TRI",  (0, 2), (0, 1, 2))
+    verify_assignable("M_VIS_TRI",  (    ), (0, 1   ))
+    verify_assignable("N_VIS_TRI",  (    ), (       ))
+
+    # Symbols selected to y
+    verify_assignable("Y_SEL_Y_VIS_BOOL", (2,), (2,))
+    verify_assignable("Y_SEL_M_VIS_BOOL", (  ), (2,))  # Vis. promoted
+    verify_assignable("Y_SEL_N_VIS_BOOL", (  ), (  ))
+    verify_assignable("Y_SEL_Y_VIS_TRI",  (2,), (2,))
+    verify_assignable("Y_SEL_M_VIS_TRI",  (  ), (2,))
+    verify_assignable("Y_SEL_N_VIS_TRI",  (  ), (  ))
+
+    # Symbols selected to m
+    verify_assignable("M_SEL_Y_VIS_BOOL", (2,), (   2,))  # Value promoted
+    verify_assignable("M_SEL_M_VIS_BOOL", (  ), (   2,))  # Vis./value promoted
+    verify_assignable("M_SEL_N_VIS_BOOL", (  ), (     ))
+    verify_assignable("M_SEL_Y_VIS_TRI",  (2,), (1, 2 ))
+    verify_assignable("M_SEL_M_VIS_TRI",  (  ), (1,   ))
+    verify_assignable("M_SEL_N_VIS_TRI",  (  ), (     ))
+
+    # Symbols implied to y
+    verify_assignable("Y_IMP_Y_VIS_BOOL", (0, 2), (0, 2))
+    verify_assignable("Y_IMP_M_VIS_BOOL", (    ), (0, 2))  # Vis. promoted
+    verify_assignable("Y_IMP_N_VIS_BOOL", (    ), (    ))
+    verify_assignable("Y_IMP_Y_VIS_TRI",  (0, 2), (0, 2))  # m removed by imply
+    verify_assignable("Y_IMP_M_VIS_TRI",  (    ), (0, 2))  # m promoted to y by imply
+    verify_assignable("Y_IMP_N_VIS_TRI",  (    ), (    ))
+
+    # Symbols implied to m (never affects assignable values)
+    verify_assignable("M_IMP_Y_VIS_BOOL", (0, 2), (0,    2))
+    verify_assignable("M_IMP_M_VIS_BOOL", (    ), (0,    2))  # Vis. promoted
+    verify_assignable("M_IMP_N_VIS_BOOL", (    ), (       ))
+    verify_assignable("M_IMP_Y_VIS_TRI",  (0, 2), (0, 1, 2))
+    verify_assignable("M_IMP_M_VIS_TRI",  (    ), (0, 1   ))
+    verify_assignable("M_IMP_N_VIS_TRI",  (    ), (       ))
+
+    # Symbols in y-mode choice
+    verify_assignable("Y_CHOICE_BOOL",           (2,), (2,))
+    verify_assignable("Y_CHOICE_TRISTATE",       (2,), (2,))
+    verify_assignable("Y_CHOICE_N_VIS_TRISTATE", (  ), (  ))
+
+    # Symbols in m/y-mode choice, starting out in m mode, or y mode when
+    # running without modules
+    verify_assignable("MY_CHOICE_BOOL",           (2,), (    ))
+    verify_assignable("MY_CHOICE_TRISTATE",       (2,), (0, 1))
+    verify_assignable("MY_CHOICE_N_VIS_TRISTATE", (  ), (    ))
+
+    c.named_choices["MY_CHOICE"].set_value(2)
+
+    # Symbols in m/y-mode choice, now in y mode
+    verify_assignable("MY_CHOICE_BOOL",           (2,), (2,))
+    verify_assignable("MY_CHOICE_TRISTATE",       (2,), (2,))
+    verify_assignable("MY_CHOICE_N_VIS_TRISTATE", (  ), (  ))
+
+    def verify_choice_assignable(choice_name, assignable_no_modules,
+                                 assignable_modules):
+        verify_assignable_imp(c.named_choices[choice_name],
+                              assignable_no_modules,
+                              assignable_modules)
+
+    # Choices with various possible modes
+    verify_choice_assignable("Y_CHOICE",   (2,  ), (      2,))
+    verify_choice_assignable("MY_CHOICE",  (2,  ), (   1, 2 ))
+    verify_choice_assignable("NMY_CHOICE", (0, 2), (0, 1, 2 ))
+    verify_choice_assignable("NY_CHOICE",  (0, 2), (0,    2 ))
+    verify_choice_assignable("NM_CHOICE",  (    ), (0, 1    ))
+    verify_choice_assignable("M_CHOICE",   (    ), (   1,   ))
+    verify_choice_assignable("N_CHOICE",   (    ), (        ))
+
+
+    print("Testing object relations")
+
+    c = Kconfig("Kconfiglib/tests/Krelation")
+
+    verify(c.syms["A"].nodes[0].parent is c.top_node,
+           "A's parent should be the top node")
+
+    verify(c.syms["B"].nodes[0].parent.item is c.named_choices["CHOICE_1"],
+           "B's parent should be the first choice")
+
+    verify(c.syms["C"].nodes[0].parent.item is c.syms["B"],
+           "C's parent should be B (due to auto menus)")
+
+    verify(c.syms["E"].nodes[0].parent.item == MENU,
+           "E's parent should be a menu")
+
+    verify(c.syms["E"].nodes[0].parent.parent is c.top_node,
+           "E's grandparent should be the top node")
+
+    verify(c.syms["G"].nodes[0].parent.item is c.named_choices["CHOICE_2"],
+           "G's parent should be the second choice")
+
+    verify(c.syms["G"].nodes[0].parent.parent.item == MENU,
+           "G's grandparent should be a menu")
+
+
+    print("Testing hex/int ranges")
+
+    c = Kconfig("Kconfiglib/tests/Krange", warn=False)
+
+    for sym_name in "HEX_NO_RANGE", "INT_NO_RANGE", "HEX_40", "INT_40":
+        sym = c.syms[sym_name]
+        verify(not sym.ranges,
+               "{} should not have ranges".format(sym_name))
+
+    for sym_name in "HEX_ALL_RANGES_DISABLED", "INT_ALL_RANGES_DISABLED", \
+                    "HEX_RANGE_10_20_LOW_DEFAULT", \
+                    "INT_RANGE_10_20_LOW_DEFAULT":
+        sym = c.syms[sym_name]
+        verify(sym.ranges, "{} should have ranges".format(sym_name))
+
+    # hex/int symbols without defaults should get no default value
+    verify_value("HEX_NO_RANGE", "")
+    verify_value("INT_NO_RANGE", "")
+    # And neither if all ranges are disabled
+    verify_value("HEX_ALL_RANGES_DISABLED", "")
+    verify_value("INT_ALL_RANGES_DISABLED", "")
+    # Make sure they are assignable though, and test that the form of the user
+    # value is reflected in the value for hex symbols
+    assign_and_verify("HEX_NO_RANGE", "0x123")
+    assign_and_verify("HEX_NO_RANGE", "123")
+    assign_and_verify("INT_NO_RANGE", "123")
+
+    # Defaults outside of the valid range should be clamped
+    verify_value("HEX_RANGE_10_20_LOW_DEFAULT", "0x10")
+    verify_value("HEX_RANGE_10_20_HIGH_DEFAULT", "0x20")
+    verify_value("INT_RANGE_10_20_LOW_DEFAULT", "10")
+    verify_value("INT_RANGE_10_20_HIGH_DEFAULT", "20")
+    # Defaults inside the valid range should be preserved. For hex symbols,
+    # they should additionally use the same form as in the assignment.
+    verify_value("HEX_RANGE_10_20_OK_DEFAULT", "0x15")
+    verify_value("HEX_RANGE_10_20_OK_DEFAULT_ALTERNATE", "15")
+    verify_value("INT_RANGE_10_20_OK_DEFAULT", "15")
+
+    # hex/int symbols with no defaults but valid ranges should default to the
+    # lower end of the range if it's > 0
+    verify_value("HEX_RANGE_10_20", "0x10")
+    verify_value("HEX_RANGE_0_10", "")
+    verify_value("INT_RANGE_10_20", "10")
+    verify_value("INT_RANGE_0_10", "")
+    verify_value("INT_RANGE_NEG_10_10", "")
+
+    # User values and dependent ranges
+
+    # Avoid warnings for assigning values outside the active range
+    c.warn = False
+
+    def verify_range(sym_name, low, high, default):
+        # Verifies that all values in the range 'low'-'high' can be assigned,
+        # and that assigning values outside the range reverts the value back to
+        # 'default' (None if it should revert back to "").
+
+        is_hex = (c.syms[sym_name].type == HEX)
+
+        for i in range(low, high + 1):
+            assign_and_verify_user_value(sym_name, str(i), str(i), True)
+            if is_hex:
+                # The form of the user value should be preserved for hex
+                # symbols
+                assign_and_verify_user_value(sym_name, hex(i), hex(i), True)
+
+        # Verify that assigning a user value just outside the range causes
+        # defaults to be used
+
+        if default is None:
+            default_str = ""
+        else:
+            default_str = hex(default) if is_hex else str(default)
+
+        if is_hex:
+            too_low_str = hex(low - 1)
+            too_high_str = hex(high + 1)
+        else:
+            too_low_str = str(low - 1)
+            too_high_str = str(high + 1)
+
+        assign_and_verify_value(sym_name, too_low_str, default_str)
+        assign_and_verify_value(sym_name, too_high_str, default_str)
+
+    verify_range("HEX_RANGE_10_20_LOW_DEFAULT",  0x10, 0x20,  0x10)
+    verify_range("HEX_RANGE_10_20_HIGH_DEFAULT", 0x10, 0x20,  0x20)
+    verify_range("HEX_RANGE_10_20_OK_DEFAULT",   0x10, 0x20,  0x15)
+
+    verify_range("INT_RANGE_10_20_LOW_DEFAULT",  10,   20,    10)
+    verify_range("INT_RANGE_10_20_HIGH_DEFAULT", 10,   20,    20)
+    verify_range("INT_RANGE_10_20_OK_DEFAULT",   10,   20,    15)
+
+    verify_range("HEX_RANGE_10_20",              0x10, 0x20,  0x10)
+
+    verify_range("INT_RANGE_10_20",              10,  20,     10)
+    verify_range("INT_RANGE_0_10",               0,   10,     None)
+    verify_range("INT_RANGE_NEG_10_10",          -10, 10,     None)
+
+    # Dependent ranges
+
+    verify_value("HEX_40", "40")
+    verify_value("INT_40", "40")
+
+    c.syms["HEX_RANGE_10_20"].unset_value()
+    c.syms["INT_RANGE_10_20"].unset_value()
+    verify_value("HEX_RANGE_10_40_DEPENDENT", "0x10")
+    verify_value("INT_RANGE_10_40_DEPENDENT", "10")
+    c.syms["HEX_RANGE_10_20"].set_value("15")
+    c.syms["INT_RANGE_10_20"].set_value("15")
+    verify_value("HEX_RANGE_10_40_DEPENDENT", "0x15")
+    verify_value("INT_RANGE_10_40_DEPENDENT", "15")
+    c.unset_values()
+    verify_range("HEX_RANGE_10_40_DEPENDENT", 0x10, 0x40,  0x10)
+    verify_range("INT_RANGE_10_40_DEPENDENT", 10,   40,    10)
+
+    # Ranges and symbols defined in multiple locations
+
+    verify_value("INACTIVE_RANGE", "2")
+    verify_value("ACTIVE_RANGE", "1")
+
+
+    print("Testing defconfig_filename")
+
+    c = Kconfig("Kconfiglib/tests/empty")
+    verify(c.defconfig_filename is None,
+           "defconfig_filename should be None with no defconfig_list symbol")
+
+    c = Kconfig("Kconfiglib/tests/Kdefconfig_nonexistent")
+    verify(c.defconfig_filename is None,
+           "defconfig_filename should be None when none of the files in the "
+           "defconfig_list symbol exist")
+
+    # Referenced in Kdefconfig_existent(_but_n)
+    os.environ["FOO"] = "defconfig_2"
+
+    c = Kconfig("Kconfiglib/tests/Kdefconfig_existent_but_n")
+    verify(c.defconfig_filename is None,
+           "defconfig_filename should be None when the condition is n for all "
+           "the defaults")
+
+    c = Kconfig("Kconfiglib/tests/Kdefconfig_existent")
+    verify(c.defconfig_filename == "Kconfiglib/tests/defconfig_2",
+           "defconfig_filename should return the existing file "
+           "Kconfiglib/tests/defconfig_2")
+
+    # Should also look relative to $srctree if the specified defconfig is a
+    # relative path and can't be opened
+
+    c = Kconfig("Kconfiglib/tests/Kdefconfig_srctree")
+    verify(c.defconfig_filename == "Kconfiglib/tests/defconfig_2",
+           "defconfig_filename gave wrong file with $srctree unset")
+
+    os.environ["srctree"] = "Kconfiglib/tests"
+    c = Kconfig("Kdefconfig_srctree")
+    verify(c.defconfig_filename == "Kconfiglib/tests/sub/defconfig_in_sub",
+           "defconfig_filename gave wrong file with $srctree set")
+
+    os.environ.pop("srctree", None)
+
+
+    print("Testing mainmenu_text")
+
+    c = Kconfig("Kconfiglib/tests/empty")
+    verify(c.mainmenu_text == "Main menu",
+           "An empty Kconfig should get a default main menu prompt")
+
+    # Expanded in the mainmenu text
+    os.environ["FOO"] = "bar baz"
+    c = Kconfig("Kconfiglib/tests/Kmainmenu")
+    verify(c.mainmenu_text == "---bar baz---",
+           "Wrong mainmenu text")
+
+
+    print("Testing user_value")
+
+    # References undefined env. var. Disable warnings.
+    c = Kconfig("Kconfiglib/tests/Kmisc", warn=False)
+
+    # Avoid warnings from assigning invalid user values and assigning user
+    # values to symbols without prompts
+    c.warn = False
+
+    syms = [c.syms[name] for name in
+            ("BOOL", "TRISTATE", "STRING", "INT", "HEX")]
+
+    for sym in syms:
+        verify(sym.user_value is None,
+               "{} should not have a user value to begin with")
+
+    # Assign valid values for the types
+
+    assign_and_verify_user_value("BOOL", 0, 0, True)
+    assign_and_verify_user_value("BOOL", 2, 2, True)
+    assign_and_verify_user_value("TRISTATE", 0, 0, True)
+    assign_and_verify_user_value("TRISTATE", 1, 1, True)
+    assign_and_verify_user_value("TRISTATE", 2, 2, True)
+    assign_and_verify_user_value("STRING", "foo bar", "foo bar", True)
+    assign_and_verify_user_value("INT", "123", "123", True)
+    assign_and_verify_user_value("HEX", "0x123", "0x123", True)
+
+    # Assign invalid values for the types. They should retain their old user
+    # value.
+
+    assign_and_verify_user_value("BOOL", 1, 2, False)
+    assign_and_verify_user_value("BOOL", "foo", 2, False)
+    assign_and_verify_user_value("BOOL", "1", 2, False)
+    assign_and_verify_user_value("TRISTATE", "foo", 2, False)
+    assign_and_verify_user_value("TRISTATE", "1", 2, False)
+    assign_and_verify_user_value("STRING", 0, "foo bar", False)
+    assign_and_verify_user_value("INT", "foo", "123", False)
+    assign_and_verify_user_value("INT", 0, "123", False)
+    assign_and_verify_user_value("HEX", "foo", "0x123", False)
+    assign_and_verify_user_value("HEX", 0, "0x123", False)
+    assign_and_verify_user_value("HEX", "-0x1", "0x123", False)
+
+    for s in syms:
+        s.unset_value()
+        verify(s.user_value is None,
+               "{} should not have a user value after being reset".
+               format(s.name))
+
+
+    print("Testing is_menuconfig")
+
+    c = Kconfig("Kconfiglib/tests/Kmenuconfig")
+
+    for not_menuconfig in c.syms["NOT_MENUCONFIG_1"].nodes[0], \
+                          c.syms["NOT_MENUCONFIG_2"].nodes[0], \
+                          c.syms["MENUCONFIG_MULTI_DEF"].nodes[0], \
+                          c.syms["COMMENT_HOOK"].nodes[0].next:
+
+        verify(not not_menuconfig.is_menuconfig,
+               "'{}' should have is_menuconfig False".format(not_menuconfig))
+
+    for menuconfig in c.top_node, \
+                      c.syms["MENUCONFIG_1"].nodes[0], \
+                      c.syms["MENUCONFIG_MULTI_DEF"].nodes[1], \
+                      c.syms["MENU_HOOK"].nodes[0].next, \
+                      c.syms["CHOICE_HOOK"].nodes[0].next:
+
+        verify(menuconfig.is_menuconfig,
+               "'{}' should have is_menuconfig True".format(menuconfig))
+
+
+    print("Testing 'option env' semantics")
+
+    os.environ["ENV_VAR"] = "ENV_VAR value"
+
+    # References undefined env. var., so disable warnings
+    c = Kconfig("Kconfiglib/tests/Kmisc", warn=False)
+
+    # Verify that 'option env' is treated like a default
+    verify_value("FROM_ENV", "ENV_VAR value")
+    verify_value("FROM_ENV_MISSING", "missing")
+
+    verify_value("FROM_ENV_WEIRD", "weird")
+
+
+    print("Testing defined vs undefined symbols")
+
+    for name in "A", "B", "C", "D", "BOOL", "TRISTATE", "STRING", "INT", "HEX":
+        verify(c.syms[name].nodes,
+               "{} should be defined".format(name))
+
+    for name in "NOT_DEFINED_1", "NOT_DEFINED_2", "NOT_DEFINED_3", \
+                "NOT_DEFINED_4":
+        sym = c.syms[name]
+        verify(not c.syms[name].nodes,
+               "{} should not be defined".format(name))
+
+
+    print("Testing Symbol.choice")
+
+    for name in "A", "B", "C", "D":
+        verify(c.syms[name].choice is not None,
+               "{} should be a choice symbol".format(name))
+
+    for name in "Q1", "Q2", "Q3", "BOOL", "TRISTATE", "STRING", "INT", "HEX", \
+                "FROM_ENV", "FROM_ENV_MISSING", "NOT_DEFINED_1", \
+                "NOT_DEFINED_2", "NOT_DEFINED_3", "NOT_DEFINED_4":
+        verify(c.syms[name].choice is None,
+               "{} should not be a choice symbol".format(name))
+
+
+    print("Testing is_allnoconfig_y")
+
+    verify(not c.syms["NOT_ALLNOCONFIG_Y"].is_allnoconfig_y,
+           "NOT_ALLNOCONFIG_Y should not be allnoconfig_y")
+    verify(c.syms["ALLNOCONFIG_Y"].is_allnoconfig_y,
+           "ALLNOCONFIG_Y should be allnoconfig_y")
+
+
+    print("Testing .config reading and writing")
+
+    config_test_file = "Kconfiglib/tests/config_test"
+
+    def verify_file_contents(fname, contents):
+        with open(fname, "r") as f:
+            file_contents = f.read()
+            verify(file_contents == contents,
+                   "{} contains '{}'. Expected '{}'."
+                   .format(fname, file_contents, contents))
+
+    # Writing/reading strings with characters that need to be escaped
+
+    c = Kconfig("Kconfiglib/tests/Kescape")
+
+    # Test the default value
+    c.write_config(config_test_file + "_from_def")
+    verify_file_contents(config_test_file + "_from_def",
+                         r'''CONFIG_STRING="\"\\"''' "\n")
+    # Write our own value
+    c.syms["STRING"].set_value(r'''\"a'\\''')
+    c.write_config(config_test_file + "_from_user")
+    verify_file_contents(config_test_file + "_from_user",
+                         r'''CONFIG_STRING="\\\"a'\\\\"''' "\n")
+
+    # Read back the two configs and verify the respective values
+    c.load_config(config_test_file + "_from_def")
+    verify_value("STRING", '"\\')
+    c.load_config(config_test_file + "_from_user")
+    verify_value("STRING", r'''\"a'\\''')
+
+    # Appending values from a .config
+
+    c = Kconfig("Kconfiglib/tests/Kappend")
+
+    # Values before assigning
+    verify_value("BOOL", "n")
+    verify_value("STRING", "")
+
+    # Assign BOOL
+    c.load_config("Kconfiglib/tests/config_set_bool", replace=False)
+    verify_value("BOOL", "y")
+    verify_value("STRING", "")
+
+    # Assign STRING
+    c.load_config("Kconfiglib/tests/config_set_string", replace=False)
+    verify_value("BOOL", "y")
+    verify_value("STRING", "foo bar")
+
+    # Reset BOOL
+    c.load_config("Kconfiglib/tests/config_set_string")
+    verify_value("BOOL", "n")
+    verify_value("STRING", "foo bar")
+
+    # Loading a completely empty .config should reset values
+    c.load_config("Kconfiglib/tests/empty")
+    verify_value("STRING", "")
+
+    # An indented assignment in a .config should be ignored
+    c.load_config("Kconfiglib/tests/config_indented")
+    verify_value("IGNOREME", "y")
+
+    # Symbol order in headers and minimal configuration files should match
+    # definition order, like in .config files
+
+    c = Kconfig("Kconfiglib/tests/Korder")
+
+    c.write_autoconf(config_test_file)
+    verify_file_contents(config_test_file, """
+#define CONFIG_O 0
+#define CONFIG_R 1
+#define CONFIG_D 2
+#define CONFIG_E 3
+#define CONFIG_R2 4
+#define CONFIG_I 5
+#define CONFIG_N 6
+#define CONFIG_G 7
+"""[1:])
+
+    # Differs from defaults
+    c.syms["O"].set_value("-1")
+    c.syms["R"].set_value("-1")
+    c.syms["E"].set_value("-1")
+    c.syms["R2"].set_value("-1")
+    c.syms["N"].set_value("-1")
+    c.syms["G"].set_value("-1")
+    c.write_min_config(config_test_file)
+    verify_file_contents(config_test_file, """
+CONFIG_O=-1
+CONFIG_R=-1
+CONFIG_E=-1
+CONFIG_R2=-1
+CONFIG_N=-1
+CONFIG_G=-1
+"""[1:])
+
+    # Test header strings in configuration files and headers
+
+    os.environ["KCONFIG_CONFIG_HEADER"] = "config header from env.\n"
+    os.environ["KCONFIG_AUTOHEADER_HEADER"] = "header header from env.\n"
+
+    c = Kconfig("Kconfiglib/tests/Kheader")
+    c.write_config(config_test_file, header="config header from param\n")
+    verify_file_contents(config_test_file, """\
+config header from param
+CONFIG_FOO=y
+""")
+    c.write_min_config(config_test_file, header="min. config header from param\n")
+    verify_file_contents(config_test_file, """\
+min. config header from param
+""")
+    c.write_config(config_test_file)
+    verify_file_contents(config_test_file, """\
+config header from env.
+CONFIG_FOO=y
+""")
+    c.write_min_config(config_test_file)
+    verify_file_contents(config_test_file, """\
+config header from env.
+""")
+    c.write_autoconf(config_test_file, header="header header from param\n")
+    verify_file_contents(config_test_file, """\
+header header from param
+#define CONFIG_FOO 1
+""")
+    c.write_autoconf(config_test_file)
+    verify_file_contents(config_test_file, """\
+header header from env.
+#define CONFIG_FOO 1
+""")
+
+    del os.environ["KCONFIG_CONFIG_HEADER"]
+    del os.environ["KCONFIG_AUTOHEADER_HEADER"]
+
+
+    print("Testing Kconfig fetching and separation")
+
+    for c in Kconfig("Kconfiglib/tests/Kmisc", warn=False), \
+             Kconfig("Kconfiglib/tests/Kmisc", warn=False):
+        for item in c.syms["BOOL"], \
+                    c.syms["BOOL"].nodes[0], \
+                    c.named_choices["OPTIONAL"], \
+                    c.named_choices["OPTIONAL"].nodes[0], \
+                    c.syms["MENU_HOOK"].nodes[0].next, \
+                    c.syms["COMMENT_HOOK"].nodes[0].next:
+            verify(item.kconfig is c,
+                   ".kconfig not properly set for " + repr(item))
+
+
+    print("Testing imply semantics")
+
+    c = Kconfig("Kconfiglib/tests/Kimply")
+
+    verify_value("IMPLY_DIRECT_DEPS", "y")
+    verify_value("UNMET_DIRECT_1", "n")
+    verify_value("UNMET_DIRECT_2", "n")
+    verify_value("UNMET_DIRECT_3", "n")
+    verify_value("MET_DIRECT_1", "y")
+    verify_value("MET_DIRECT_2", "y")
+    verify_value("MET_DIRECT_3", "y")
+    verify_value("MET_DIRECT_4", "y")
+
+    verify_value("IMPLY_COND", "y")
+    verify_value("IMPLIED_N_COND", "n")
+    verify_value("IMPLIED_M_COND", "m")
+    verify_value("IMPLIED_Y_COND", "y")
+
+    verify_value("IMPLY_N_1", "n")
+    verify_value("IMPLY_N_2", "n")
+    verify_value("IMPLIED_FROM_N_1", "n")
+    verify_value("IMPLIED_FROM_N_2", "n")
+
+    verify_value("IMPLY_M", "m")
+    verify_value("IMPLIED_M", "m")
+    verify_value("IMPLIED_M_BOOL", "y")
+
+    verify_value("IMPLY_M_TO_Y", "y")
+    verify_value("IMPLIED_M_TO_Y", "y")
+
+    # Test user value semantics
+
+    # Verify that IMPLIED_TRISTATE is invalidated if the direct
+    # dependencies change
+
+    assign_and_verify("IMPLY", 2)
+    assign_and_verify("DIRECT_DEP", 2)
+    verify_value("IMPLIED_TRISTATE", 2)
+    assign_and_verify("DIRECT_DEP", 0)
+    verify_value("IMPLIED_TRISTATE", 0)
+    # Set back for later tests
+    assign_and_verify("DIRECT_DEP", 2)
+
+    # Verify that IMPLIED_TRISTATE can be set to anything when IMPLY has value
+    # n, and that it gets the value n by default (for non-imply-related
+    # reasons)
+
+    assign_and_verify("IMPLY", 0)
+    assign_and_verify("IMPLIED_TRISTATE", 0)
+    assign_and_verify("IMPLIED_TRISTATE", 1)
+    assign_and_verify("IMPLIED_TRISTATE", 2)
+    c.syms["IMPLIED_TRISTATE"].unset_value()
+    verify_value("IMPLIED_TRISTATE", "n")
+
+    # Same as above for m. Anything still goes, but m by default now.
+
+    assign_and_verify("IMPLY", 1)
+    assign_and_verify("IMPLIED_TRISTATE", 0)
+    assign_and_verify("IMPLIED_TRISTATE", 1)
+    assign_and_verify("IMPLIED_TRISTATE", 2)
+    c.syms["IMPLIED_TRISTATE"].unset_value()
+    verify_value("IMPLIED_TRISTATE", 1)
+
+    # Same as above for y. Only n and y should be accepted. m gets promoted to
+    # y. Default should be y.
+
+    assign_and_verify("IMPLY", 2)
+    assign_and_verify("IMPLIED_TRISTATE", 0)
+    assign_and_verify_value("IMPLIED_TRISTATE", 1, 2)
+    assign_and_verify("IMPLIED_TRISTATE", 2)
+    c.syms["IMPLIED_TRISTATE"].unset_value()
+    verify_value("IMPLIED_TRISTATE", 2)
+
+    # Being implied to either m or y should give a bool the value y
+
+    c.syms["IMPLY"].unset_value()
+    verify_value("IMPLIED_BOOL", 0)
+    assign_and_verify("IMPLY", 0)
+    verify_value("IMPLIED_BOOL", 0)
+    assign_and_verify("IMPLY", 1)
+    verify_value("IMPLIED_BOOL", 2)
+    assign_and_verify("IMPLY", 2)
+    verify_value("IMPLIED_BOOL", 2)
+
+    # A bool implied to m or y can take the values n and y
+
+    c.syms["IMPLY"].set_value(1)
+    assign_and_verify("IMPLIED_BOOL", 0)
+    assign_and_verify("IMPLIED_BOOL", 2)
+
+    c.syms["IMPLY"].set_value(2)
+    assign_and_verify("IMPLIED_BOOL", 0)
+    assign_and_verify("IMPLIED_BOOL", 2)
+
+
+    print("Testing choice semantics")
+
+    # Would warn for choice value symbols defined without a type, even
+    # though the type is automatically derived. This is probably more
+    # helpful than ignoring those cases, as this feature isn't used
+    # deliberately anywhere from what I've seen.
+    c = Kconfig("Kconfiglib/tests/Kchoice", warn=False)
+
+    for name in "BOOL", "BOOL_OPT", "BOOL_M", "DEFAULTS":
+        verify(c.named_choices[name].orig_type == BOOL,
+               "choice {} should have type bool".format(name))
+
+    for name in "TRISTATE", "TRISTATE_OPT", "TRISTATE_M":
+        verify(c.named_choices[name].orig_type == TRISTATE,
+               "choice {} should have type tristate".format(name))
+
+    def select_and_verify(sym):
+        choice = sym.nodes[0].parent.item
+        choice.set_value(2)
+
+        sym.set_value(2)
+
+        verify(sym.choice.selection is sym,
+               sym.name + " should be the selected symbol")
+
+        verify(choice.user_selection is sym,
+               sym.name + " should be the user selection of the choice")
+
+        verify(sym.tri_value == 2,
+               sym.name + " should have value y when selected")
+
+        verify(sym.user_value == 2,
+               sym.name + " should have user value y when selected")
+
+        for sibling in choice.syms:
+            if sibling is not sym:
+                verify(sibling.tri_value == 0,
+                       sibling.name + " should be n when not selected")
+
+    def select_and_verify_all(choice_name):
+        choice = c.named_choices[choice_name]
+
+        # Select in forward order
+        for sym in choice.syms:
+            select_and_verify(sym)
+
+        # Select in reverse order
+        for sym in reversed(choice.syms):
+            select_and_verify(sym)
+
+    def verify_mode(choice_name, no_modules_mode, modules_mode):
+        choice = c.named_choices[choice_name]
+
+        c.modules.set_value(0)
+        verify(choice.tri_value == no_modules_mode,
+               'Wrong mode for choice {} with no modules. Expected {}, got {}.'
+               .format(choice.name, no_modules_mode, choice.tri_value))
+
+        c.modules.set_value(2)
+        verify(choice.tri_value == modules_mode,
+               'Wrong mode for choice {} with modules. Expected {}, got {}.'
+               .format(choice.name, modules_mode, choice.tri_value))
+
+    verify_mode("BOOL",         2, 2)
+    verify_mode("BOOL_OPT",     0, 0)
+    verify_mode("TRISTATE",     2, 1)
+    verify_mode("TRISTATE_OPT", 0, 0)
+    verify_mode("BOOL_M",       0, 2)
+    verify_mode("TRISTATE_M",   0, 1)
+
+    # Test defaults
+
+    choice = c.named_choices["DEFAULTS"]
+
+    c.syms["TRISTATE_SYM"].set_value(0)
+    verify(choice.selection is c.syms["OPT_4"],
+           "Wrong choice default with TRISTATE_SYM = n")
+
+    c.syms["TRISTATE_SYM"].set_value(2)
+    verify(choice.selection is c.syms["OPT_2"],
+           "Wrong choice default with TRISTATE_SYM = y")
+
+    c.syms["OPT_1"].set_value(2)
+    verify(choice.selection is c.syms["OPT_1"],
+           "User selection should override defaults")
+
+    verify(c.named_choices["DEFAULTS_NOT_VISIBLE"].selection
+           is c.syms["OPT_8"],
+           "Non-visible choice symbols should cause the next default to be "
+           "considered")
+
+    # Test y mode selection
+
+    c.modules.set_value(2)
+
+    select_and_verify_all("BOOL")
+    select_and_verify_all("BOOL_OPT")
+    select_and_verify_all("TRISTATE")
+    select_and_verify_all("TRISTATE_OPT")
+    # For BOOL_M, the mode should have been promoted
+    select_and_verify_all("BOOL_M")
+
+    # Test m mode selection
+
+    c.named_choices["TRISTATE"].set_value(1)
+
+    verify(c.named_choices["TRISTATE"].tri_value == 1,
+           "TRISTATE choice should have mode m after explicit mode assignment")
+
+    assign_and_verify_value("T_1", 0, 0)
+    assign_and_verify_value("T_2", 0, 0)
+    assign_and_verify_value("T_1", 1, 1)
+    assign_and_verify_value("T_2", 1, 1)
+    assign_and_verify_value("T_1", 2, 1)
+    assign_and_verify_value("T_2", 2, 1)
+
+    # Switching to y mode should cause T_2 to become selected
+    c.named_choices["TRISTATE"].set_value(2)
+    verify_value("T_1", 0)
+    verify_value("T_2", 2)
+
+    # Verify that choices with no explicitly specified type get the type of the
+    # first contained symbol with a type
+
+    verify(c.named_choices["NO_TYPE_BOOL"].orig_type == BOOL,
+           "Expected first choice without explicit type to have type bool")
+
+    verify(c.named_choices["NO_TYPE_TRISTATE"].orig_type == TRISTATE,
+           "Expected second choice without explicit type to have type "
+           "tristate")
+
+    # Verify that symbols without a type in the choice get the type of the
+    # choice
+
+    for name in "MMT_1", "MMT_2", "MMT_4", "MMT_5":
+        verify(c.syms[name].orig_type == BOOL,
+               "Expected {} to get type bool".format(name))
+
+    verify(c.syms["MMT_3"].orig_type == TRISTATE,
+           "Expected MMT_3 to have type tristate")
+
+    # Verify that the default selection can change depending on the
+    # visibility of the choice symbols
+
+    default_with_dep_choice = c.named_choices["DEFAULT_WITH_DEP"]
+
+    verify(default_with_dep_choice.selection is c.syms["B"],
+           "Wrong choice default with unsatisfied deps on default")
+
+    c.syms["DEP"].set_value("y")
+
+    verify(default_with_dep_choice.selection is c.syms["A"],
+           "Wrong choice default with satisfied deps on default")
+
+    c.syms["DEP"].set_value("n")
+
+    verify(default_with_dep_choice.selection is c.syms["B"],
+           "Wrong choice default with unsatisfied deps on default (round two)")
+
+    # Verify that symbols in choices that depend on the preceding symbol aren't
+    # considered choice symbols
+
+    weird_choice = c.named_choices["WEIRD_SYMS"]
+
+    def verify_is_normal_choice_symbol(name):
+        sym = c.syms[name]
+        verify(sym.choice is not None and
+               sym in weird_choice.syms and
+               sym.nodes[0].parent.item is weird_choice,
+               "{} should be a normal choice symbol".format(sym.name))
+
+    def verify_is_weird_choice_symbol(name):
+        sym = c.syms[name]
+        verify(sym.choice is None and
+               sym not in weird_choice.syms,
+               "{} should be a weird (non-)choice symbol"
+               .format(sym.name))
+
+    verify_is_normal_choice_symbol("WS1")
+    verify_is_weird_choice_symbol("WS2")
+    verify_is_weird_choice_symbol("WS3")
+    verify_is_weird_choice_symbol("WS4")
+    verify_is_weird_choice_symbol("WS5")
+    verify_is_normal_choice_symbol("WS6")
+    verify_is_weird_choice_symbol("WS7")
+    verify_is_weird_choice_symbol("WS8")
+    verify_is_normal_choice_symbol("WS9")
+
+
+    print("Testing 'if' node removal")
+
+    c = Kconfig("Kconfiglib/tests/Kifremoval", warn=False)
+
+    nodes = tuple(c.node_iter())
+    verify_equal(nodes[0].item.name, "A")
+    verify_equal(nodes[1].item.name, "B")
+    verify_equal(nodes[2].item.name, "C")
+    verify_equal(nodes[3].item.name, "D")
+    verify_equal(nodes[4].prompt[0], "E")
+    verify_equal(nodes[5].prompt[0], "F")
+    verify_equal(nodes[6].prompt[0], "G")
+    verify_equal(nodes[7].item.name, "H")
+    verify_equal(nodes[8].item.name, "I")
+    verify_equal(nodes[9].item.name, "J")
+    verify(len(nodes) == 10,
+           "Wrong number of nodes after 'if' removal")
+
+
+    print("Testing multi.def. property copying")
+
+    c = Kconfig("Kconfiglib/tests/Kdepcopy", warn=False)
+
+    def verify_props(desc, props, prop_names):
+        actual = [prop[0].name for prop in props]
+        expected = prop_names.split()
+
+        verify(actual == expected,
+               "Wrong {} properties, expected '{}', got '{}'"
+               .format(desc, expected, actual))
+
+    verify_props("default", c.syms["MULTIDEF"].defaults,
+                 "A B C D E F G H I J K L M N O P Q R")
+
+    verify_props("select", c.syms["MULTIDEF"].selects,
+                 "AA BB CC DD EE FF GG HH II JJ")
+
+    verify_props("imply", c.syms["MULTIDEF"].selects,
+                 "AA BB CC DD EE FF GG HH II JJ")
+
+    verify_props("select", c.syms["MULTIDEF_CHOICE"].selects,
+                 "A B C")
+
+    verify_props("range", c.syms["MULTIDEF_RANGE"].ranges,
+                 "A B C D E F")
+
+    verify_props("default", c.choices[1].defaults,
+                 "A B C D E")
+
+
+    print("Testing dependency loop detection")
+
+    # These are all expected to raise dependency loop errors
+    for i in range(11):
+        filename = "Kconfiglib/tests/Kdeploop" + str(i)
+        try:
+            Kconfig(filename)
+        except KconfigError as e:
+            if "Dependency loop" not in str(e):
+                fail("dependency loop in {} raised wrong KconfigError"
+                     .format(filename))
+        except:
+            fail("dependency loop in {} raised wrong exception"
+                 .format(filename))
+        else:
+            fail("dependency loop in {} not detected".format(filename))
+
+    # Check the most complicated message completely
+    try:
+        Kconfig("Kconfiglib/tests/Kdeploop10")
+    except KconfigError as e:
+        verify_equal(str(e), """
+Dependency loop
+===============
+
+A (defined at Kconfiglib/tests/Kdeploop10:1), with definition...
+
+config A
+	bool
+	depends on B
+
+...depends on B (defined at Kconfiglib/tests/Kdeploop10:5), with definition...
+
+config B
+	bool
+	depends on C = 7
+
+...depends on C (defined at Kconfiglib/tests/Kdeploop10:9), with definition...
+
+config C
+	int
+	range D 8
+
+...depends on D (defined at Kconfiglib/tests/Kdeploop10:13), with definition...
+
+config D
+	int
+	default 3 if E
+	default 8
+
+...depends on E (defined at Kconfiglib/tests/Kdeploop10:18), with definition...
+
+config E
+	bool
+
+(select-related dependencies: F && G)
+
+...depends on G (defined at Kconfiglib/tests/Kdeploop10:25), with definition...
+
+config G
+	bool
+	depends on H
+
+...depends on the choice symbol H (defined at Kconfiglib/tests/Kdeploop10:32), with definition...
+
+config H
+	bool "H"
+	depends on I && <choice>
+
+...depends on the choice symbol I (defined at Kconfiglib/tests/Kdeploop10:41), with definition...
+
+config I
+	bool "I"
+	depends on <choice>
+
+...depends on <choice> (defined at Kconfiglib/tests/Kdeploop10:38), with definition...
+
+choice
+	bool "choice" if J
+
+...depends on J (defined at Kconfiglib/tests/Kdeploop10:46), with definition...
+
+config J
+	bool
+	depends on A
+
+...depends again on A (defined at Kconfiglib/tests/Kdeploop10:1)
+"""[:-1])
+    except:
+        fail("Loop detection message check raised wrong exception")
+    else:
+        fail("Loop detection message check did not raise exception")
+
+
+    print("Testing preprocessor")
+
+    os.environ["ENV_1"] = "env_1"
+    os.environ["ENV_2"] = "env_2"
+    os.environ["ENV_3"] = "env_3"
+    os.environ["ENV_4"] = "env_4"
+    os.environ["ENV_5"] = "n"
+    os.environ["ENV_6"] = "Kconfiglib/tests/empty"
+    os.environ["ENV_7"] = "env_7"
+    # We verify warnings manually
+    c = Kconfig("Kconfiglib/tests/Kpreprocess", warn_to_stderr=False)
+
+    def verify_variable(name, unexp_value, exp_value, recursive, *args):
+        var = c.variables[name]
+
+        verify(var.value == unexp_value,
+               "expected variable '{}' to have the unexpanded value '{}', had "
+               "the value '{}'".format(name, unexp_value, var.value))
+
+        if not args:
+            verify(var.expanded_value == exp_value,
+                   "expected expanded_value for {} to be '{}', was '{}'"
+                   .format(name, exp_value, var.expanded_value))
+
+        verify(var.expanded_value_w_args(*args) == exp_value,
+               "expected expanded_value_w_args() for '{}' to be '{}', was '{}'"
+               .format(name, exp_value, var.expanded_value_w_args(*args)))
+
+        verify(var.is_recursive == recursive,
+               "{} was {}, shouldn't be"
+               .format(name, "recursive" if var.is_recursive else "simple"))
+
+    verify_variable("simple-recursive", "foo", "foo", True)
+    verify_variable("simple-immediate", "bar", "bar", False)
+    verify_variable("simple-recursive-2", "baz", "baz", True)
+
+    verify_variable("whitespaced", "foo", "foo", True)
+
+    verify_variable("preserve-recursive", "foo bar", "foo bar", True)
+    verify_variable("preserve-immediate", "foo bar", "foo bar", False)
+
+    verify_variable("recursive",
+                    "$(foo) $(bar) $($(b-char)a$(z-char)) $(indir)",
+                    "abc def ghi jkl mno",
+                    True)
+
+    verify_variable("immediate", "foofoo", "foofoo", False)
+
+    verify_variable("messy-fn-res",
+                    "$($(fn-indir)-unused-arg, a  b (,) , c  d )",
+                    'surround-rev-quote " c  d " " a  b (,) " surround-rev-quote ',
+                    True)
+
+    verify_variable("special-chars-fn-res",
+                    "$(fn,$(comma)$(dollar)$(left-paren)foo$(right-paren))",
+                    '",$(foo)"',
+                    True)
+
+    verify_variable("quote", '"$(1)" "$(2)"', '"" ""', True)
+    verify_variable("quote", '"$(1)" "$(2)"', '"one" ""', True,
+                    "one")
+    verify_variable("quote", '"$(1)" "$(2)"', '"one" "two"', True,
+                    "one", "two")
+    verify_variable("quote", '"$(1)" "$(2)"', '"one" "two"', True,
+                    "one", "two", "three")
+
+    verify_str(c.syms["PRINT_ME"], r"""
+config PRINT_ME
+	string "env_1" if (FOO && BAR) || !BAZ || !QAZ
+	default "\"foo\"" if "foo \"bar\" baz" = ""
+""")
+
+    verify_str(c.syms["PRINT_ME_TOO"], r"""
+config PRINT_ME_TOO
+	bool "foo"
+	default FOOBARBAZQAZ if QAZ && QAZFOO && xxx
+""")
+
+    def verify_repr(name, s):
+        verify_equal(repr(c.variables[name]), s)
+
+    verify_repr(
+        "simple-immediate",
+        "<variable simple-immediate, immediate, value 'bar'>")
+
+    verify_repr(
+        "messy-fn-res",
+        "<variable messy-fn-res, recursive, value '$($(fn-indir)-unused-arg, a  b (,) , c  d )'>")
+
+    def verify_recursive(name):
+        try:
+            c.variables[name].expanded_value
+        except KconfigError:
+            pass
+        else:
+            fail("Expected '{}' expansion to flag recursive expansion, didn't"
+                 .format(name))
+
+    verify_recursive("rec-1")
+    # Indirectly verifies that it's not recursive
+    verify_variable("safe-fn-rec-res",
+                    "$(safe-fn-rec,safe-fn-rec-2)",
+                    "foo",
+                    True)
+    verify_recursive("unsafe-fn-rec")
+
+    verify_variable("foo-bar-baz", "$(rhs)", "value", True)
+
+    verify_variable("space-var-res", "$(foo bar)", "value", True)
+
+    verify_variable("shell-res",
+                    "$(shell,false && echo foo bar || echo baz qaz)",
+                    "baz qaz",
+                    True)
+
+    verify_variable("shell-stderr-res", "", "", False)
+
+    verify_variable("parens-res",
+                    "pre-$(shell,echo '(a,$(b-char),(c,d),e)')-post",
+                    "pre-(a,b,(c,d),e)-post",
+                    True)
+
+    verify_variable("location-res",
+                    "Kconfiglib/tests/Kpreprocess:129",
+                    "Kconfiglib/tests/Kpreprocess:129",
+                    False)
+
+    verify_variable("warning-res", "", "", False)
+    verify_variable("error-n-res", "", "", False)
+
+    try:
+        c.variables["error-y-res"].expanded_value
+    except KconfigError:
+        pass
+    else:
+        fail("expanding error-y-res didn't raise an exception")
+
+    # Check Kconfig.env_vars
+    verify_equal(c.env_vars,
+                 set(("ENV_1", "ENV_2", "ENV_3", "ENV_4", "ENV_5", "ENV_6")))
+
+    # Check that the expected warnings were generated
+    verify_equal(c.warnings, [
+        "Kconfiglib/tests/Kpreprocess:122: warning: 'echo message on stderr >&2' wrote to stderr: message on stderr",
+        "Kconfiglib/tests/Kpreprocess:134: warning: a warning"
+    ])
+
+
+    print("Testing user-defined preprocessor functions")
+
+    # Make Kconfiglib/tests/kconfigfunctions.py importable
+    sys.path.insert(0, "Kconfiglib/tests")
+
+    c = Kconfig("Kconfiglib/tests/Kuserfunctions")
+
+    verify_variable("add-zero",  "$(add)",          "0", True)
+    verify_variable("add-one",   "$(add,1)",        "1", True)
+    verify_variable("add-three", "$(add,1,-1,2,1)", "3", True)
+
+    verify_variable("one-one", "$(one,foo bar)", "onefoo barfoo bar", True)
+
+    verify_variable("one-or-more-one", "$(one-or-more,foo)", "foo + ", True)
+    verify_variable("one-or-more-three", "$(one-or-more,foo,bar,baz)",
+                    "foo + bar,baz", True)
+
+    verify_variable("location-1", "Kconfiglib/tests/Kuserfunctions:13",
+                    "Kconfiglib/tests/Kuserfunctions:13", False)
+    verify_variable("location-2", "Kconfiglib/tests/Kuserfunctions:14",
+                    "Kconfiglib/tests/Kuserfunctions:14", False)
+
+    def verify_bad_argno(name):
+        try:
+            c.variables[name].expanded_value
+        except KconfigError:
+            pass
+        else:
+            fail("Expected '{}' expansion to flag wrong number of arguments, "
+                 "didn't".format(name))
+
+    verify_bad_argno("one-zero")
+    verify_bad_argno("one-two")
+    verify_bad_argno("one-or-more-zero")
+
+    sys.path.pop(0)
+
+    # This test can fail on older Python 3.x versions, because they don't
+    # preserve dict insertion order during iteration. The output is still
+    # correct, just different.
+    if not (3, 0) <= sys.version_info <= (3, 5):
+        print("Testing KCONFIG_WARN_UNDEF")
+
+        os.environ["KCONFIG_WARN_UNDEF"] = "y"
+        c = Kconfig("Kconfiglib/tests/Kundef", warn_to_stderr=False)
+
+        verify_equal("\n".join(c.warnings), """
+warning: the int symbol INT (defined at Kconfiglib/tests/Kundef:8) has a non-int range [UNDEF_2 (undefined), 8 (undefined)]
+warning: undefined symbol UNDEF_1:
+
+- Referenced at Kconfiglib/tests/Kundef:4:
+
+config BOOL
+	bool "foo" if DEF || !UNDEF_1
+	default UNDEF_2
+
+- Referenced at Kconfiglib/tests/Kundef:19:
+
+menu "menu"
+	depends on UNDEF_1
+	visible if UNDEF_3
+warning: undefined symbol UNDEF_2:
+
+- Referenced at Kconfiglib/tests/Kundef:4:
+
+config BOOL
+	bool "foo" if DEF || !UNDEF_1
+	default UNDEF_2
+
+- Referenced at Kconfiglib/tests/Kundef:8:
+
+config INT
+	int
+	range UNDEF_2 8
+	range 5 15
+	default 10
+warning: undefined symbol UNDEF_3:
+
+- Referenced at Kconfiglib/tests/Kundef:19:
+
+menu "menu"
+	depends on UNDEF_1
+	visible if UNDEF_3
+"""[1:-1])
+
+        os.environ.pop("KCONFIG_WARN_UNDEF")
+
+
+    print("\nAll selftests passed\n" if all_passed else
+          "\nSome selftests failed\n")
+
+
+def run_compatibility_tests():
+    # Runs tests on configurations from the kernel. Tests compability with the
+    # C implementation by comparing outputs.
+
+    # Referenced inside the kernel Kconfig files.
+    #
+    # The str() makes the type of the value 'str' on both Python 2 and Python 3,
+    # which is nice for some later dictionary key sanity checks.
+
+    os.environ["KERNELVERSION"] = str(
+        subprocess.check_output("make kernelversion", shell=True)
+            .decode("utf-8").rstrip()
+    )
+
+    os.environ["CC_VERSION_TEXT"] = str(
+        subprocess.check_output("gcc --version | head -n1", shell=True)
+            .decode("utf-8").rstrip()
+    )
+
+    os.environ["srctree"] = "."
+    os.environ["CC"] = "gcc"
+    os.environ["LD"] = "ld"
+
+
+    if not os.path.exists("scripts/kconfig/conf"):
+        print("\nscripts/kconfig/conf does not exist -- running "
+              "'make allnoconfig' to build it...")
+        shell("make allnoconfig")
+
+
+    print("Running compatibility tests...\n")
+
+    test_fns = (test_defconfig,
+                # Fails for a few defconfigs due to a bug in the C tools. Will
+                # be enabled once patches get in.
+                #test_min_config,
+                test_alldefconfig,
+                test_allnoconfig,
+                test_allnoconfig_walk,
+                test_allmodconfig,
+                test_allyesconfig,
+                test_sanity)
+
+    for test_fn in test_fns:
+        # The test description is taken from the docstring of the corresponding
+        # function
+        print(textwrap.dedent(test_fn.__doc__))
+
+        for arch, srcarch in all_arch_srcarch():
+            # Referenced inside the Kconfig files
+            os.environ["ARCH"] = arch
+            os.environ["SRCARCH"] = srcarch
+
+            rm_configs()
+
+            test_fn(arch, srcarch)
+
+    if all_passed:
+        print("All selftests and compatibility tests passed")
+    else:
+        sys.exit("Some tests failed")
+
+
+def all_arch_srcarch():
+    for srcarch in os.listdir("arch"):
+        # arc and h8300 are currently broken with the C tools on linux-next as
+        # well. Perhaps they require cross-compilers to be installed.
+        #
+        # User-mode Linux has an unorthodox Kconfig setup that would require a
+        # different testing setup. Skip it too.
+        if srcarch in ("arc", "h8300", "um"):
+            continue
+
+        if os.path.exists(os.path.join("arch", srcarch, "Kconfig")):
+            yield (srcarch, srcarch)
+
+    # Some arches define additional ARCH settings with ARCH != SRCARCH
+    # (search for "Additional ARCH settings for" in the top-level Makefile)
+
+    yield ("i386", "x86")
+    yield ("x86_64", "x86")
+
+    yield ("sparc32", "sparc")
+    yield ("sparc64", "sparc")
+
+    yield ("sh64", "sh")
+
+
+def test_allnoconfig(arch, srcarch):
+    """
+    Verify that allnoconfig.py generates the same .config as
+    'make allnoconfig', for each architecture. Runs the script via
+    'make scriptconfig'.
+    """
+    shell("make scriptconfig SCRIPT=Kconfiglib/allnoconfig.py "
+          "PYTHONCMD='{}'".format(sys.executable))
+    shell("mv .config ._config")
+    shell("scripts/kconfig/conf --allnoconfig Kconfig")
+
+    compare_configs(arch)
+
+
+def test_allnoconfig_walk(arch, srcarch):
+    """
+    Verify that examples/allnoconfig_walk.py generates the same .config as
+    'make allnoconfig', for each architecture. Runs the script via
+    'make scriptconfig'.
+    """
+    shell("make scriptconfig SCRIPT=Kconfiglib/examples/allnoconfig_walk.py "
+          "PYTHONCMD='{}'".format(sys.executable))
+    shell("mv .config ._config")
+    shell("scripts/kconfig/conf --allnoconfig Kconfig")
+
+    compare_configs(arch)
+
+
+def test_allmodconfig(arch, srcarch):
+    """
+    Verify that allmodconfig.py generates the same .config as
+    'make allmodconfig', for each architecture. Runs the script via
+    'make scriptconfig'.
+    """
+    shell("make scriptconfig SCRIPT=Kconfiglib/allmodconfig.py "
+          "PYTHONCMD='{}'".format(sys.executable))
+    shell("mv .config ._config")
+    shell("scripts/kconfig/conf --allmodconfig Kconfig")
+
+    compare_configs(arch)
+
+
+def test_allyesconfig(arch, srcarch):
+    """
+    Verify that allyesconfig.py generates the same .config as
+    'make allyesconfig', for each architecture. Runs the script via
+    'make scriptconfig'.
+    """
+    shell("make scriptconfig SCRIPT=Kconfiglib/allyesconfig.py "
+          "PYTHONCMD='{}'".format(sys.executable))
+    shell("mv .config ._config")
+    shell("scripts/kconfig/conf --allyesconfig Kconfig")
+
+    compare_configs(arch)
+
+
+def test_sanity(arch, srcarch):
+    """
+    Do sanity checks on each configuration and call all public methods on all
+    symbols, choices, and menu nodes for all architectures to make sure we
+    never crash or hang.
+    """
+    print("For {}...".format(arch))
+
+    kconf = Kconfig()
+
+    for sym in kconf.defined_syms:
+        verify(sym._visited == 2,
+               "{} has broken dependency loop detection (_visited = {})"
+               .format(sym.name, sym._visited))
+
+    kconf.modules
+    kconf.defconfig_list
+    kconf.defconfig_filename
+
+    # Legacy warning functions
+    kconf.enable_redun_warnings()
+    kconf.disable_redun_warnings()
+    kconf.enable_undef_warnings()
+    kconf.disable_undef_warnings()
+    kconf.enable_warnings()
+    kconf.disable_warnings()
+    kconf.enable_stderr_warnings()
+    kconf.disable_stderr_warnings()
+
+    kconf.mainmenu_text
+    kconf.unset_values()
+
+    kconf.write_autoconf("/dev/null")
+
+    # No tempfile.TemporaryDirectory in Python 2
+    tmpdir = tempfile.mkdtemp()
+    kconf.sync_deps(os.path.join(tmpdir, "deps"))  # Create
+    kconf.sync_deps(os.path.join(tmpdir, "deps"))  # Update
+    shutil.rmtree(tmpdir)
+
+    # Python 2/3 compatible
+    for key, sym in kconf.syms.items():
+        verify(isinstance(key, str), "weird key '{}' in syms dict".format(key))
+
+        verify(not sym.is_constant, sym.name + " in 'syms' and constant")
+
+        verify(sym not in kconf.const_syms,
+               sym.name + " in both 'syms' and 'const_syms'")
+
+        for dep in sym._dependents:
+            verify(not dep.is_constant,
+                   "the constant symbol {} depends on {}"
+                   .format(dep.name, sym.name))
+
+        sym.__repr__()
+        sym.__str__()
+        sym.assignable
+        kconf.disable_warnings()
+        sym.set_value(2)
+        sym.set_value("foo")
+        sym.unset_value()
+        kconf.enable_warnings()  # Legacy warning function
+        sym.str_value
+        sym.tri_value
+        sym.type
+        sym.user_value
+        sym.visibility
+
+    for sym in kconf.defined_syms:
+        verify(sym.nodes, sym.name + " is defined but lacks menu nodes")
+
+        verify(not (sym.orig_type not in (BOOL, TRISTATE) and sym.choice),
+               sym.name + " is a choice symbol but not bool/tristate")
+
+    for key, sym in kconf.const_syms.items():
+        verify(isinstance(key, str),
+               "weird key '{}' in const_syms dict".format(key))
+
+        verify(sym.is_constant,
+               '"{}" is in const_syms but not marked constant'
+               .format(sym.name))
+
+        verify(not sym.nodes,
+               '"{}" is constant but has menu nodes'.format(sym.name))
+
+        verify(not sym._dependents,
+               '"{}" is constant but is a dependency of some symbol'
+               .format(sym.name))
+
+        verify(not sym.choice,
+               '"{}" is constant and a choice symbol'.format(sym.name))
+
+        sym.__repr__()
+        sym.__str__()
+        sym.assignable
+        kconf.disable_warnings()
+        sym.set_value(2)
+        sym.set_value("foo")
+        sym.unset_value()
+        kconf.enable_warnings()  # Legacy warning function
+        sym.str_value
+        sym.tri_value
+        sym.type
+        sym.visibility
+
+    for choice in kconf.choices:
+        for sym in choice.syms:
+            verify(sym.choice is choice,
+                   "{0} is in choice.syms but 'sym.choice' is not the choice"
+                   .format(sym.name))
+
+            verify(sym.type in (BOOL, TRISTATE),
+                   "{} is a choice symbol but is not a bool/tristate"
+                   .format(sym.name))
+
+        choice.__str__()
+        choice.__repr__()
+        choice.str_value
+        choice.tri_value
+        choice.user_value
+        choice.assignable
+        choice.selection
+        choice.type
+        choice.visibility
+
+    # Menu nodes
+
+    node = kconf.top_node
+
+    while 1:
+        # Everything else should be well exercised elsewhere
+        node.__repr__()
+        node.__str__()
+        verify(isinstance(node.item, (Symbol, Choice)) or \
+               node.item in (MENU, COMMENT),
+               "'{}' appeared as a menu item".format(node.item))
+
+        if node.list is not None:
+            node = node.list
+
+        elif node.next is not None:
+            node = node.next
+
+        else:
+            while node.parent is not None:
+                node = node.parent
+                if node.next is not None:
+                    node = node.next
+                    break
+            else:
+                break
+
+
+def test_alldefconfig(arch, srcarch):
+    """
+    Verify that alldefconfig.py generates the same .config as
+    'make alldefconfig', for each architecture. Runs the script via
+    'make scriptconfig'.
+    """
+    shell("make scriptconfig SCRIPT=Kconfiglib/alldefconfig.py "
+          "PYTHONCMD='{}'".format(sys.executable))
+    shell("mv .config ._config")
+    shell("scripts/kconfig/conf --alldefconfig Kconfig")
+
+    compare_configs(arch)
+
+
+def test_defconfig(arch, srcarch):
+    """
+    Verify that Kconfiglib generates the same .config as scripts/kconfig/conf,
+    for each architecture/defconfig pair. In obsessive mode, this test includes
+    nonsensical groupings of arches with defconfigs from other arches (every
+    arch/defconfig combination) and takes an order of magnitude longer time to
+    run.
+
+    With logging enabled, this test appends any failures to a file
+    test_defconfig_fails in the root.
+    """
+    kconf = Kconfig()
+
+    if obsessive:
+        defconfigs = []
+
+        # Collect all defconfigs. This could be done once instead, but it's
+        # a speedy operation comparatively.
+        for srcarch_ in os.listdir("arch"):
+            defconfigs.extend(defconfig_files(srcarch_))
+    else:
+        defconfigs = defconfig_files(srcarch)
+
+    # Test architecture for each defconfig
+
+    for defconfig in defconfigs:
+        rm_configs()
+
+        kconf.load_config(defconfig)
+        kconf.write_config("._config")
+        shell("scripts/kconfig/conf --defconfig='{}' Kconfig".
+              format(defconfig))
+
+        arch_defconfig_str = "  {:14}with {:60} ".format(arch, defconfig)
+
+        if equal_configs():
+            print(arch_defconfig_str + "OK")
+        else:
+            print(arch_defconfig_str + "FAIL")
+            fail()
+            if log:
+                with open("test_defconfig_fails", "a") as fail_log:
+                    fail_log.write("{} with {} did not match\n"
+                                   .format(arch, defconfig))
+
+
+def test_min_config(arch, srcarch):
+    """
+    Verify that Kconfiglib generates the same .config as 'make savedefconfig'
+    for each architecture/defconfig pair.
+    """
+    kconf = Kconfig()
+
+    if obsessive_min_config:
+        defconfigs = []
+        for srcarch_ in os.listdir("arch"):
+            defconfigs.extend(defconfig_files(srcarch_))
+    else:
+        defconfigs = defconfig_files(srcarch)
+
+    for defconfig in defconfigs:
+        rm_configs()
+
+        kconf.load_config(defconfig)
+        kconf.write_min_config("._config")
+
+        shell("cp {} .config".format(defconfig))
+
+        shell("scripts/kconfig/conf --savedefconfig=.config Kconfig")
+
+        arch_defconfig_str = "  {:14}with {:60} ".format(arch, defconfig)
+
+        if equal_configs():
+            print(arch_defconfig_str + "OK")
+        else:
+            print(arch_defconfig_str + "FAIL")
+
+
+#
+# Helper functions
+#
+
+
+def defconfig_files(srcarch):
+    # Yields a list of defconfig file filenames for a particular srcarch
+    # subdirectory (arch/<srcarch>/)
+
+    srcarch_dir = os.path.join("arch", srcarch)
+
+    # Some arches have a defconfig in the root of their arch/<arch>/ directory
+    root_defconfig = os.path.join(srcarch_dir, "defconfig")
+    if os.path.exists(root_defconfig):
+        yield root_defconfig
+
+    # Assume all files in the arch/<arch>/configs/ directory (if it exists) are
+    # configurations
+    defconfigs_dir = os.path.join(srcarch_dir, "configs")
+
+    if not os.path.isdir(defconfigs_dir):
+        return
+
+    for dirpath, _, filenames in os.walk(defconfigs_dir):
+        for filename in filenames:
+            yield os.path.join(dirpath, filename)
+
+
+def rm_configs():
+    # Delete any old ".config" (generated by the C implementation) and
+    # "._config" (generated by us), if present.
+
+    def rm_if_exists(f):
+        if os.path.exists(f):
+            os.remove(f)
+
+    rm_if_exists(".config")
+    rm_if_exists("._config")
+
+
+def compare_configs(arch):
+    if equal_configs():
+        print("{:14}OK".format(arch))
+    else:
+        print("{:14}FAIL".format(arch))
+        fail()
+
+
+def equal_configs():
+    with open(".config") as f:
+        their = f.readlines()
+
+    # Strip the header generated by 'conf'
+    i = 0
+    for line in their:
+        if not line.startswith("#") or \
+           re.match(r"# CONFIG_(\w+) is not set", line):
+            break
+        i += 1
+    their = their[i:]
+
+    try:
+        f = open("._config")
+    except EnvironmentError as e:
+        if e.errno != errno.ENOENT:
+            raise
+        print("._config not found. Did you forget to apply the Makefile patch?")
+        return False
+    else:
+        with f:
+            our = f.readlines()
+
+    if their == our:
+        return True
+
+    # Print a unified diff to help debugging
+    print("Mismatched .config's! Unified diff:")
+    sys.stdout.writelines(difflib.unified_diff(their, our, fromfile="their",
+                                               tofile="our"))
+
+    return False
+
+
+if __name__ == "__main__":
+    run_tests()
-- 
2.34.1



From wxjstz at 126.com  Tue Jul 19 10:28:55 2022
From: wxjstz at 126.com (Xiang W)
Date: Wed, 20 Jul 2022 01:28:55 +0800
Subject: [PATCH v3 01/15] scripts: Add Kconfiglib v14.1.0 under scripts
 directory
In-Reply-To: <20220719131837.2389630-2-apatel@ventanamicro.com>
References: <20220719131837.2389630-1-apatel@ventanamicro.com>
	 <20220719131837.2389630-2-apatel@ventanamicro.com>
Message-ID: <a6f86fe87536a22bd77d645fd29ddf32aa49cc98.camel@126.com>

? 2022-07-19???? 18:48 +0530?Anup Patel???
> We adopt Kconfiglib v14.1.0 sources under scripts directory so that
> top-level OpenSBI makefile can directly use Kconfiglib scripts without
> expecting users to install a particular version Kconfiglib on their
> build system.
> 
The introduction of kconfiglib is best to use git submodule, which is
convenient for later upgrade.

Regards,
Xiang W





From atishp at rivosinc.com  Tue Jul 19 10:33:11 2022
From: atishp at rivosinc.com (Atish Kumar Patra)
Date: Tue, 19 Jul 2022 10:33:11 -0700
Subject: [PATCH 2/2] lib: sbi: Fix counter index sanity check
In-Reply-To: <20220719084715.mpuogf5g2x4qy4me@kamzik>
References: <20220718200458.3015600-1-atishp@rivosinc.com> <20220718200458.3015600-2-atishp@rivosinc.com>
 <20220719084715.mpuogf5g2x4qy4me@kamzik>
Message-ID: <CAHBxVyH2H6T+qttp000T336=e_nTpxEo=UR+S8tmc=dR3eXtWA@mail.gmail.com>

On Tue, Jul 19, 2022 at 1:47 AM Andrew Jones <ajones at ventanamicro.com> wrote:
>
> On Mon, Jul 18, 2022 at 01:04:58PM -0700, Atish Patra wrote:
> > The current implementation computes the possible counter range
> > by doing a left shift of counter base. However, this may overflow depending
> > on the counter base value. In case of overflow, the highest counter id
> > may be computed incorrectly. As per the SBI specification, the respective
> > function should return an error if any of the counter is not valid.
> >
> > Fix the counter index check by avoiding left shifting while doing the
> > sanity checks.
> >
> > Signed-off-by: Atish Patra <atishp at rivosinc.com>
> > ---
> >  lib/sbi/sbi_pmu.c | 33 +++++++++++++++++----------------
> >  1 file changed, 17 insertions(+), 16 deletions(-)
> >
> > diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
> > index 65bc9dd63f6b..29e7e2954ae3 100644
> > --- a/lib/sbi/sbi_pmu.c
> > +++ b/lib/sbi/sbi_pmu.c
> > @@ -346,25 +346,26 @@ int sbi_pmu_ctr_start(unsigned long cbase, unsigned long cmask,
> >  {
> >       int event_idx_type;
> >       uint32_t event_code;
> > -     unsigned long ctr_mask = cmask << cbase;
> >       int ret = SBI_EINVAL;
> >       bool bUpdate = FALSE;
> > +     int i, cidx;
> >
> > -     if (sbi_fls(ctr_mask) >= total_ctrs)
> > +     if ((cbase + sbi_fls(cmask)) >= total_ctrs)
> >               return ret;
> >
> >       if (flags & SBI_PMU_START_FLAG_SET_INIT_VALUE)
> >               bUpdate = TRUE;
> >
> > -     for_each_set_bit_from(cbase, &ctr_mask, total_ctrs) {
> > -             event_idx_type = pmu_ctr_validate(cbase, &event_code);
> > +     for_each_set_bit(i, &cmask, total_ctrs) {
> > +             cidx = i + cbase;
>
> I guess this for_each_set_bit_from() to for_each_set_bit() change is
> necessary to avoid skipping cidx == cbase after we no longer shift
> the mask. Might be nice to point that out in the commit message since
> it's a bit subtle.
>

Sure. I will update the commit text.

> > +             event_idx_type = pmu_ctr_validate(cidx, &event_code);
> >               if (event_idx_type < 0)
> >                       /* Continue the start operation for other counters */
> >                       continue;
> >               else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW)
> > -                     ret = pmu_ctr_start_fw(cbase, event_code, ival, bUpdate);
> > +                     ret = pmu_ctr_start_fw(cidx, event_code, ival, bUpdate);
> >               else
> > -                     ret = pmu_ctr_start_hw(cbase, ival, bUpdate);
> > +                     ret = pmu_ctr_start_hw(cidx, ival, bUpdate);
> >       }
> >
> >       return ret;
> > @@ -424,25 +425,26 @@ int sbi_pmu_ctr_stop(unsigned long cbase, unsigned long cmask,
> >       int ret = SBI_EINVAL;
> >       int event_idx_type;
> >       uint32_t event_code;
> > -     unsigned long ctr_mask = cmask << cbase;
> > +     int i, cidx;
> >
> > -     if (sbi_fls(ctr_mask) >= total_ctrs)
> > +     if ((cbase + sbi_fls(cmask)) >= total_ctrs)
> >               return SBI_EINVAL;
> >
> > -     for_each_set_bit_from(cbase, &ctr_mask, total_ctrs) {
> > -             event_idx_type = pmu_ctr_validate(cbase, &event_code);
> > +     for_each_set_bit(i, &cmask, total_ctrs) {
> > +             cidx = i + cbase;
> > +             event_idx_type = pmu_ctr_validate(cidx, &event_code);
> >               if (event_idx_type < 0)
> >                       /* Continue the stop operation for other counters */
> >                       continue;
> >
> >               else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW)
> > -                     ret = pmu_ctr_stop_fw(cbase, event_code);
> > +                     ret = pmu_ctr_stop_fw(cidx, event_code);
> >               else
> > -                     ret = pmu_ctr_stop_hw(cbase);
> > +                     ret = pmu_ctr_stop_hw(cidx);
> >
> >               if (flag & SBI_PMU_STOP_FLAG_RESET) {
> > -                     active_events[hartid][cbase] = SBI_PMU_EVENT_IDX_INVALID;
> > -                     pmu_reset_hw_mhpmevent(cbase);
> > +                     active_events[hartid][cidx] = SBI_PMU_EVENT_IDX_INVALID;
> > +                     pmu_reset_hw_mhpmevent(cidx);
> >               }
> >       }
> >
> > @@ -618,10 +620,9 @@ int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask,
> >       int event_type = get_cidx_type(event_idx);
> >       struct sbi_pmu_fw_event *fevent;
> >       uint32_t fw_evt_code;
> > -     unsigned long tmp = cidx_mask << cidx_base;
> >
> >       /* Do a basic sanity check of counter base & mask */
> > -     if (sbi_fls(tmp) >= total_ctrs || event_type >= SBI_PMU_EVENT_TYPE_MAX)
> > +     if ((cidx_base + sbi_fls(cidx_mask)) >= total_ctrs || event_type >= SBI_PMU_EVENT_TYPE_MAX)
> >               return SBI_EINVAL;
> >
> >       if (flags & SBI_PMU_CFG_FLAG_SKIP_MATCH) {
> > --
> > 2.25.1
> >
>
> Reviewed-by: Andrew Jones <ajones at ventanamicro.com>


From atishp at rivosinc.com  Tue Jul 19 12:33:47 2022
From: atishp at rivosinc.com (Atish Kumar Patra)
Date: Tue, 19 Jul 2022 12:33:47 -0700
Subject: [PATCH 1/2] lib: sbi: Add the bound check for firmware events
In-Reply-To: <20220719071029.te2tqptafw7kilxm@kamzik>
References: <20220718200458.3015600-1-atishp@rivosinc.com> <20220719071029.te2tqptafw7kilxm@kamzik>
Message-ID: <CAHBxVyG-1=wzch+p7ZDb2pMYHZHwks-oj-aBg9_At5_Ug4E=ng@mail.gmail.com>

On Tue, Jul 19, 2022 at 12:10 AM Andrew Jones <ajones at ventanamicro.com> wrote:
>
>
> Hi Atish,
>
> Top posting because there's no cover letter to reply to. While reviewing
> for this patch I see another buffer overrun possibility in
> pmu_ctr_validate()
>
> static int pmu_ctr_validate(uint32_t cidx, uint32_t *event_idx_code)
> {
>         uint32_t event_idx_val;
>         uint32_t event_idx_type;
>         u32 hartid = current_hartid();
>
>         event_idx_val = active_events[hartid][cidx]; <--- Here, because...
>
> ...we have...
>
> int sbi_pmu_ctr_read(uint32_t cidx, unsigned long *cval)
> {
>         int event_idx_type;
>         uint32_t event_code;
>         uint64_t cval64;
>
>         event_idx_type = pmu_ctr_validate(cidx, &event_code);
>
> ...where sbi_pmu_ctr_read() is called directly from the ecall handler
> passing a0 for cidx.
>

Yes. I do have the check cidx >= total_ctrs but it is placed after the
array access by mistake.
I will fix it in v2. Thanks for catching this.

> Thanks,
> drew
>
>
> On Mon, Jul 18, 2022 at 01:04:57PM -0700, Atish Patra wrote:
> > Currently, there is no sanity check for firmware event code. We don't see
> > any issue as Linux kernel driver does a bound check on firmware events
> > already. However, OpenSBI can't assume sane supervisor mode software
> > always. Thus, an invalid event idx can cause a buffer overflow error.
> >
> > Improve the sanity check for firmware events.
> >
> > Signed-off-by: Atish Patra <atishp at rivosinc.com>
> > ---
> >  lib/sbi/sbi_pmu.c | 7 ++++++-
> >  1 file changed, 6 insertions(+), 1 deletion(-)
> >
> > diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
> > index 3f5fd1031b25..65bc9dd63f6b 100644
> > --- a/lib/sbi/sbi_pmu.c
> > +++ b/lib/sbi/sbi_pmu.c
> > @@ -123,6 +123,9 @@ static int pmu_ctr_validate(uint32_t cidx, uint32_t *event_idx_code)
> >               return SBI_EINVAL;
> >
> >       *event_idx_code = get_cidx_code(event_idx_val);
> > +     if ((event_idx_type == SBI_PMU_EVENT_TYPE_FW) &&
> > +         (*event_idx_code >= SBI_PMU_FW_MAX))
> > +             return SBI_EINVAL;
> >
> >       return event_idx_type;
> >  }
> > @@ -652,6 +655,8 @@ skip_match:
> >                       pmu_ctr_start_hw(ctr_idx, 0, false);
> >       } else if (event_type == SBI_PMU_EVENT_TYPE_FW) {
> >               fw_evt_code = get_cidx_code(event_idx);
> > +             if (unlikely(fw_evt_code >= SBI_PMU_FW_MAX))
> > +                     return SBI_ENOTSUPP;
> >               fevent = &fw_event_map[hartid][fw_evt_code];
> >               if (flags & SBI_PMU_CFG_FLAG_CLEAR_VALUE)
> >                       fevent->curr_count = 0;
> > @@ -721,7 +726,7 @@ static void pmu_reset_event_map(u32 hartid)
> >       /* Initialize the counter to event mapping table */
> >       for (j = 3; j < total_ctrs; j++)
> >               active_events[hartid][j] = SBI_PMU_EVENT_IDX_INVALID;
> > -     for (j = 0; j < SBI_PMU_FW_CTR_MAX; j++)
> > +     for (j = 0; j < SBI_PMU_FW_EVENT_MAX; j++)
> >               sbi_memset(&fw_event_map[hartid][j], 0,
> >                          sizeof(struct sbi_pmu_fw_event));
> >  }
> > --
> > 2.25.1
> >
> >
> > --
> > opensbi mailing list
> > opensbi at lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/opensbi


From atishp at rivosinc.com  Tue Jul 19 12:40:37 2022
From: atishp at rivosinc.com (Atish Kumar Patra)
Date: Tue, 19 Jul 2022 12:40:37 -0700
Subject: [PATCH 1/2] lib: sbi: Add the bound check for firmware events
In-Reply-To: <20220719070235.yzfne4bwamh7a43n@kamzik>
References: <20220718200458.3015600-1-atishp@rivosinc.com> <20220719070235.yzfne4bwamh7a43n@kamzik>
Message-ID: <CAHBxVyG_7pcGw-prUi7oXK_YgVFxXt81i4xH6AAay9WF=DBx5w@mail.gmail.com>

On Tue, Jul 19, 2022 at 12:02 AM Andrew Jones <ajones at ventanamicro.com> wrote:
>
> On Mon, Jul 18, 2022 at 01:04:57PM -0700, Atish Patra wrote:
> > Currently, there is no sanity check for firmware event code. We don't see
> > any issue as Linux kernel driver does a bound check on firmware events
> > already. However, OpenSBI can't assume sane supervisor mode software
> > always. Thus, an invalid event idx can cause a buffer overflow error.
> >
> > Improve the sanity check for firmware events.
> >
> > Signed-off-by: Atish Patra <atishp at rivosinc.com>
> > ---
> >  lib/sbi/sbi_pmu.c | 7 ++++++-
> >  1 file changed, 6 insertions(+), 1 deletion(-)
> >
> > diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
> > index 3f5fd1031b25..65bc9dd63f6b 100644
> > --- a/lib/sbi/sbi_pmu.c
> > +++ b/lib/sbi/sbi_pmu.c
> > @@ -123,6 +123,9 @@ static int pmu_ctr_validate(uint32_t cidx, uint32_t *event_idx_code)
> >               return SBI_EINVAL;
> >
> >       *event_idx_code = get_cidx_code(event_idx_val);
> > +     if ((event_idx_type == SBI_PMU_EVENT_TYPE_FW) &&
> > +         (*event_idx_code >= SBI_PMU_FW_MAX))
> > +             return SBI_EINVAL;
>
> I think it would be better to only write *event_idx_code if we know
> it's valid. Actually, maybe at the start of pmu_ctr_validate() we
> should initialize it to something we know is invalid and then at the
> bottom write it after we're sure we have something worth writing.
>
> If pmu_ctr_validate() should be used to validate all event types, then
> shouldn't we have a switch here, something like...
>
>   switch (event_idx_type) {
>   case SBI_PMU_EVENT_TYPE_HW:
>     if (code >= SBI_PMU_HW_MAX)
>         return SBI_EINVAL;
>     break;
>   case SBI_PMU_EVENT_TYPE_HW_CACHE:
>     if (code >= SBI_PMU_HW_CACHE_MAX)
>         return SBI_EINVAL;
>     break;
>   ...
>

We should probably do event id validation in cfg_match. If invalid
event id is passed in cfg_match,
the corresponding entry in active_events will always be set to
SBI_PMU_EVENT_IDX_INVALID.

Thus, we can avoid any kind of event validation here. I will improve
the cfg_match function instead
of this change.

> >
> >       return event_idx_type;
> >  }
> > @@ -652,6 +655,8 @@ skip_match:
> >                       pmu_ctr_start_hw(ctr_idx, 0, false);
> >       } else if (event_type == SBI_PMU_EVENT_TYPE_FW) {
> >               fw_evt_code = get_cidx_code(event_idx);
> > +             if (unlikely(fw_evt_code >= SBI_PMU_FW_MAX))
> > +                     return SBI_ENOTSUPP;
>
> Validation buried down here is a bit scary. It would be much nicer if we
> could pull all the validation up to the ecall entry points.
>

Agreed. We should have event_validate called earlier in the
sbi_pmu_ctr_cfg_match.
Will fix it.

> >               fevent = &fw_event_map[hartid][fw_evt_code];
> >               if (flags & SBI_PMU_CFG_FLAG_CLEAR_VALUE)
> >                       fevent->curr_count = 0;
> > @@ -721,7 +726,7 @@ static void pmu_reset_event_map(u32 hartid)
> >       /* Initialize the counter to event mapping table */
> >       for (j = 3; j < total_ctrs; j++)
> >               active_events[hartid][j] = SBI_PMU_EVENT_IDX_INVALID;
> > -     for (j = 0; j < SBI_PMU_FW_CTR_MAX; j++)
> > +     for (j = 0; j < SBI_PMU_FW_EVENT_MAX; j++)
>
> This change is not related to what the commit message is talking about,
> so it belongs in a separate patch.
>

Sure. Will do that.

> Thanks,
> drew
>
> >               sbi_memset(&fw_event_map[hartid][j], 0,
> >                          sizeof(struct sbi_pmu_fw_event));
> >  }
> > --
> > 2.25.1
> >
> >
> > --
> > opensbi mailing list
> > opensbi at lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/opensbi


From atishp at rivosinc.com  Tue Jul 19 15:46:11 2022
From: atishp at rivosinc.com (Atish Patra)
Date: Tue, 19 Jul 2022 15:46:11 -0700
Subject: [PATCH v2  0/4] Miscellaneous PMU fixes
Message-ID: <20220719224615.3080494-1-atishp@rivosinc.com>

This series have a set of smaller PMU related fixes/improvements.
Most of the fixes are around possible buffer overrun errors in case of 
corrupt supervisor software.

Changes from v1->v2:
1. Split patch 1 to two patches with further improvements.
2. Added a fix for the issue pointed by Andrew.

Atish Patra (4):
lib: sbi: Fix counter index sanity check
lib: sbi: Add the bound check for events during config match
lib: sbi: Fix possible buffer overrun in counter validation
lib: sbi: Fix fw_event_map initialization

lib/sbi/sbi_pmu.c | 80 ++++++++++++++++++++++++++++++++++-------------
1 file changed, 58 insertions(+), 22 deletions(-)

--
2.25.1



From atishp at rivosinc.com  Tue Jul 19 15:46:14 2022
From: atishp at rivosinc.com (Atish Patra)
Date: Tue, 19 Jul 2022 15:46:14 -0700
Subject: [PATCH v2  3/4] lib: sbi: Fix possible buffer overrun in counter validation
In-Reply-To: <20220719224615.3080494-1-atishp@rivosinc.com>
References: <20220719224615.3080494-1-atishp@rivosinc.com>
Message-ID: <20220719224615.3080494-4-atishp@rivosinc.com>

The active_events array is accessed with counter ID passed from the supervisor
software before the counter ID bound check. This may cause a buffer overrun
if a supervisor passes an invalid counter ID.

Fix this by moving the access part after the bound check.

Signed-off-by: Atish Patra <atishp at rivosinc.com>
---
 lib/sbi/sbi_pmu.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
index 1170cba43b3d..a159d72ac6dc 100644
--- a/lib/sbi/sbi_pmu.c
+++ b/lib/sbi/sbi_pmu.c
@@ -144,13 +144,13 @@ static int pmu_ctr_validate(uint32_t cidx, uint32_t *event_idx_code)
 	uint32_t event_idx_type;
 	u32 hartid = current_hartid();
 
-	event_idx_val = active_events[hartid][cidx];
-
-	if (cidx >= total_ctrs || (event_idx_val == SBI_PMU_EVENT_IDX_INVALID))
+	if (cidx >= total_ctrs)
 		return SBI_EINVAL;
 
+	event_idx_val = active_events[hartid][cidx];
 	event_idx_type = get_cidx_type(event_idx_val);
-	if (event_idx_type >= SBI_PMU_EVENT_TYPE_MAX)
+	if (event_idx_val == SBI_PMU_EVENT_IDX_INVALID ||
+	    event_idx_type >= SBI_PMU_EVENT_TYPE_MAX)
 		return SBI_EINVAL;
 
 	*event_idx_code = get_cidx_code(event_idx_val);
-- 
2.25.1



From atishp at rivosinc.com  Tue Jul 19 15:46:13 2022
From: atishp at rivosinc.com (Atish Patra)
Date: Tue, 19 Jul 2022 15:46:13 -0700
Subject: [PATCH v2  2/4] lib: sbi: Add the bound check for events during config match
In-Reply-To: <20220719224615.3080494-1-atishp@rivosinc.com>
References: <20220719224615.3080494-1-atishp@rivosinc.com>
Message-ID: <20220719224615.3080494-3-atishp@rivosinc.com>

Currently, there is no sanity check for firmware event code. We don't see
any issue as Linux kernel driver does a bound check on firmware events
already. However, OpenSBI can't assume sane supervisor mode software
always. Thus, an invalid event idx can cause a buffer overflow error.
For hardware events, the match will fail for invalid event code anyways.
However, a search is unecessary if event code is invalid.

Add a event ID validation function to solve the issue.

Signed-off-by: Atish Patra <atishp at rivosinc.com>
---
 lib/sbi/sbi_pmu.c | 39 +++++++++++++++++++++++++++++++++++++--
 1 file changed, 37 insertions(+), 2 deletions(-)

diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
index 31631a2fab80..1170cba43b3d 100644
--- a/lib/sbi/sbi_pmu.c
+++ b/lib/sbi/sbi_pmu.c
@@ -107,6 +107,37 @@ static bool pmu_event_select_overlap(struct sbi_pmu_hw_event *evt,
 	return FALSE;
 }
 
+static int pmu_event_validate(unsigned long event_idx)
+{
+	uint32_t event_idx_type = get_cidx_type(event_idx);
+	uint32_t event_idx_code = get_cidx_code(event_idx);
+	uint32_t event_idx_code_max = -1;
+
+	switch(event_idx_type) {
+	case SBI_PMU_EVENT_TYPE_HW:
+		event_idx_code_max = SBI_PMU_HW_GENERAL_MAX;
+		break;
+	case SBI_PMU_EVENT_TYPE_FW:
+		event_idx_code_max = SBI_PMU_FW_MAX;
+		break;
+	case SBI_PMU_EVENT_TYPE_HW_CACHE:
+		event_idx_code_max = ((SBI_PMU_HW_CACHE_MAX & 0xFFF) << 0x03) |
+				       (SBI_PMU_HW_CACHE_OP_MAX & 0x3) << 0x01 |
+					0x01;
+		break;
+	case SBI_PMU_EVENT_TYPE_HW_RAW:
+		event_idx_code_max = 1; // event_idx.code should be zero
+		break;
+	default:
+		return SBI_EINVAL;
+	}
+
+	if (event_idx_code < event_idx_code_max)
+		return event_idx_type;
+	else
+		return SBI_EINVAL;
+}
+
 static int pmu_ctr_validate(uint32_t cidx, uint32_t *event_idx_code)
 {
 	uint32_t event_idx_val;
@@ -614,12 +645,16 @@ int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask,
 {
 	int ctr_idx = SBI_ENOTSUPP;
 	u32 hartid = current_hartid();
-	int event_type = get_cidx_type(event_idx);
+	int event_type;
 	struct sbi_pmu_fw_event *fevent;
 	uint32_t fw_evt_code;
 
 	/* Do a basic sanity check of counter base & mask */
-	if ((cidx_base + sbi_fls(cidx_mask)) >= total_ctrs || event_type >= SBI_PMU_EVENT_TYPE_MAX)
+	if ((cidx_base + sbi_fls(cidx_mask)) >= total_ctrs)
+		return SBI_EINVAL;
+
+	event_type = pmu_event_validate(event_idx);
+	if (event_type < 0)
 		return SBI_EINVAL;
 
 	if (flags & SBI_PMU_CFG_FLAG_SKIP_MATCH) {
-- 
2.25.1



From atishp at rivosinc.com  Tue Jul 19 15:46:12 2022
From: atishp at rivosinc.com (Atish Patra)
Date: Tue, 19 Jul 2022 15:46:12 -0700
Subject: [PATCH v2  1/4] lib: sbi: Fix counter index sanity check
In-Reply-To: <20220719224615.3080494-1-atishp@rivosinc.com>
References: <20220719224615.3080494-1-atishp@rivosinc.com>
Message-ID: <20220719224615.3080494-2-atishp@rivosinc.com>

The current implementation computes the possible counter range
by doing a left shift of counter base. However, this may overflow depending
on the counter base value. In case of overflow, the highest counter id
may be computed incorrectly. As per the SBI specification, the respective
function should return an error if any of the counter is not valid.

Fix the counter index check by avoiding left shifting while doing the
sanity checks. Without the shift, the implementation just iterates
over the counter mask and computes the correct counter index by adding
the base to it.

Reviewed-by: Andrew Jones <ajones at ventanamicro.com>
Signed-off-by: Atish Patra <atishp at rivosinc.com>
---
 lib/sbi/sbi_pmu.c | 33 +++++++++++++++++----------------
 1 file changed, 17 insertions(+), 16 deletions(-)

diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
index 3f5fd1031b25..31631a2fab80 100644
--- a/lib/sbi/sbi_pmu.c
+++ b/lib/sbi/sbi_pmu.c
@@ -343,25 +343,26 @@ int sbi_pmu_ctr_start(unsigned long cbase, unsigned long cmask,
 {
 	int event_idx_type;
 	uint32_t event_code;
-	unsigned long ctr_mask = cmask << cbase;
 	int ret = SBI_EINVAL;
 	bool bUpdate = FALSE;
+	int i, cidx;
 
-	if (sbi_fls(ctr_mask) >= total_ctrs)
+	if ((cbase + sbi_fls(cmask)) >= total_ctrs)
 		return ret;
 
 	if (flags & SBI_PMU_START_FLAG_SET_INIT_VALUE)
 		bUpdate = TRUE;
 
-	for_each_set_bit_from(cbase, &ctr_mask, total_ctrs) {
-		event_idx_type = pmu_ctr_validate(cbase, &event_code);
+	for_each_set_bit(i, &cmask, total_ctrs) {
+		cidx = i + cbase;
+		event_idx_type = pmu_ctr_validate(cidx, &event_code);
 		if (event_idx_type < 0)
 			/* Continue the start operation for other counters */
 			continue;
 		else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW)
-			ret = pmu_ctr_start_fw(cbase, event_code, ival, bUpdate);
+			ret = pmu_ctr_start_fw(cidx, event_code, ival, bUpdate);
 		else
-			ret = pmu_ctr_start_hw(cbase, ival, bUpdate);
+			ret = pmu_ctr_start_hw(cidx, ival, bUpdate);
 	}
 
 	return ret;
@@ -421,25 +422,26 @@ int sbi_pmu_ctr_stop(unsigned long cbase, unsigned long cmask,
 	int ret = SBI_EINVAL;
 	int event_idx_type;
 	uint32_t event_code;
-	unsigned long ctr_mask = cmask << cbase;
+	int i, cidx;
 
-	if (sbi_fls(ctr_mask) >= total_ctrs)
+	if ((cbase + sbi_fls(cmask)) >= total_ctrs)
 		return SBI_EINVAL;
 
-	for_each_set_bit_from(cbase, &ctr_mask, total_ctrs) {
-		event_idx_type = pmu_ctr_validate(cbase, &event_code);
+	for_each_set_bit(i, &cmask, total_ctrs) {
+		cidx = i + cbase;
+		event_idx_type = pmu_ctr_validate(cidx, &event_code);
 		if (event_idx_type < 0)
 			/* Continue the stop operation for other counters */
 			continue;
 
 		else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW)
-			ret = pmu_ctr_stop_fw(cbase, event_code);
+			ret = pmu_ctr_stop_fw(cidx, event_code);
 		else
-			ret = pmu_ctr_stop_hw(cbase);
+			ret = pmu_ctr_stop_hw(cidx);
 
 		if (flag & SBI_PMU_STOP_FLAG_RESET) {
-			active_events[hartid][cbase] = SBI_PMU_EVENT_IDX_INVALID;
-			pmu_reset_hw_mhpmevent(cbase);
+			active_events[hartid][cidx] = SBI_PMU_EVENT_IDX_INVALID;
+			pmu_reset_hw_mhpmevent(cidx);
 		}
 	}
 
@@ -615,10 +617,9 @@ int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask,
 	int event_type = get_cidx_type(event_idx);
 	struct sbi_pmu_fw_event *fevent;
 	uint32_t fw_evt_code;
-	unsigned long tmp = cidx_mask << cidx_base;
 
 	/* Do a basic sanity check of counter base & mask */
-	if (sbi_fls(tmp) >= total_ctrs || event_type >= SBI_PMU_EVENT_TYPE_MAX)
+	if ((cidx_base + sbi_fls(cidx_mask)) >= total_ctrs || event_type >= SBI_PMU_EVENT_TYPE_MAX)
 		return SBI_EINVAL;
 
 	if (flags & SBI_PMU_CFG_FLAG_SKIP_MATCH) {
-- 
2.25.1



From atishp at rivosinc.com  Tue Jul 19 15:46:15 2022
From: atishp at rivosinc.com (Atish Patra)
Date: Tue, 19 Jul 2022 15:46:15 -0700
Subject: [PATCH v2  4/4] lib: sbi: Fix fw_event_map initialization
In-Reply-To: <20220719224615.3080494-1-atishp@rivosinc.com>
References: <20220719224615.3080494-1-atishp@rivosinc.com>
Message-ID: <20220719224615.3080494-5-atishp@rivosinc.com>

fw_event_map represents array of firmware events. It should initialized
for maximum number of firmware events not counters.

Signed-off-by: Atish Patra <atishp at rivosinc.com>
---
 lib/sbi/sbi_pmu.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
index a159d72ac6dc..a622054595c1 100644
--- a/lib/sbi/sbi_pmu.c
+++ b/lib/sbi/sbi_pmu.c
@@ -757,7 +757,7 @@ static void pmu_reset_event_map(u32 hartid)
 	/* Initialize the counter to event mapping table */
 	for (j = 3; j < total_ctrs; j++)
 		active_events[hartid][j] = SBI_PMU_EVENT_IDX_INVALID;
-	for (j = 0; j < SBI_PMU_FW_CTR_MAX; j++)
+	for (j = 0; j < SBI_PMU_FW_EVENT_MAX; j++)
 		sbi_memset(&fw_event_map[hartid][j], 0,
 			   sizeof(struct sbi_pmu_fw_event));
 }
-- 
2.25.1



From anup at brainfault.org  Tue Jul 19 21:39:45 2022
From: anup at brainfault.org (Anup Patel)
Date: Wed, 20 Jul 2022 10:09:45 +0530
Subject: [PATCH v2] lib: sbi_hart: Shorten the code to set MPV bit
In-Reply-To: <20220714172941.2967-1-vivahavey@gmail.com>
References: <20220714172941.2967-1-vivahavey@gmail.com>
Message-ID: <CAAhSdy14uN93KeW5U79KNUeOYf3fHAf_NKKUmG12mOv8793LuA@mail.gmail.com>

On Thu, Jul 14, 2022 at 11:00 PM gagachang <vivahavey at gmail.com> wrote:
>
> MPV bit is set when the value of next_virt boolean variable equals
> true. Since the value of next_virt is either 0 or 1, we can set
> MPV bit without if-else logic.
>
> Signed-off-by: Che-Chia Chang <alvinga at andestech.com>
> Reviewed-by: Andrew Jones <ajones at ventanamicro.com>

Looks good to me.

Reviewed-by: Anup Patel <anup at brainfault.org>

Applied this patch to the riscv/opensbi repo.

Thanks,
Anup

>
> --
> Change since v1:
> - Drop the unnecessary brackets
> ---
>  lib/sbi/sbi_hart.c | 13 +++----------
>  1 file changed, 3 insertions(+), 10 deletions(-)
>
> diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c
> index de86fee..67b685a 100644
> --- a/lib/sbi/sbi_hart.c
> +++ b/lib/sbi/sbi_hart.c
> @@ -769,19 +769,12 @@ sbi_hart_switch_mode(unsigned long arg0, unsigned long arg1,
>  #if __riscv_xlen == 32
>         if (misa_extension('H')) {
>                 valH = csr_read(CSR_MSTATUSH);
> -               if (next_virt)
> -                       valH = INSERT_FIELD(valH, MSTATUSH_MPV, 1);
> -               else
> -                       valH = INSERT_FIELD(valH, MSTATUSH_MPV, 0);
> +               valH = INSERT_FIELD(valH, MSTATUSH_MPV, next_virt);
>                 csr_write(CSR_MSTATUSH, valH);
>         }
>  #else
> -       if (misa_extension('H')) {
> -               if (next_virt)
> -                       val = INSERT_FIELD(val, MSTATUS_MPV, 1);
> -               else
> -                       val = INSERT_FIELD(val, MSTATUS_MPV, 0);
> -       }
> +       if (misa_extension('H'))
> +               val = INSERT_FIELD(val, MSTATUS_MPV, next_virt);
>  #endif
>         csr_write(CSR_MSTATUS, val);
>         csr_write(CSR_MEPC, next_addr);
> --
> 2.25.1
>
>
> --
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi


From anup at brainfault.org  Tue Jul 19 21:42:49 2022
From: anup at brainfault.org (Anup Patel)
Date: Wed, 20 Jul 2022 10:12:49 +0530
Subject: [PATCH v3] lib: utils/serial: Update Shakti UART based on latest implementation
In-Reply-To: <20220708115244.556074-1-apatel@ventanamicro.com>
References: <20220708115244.556074-1-apatel@ventanamicro.com>
Message-ID: <CAAhSdy1PGGVHcbEgNr3vhOntLyoGqXZH-_qz3WkfnEC_Muezzw@mail.gmail.com>

On Fri, Jul 8, 2022 at 5:23 PM Anup Patel <apatel at ventanamicro.com> wrote:
>
> From: Prasanna T <ptprasanna at gmail.com>
>
> The age old version of Shakti UART was upgraded long back, but we missed
> updating the driver in OpenSBI. The old version of UART is not supported
> anymore, hence removed the inline comment which is also outdated now.
>
> Signed-off-by: Prasanna T <ptprasanna at gmail.com>
> Signed-off-by: Anup Patel <apatel at ventanamicro.com>

@Prasanna, Please make sure that Shakti UART compatible string is
documented in the upstream Linux kernel. This time it's okay since there
is no HW with the old version of Shakti UART but subsequent changes to
Shakti UART should require a new compatible string.

Applied this patch to the riscv/opensbi repo.

Thanks,
Anup

> ---
> Changes since v2:
>  - Updated patch subject and description
>  - Minor fix in shakti_uart_getc()
> ---
>  lib/utils/serial/shakti-uart.c | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/lib/utils/serial/shakti-uart.c b/lib/utils/serial/shakti-uart.c
> index 5f2fe75..3556935 100644
> --- a/lib/utils/serial/shakti-uart.c
> +++ b/lib/utils/serial/shakti-uart.c
> @@ -19,21 +19,21 @@
>  #define REG_RX_THRES   0x20
>
>  #define UART_TX_FULL  0x2
> +#define UART_RX_NOT_EMPTY 0x4
>  #define UART_RX_FULL  0x8
>
>  static volatile char *uart_base;
>
>  static void shakti_uart_putc(char ch)
>  {
> -       while((readw(uart_base + REG_STATUS) & UART_TX_FULL))
> +       while ((readb(uart_base + REG_STATUS) & UART_TX_FULL))
>                 ;
>         writeb(ch, uart_base + REG_TX);
>  }
>
>  static int shakti_uart_getc(void)
>  {
> -       u16 status = readw(uart_base + REG_STATUS);
> -       if (status & UART_RX_FULL)
> +       if (readb(uart_base + REG_STATUS) & UART_RX_NOT_EMPTY)
>                 return readb(uart_base + REG_RX);
>         return -1;
>  }
> --
> 2.34.1
>


From ajones at ventanamicro.com  Tue Jul 19 23:51:10 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Wed, 20 Jul 2022 08:51:10 +0200
Subject: [PATCH v3 01/15] scripts: Add Kconfiglib v14.1.0 under scripts
 directory
In-Reply-To: <a6f86fe87536a22bd77d645fd29ddf32aa49cc98.camel@126.com>
References: <20220719131837.2389630-1-apatel@ventanamicro.com>
 <20220719131837.2389630-2-apatel@ventanamicro.com>
 <a6f86fe87536a22bd77d645fd29ddf32aa49cc98.camel@126.com>
Message-ID: <20220720065110.lh6rrc7t7xply7ha@kamzik>

On Wed, Jul 20, 2022 at 01:28:55AM +0800, Xiang W wrote:
> ? 2022-07-19???? 18:48 +0530?Anup Patel???
> > We adopt Kconfiglib v14.1.0 sources under scripts directory so that
> > top-level OpenSBI makefile can directly use Kconfiglib scripts without
> > expecting users to install a particular version Kconfiglib on their
> > build system.
> > 
> The introduction of kconfiglib is best to use git submodule, which is
> convenient for later upgrade.
>

submodules are nifty, but add a burden on developers who frequently
checkout older versions of the superproject, such as when bisecting,
since manually running 'git submodule update' may be necessary after
the checkouts. That probably wouldn't be the case for kconfiglib,
since that project isn't getting updates (hasn't for 2.5 years) and
opensbi would likely rarely update the version it tracks anyway, but
that also means there's not much value in making it a submodule in
the first place.

That said, it might be nice to only commit something like QEMU's
minikconf.py to this repo, providing basic Kconfig/defconfig parsing,
but no user interfaces, and then provide an option to support
menuconfig, etc. by 'updating' a kconfiglib submodule to get the
scripts it provides.

Thanks,
drew


From ajones at ventanamicro.com  Wed Jul 20 03:29:48 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Wed, 20 Jul 2022 12:29:48 +0200
Subject: [PATCH v2  2/4] lib: sbi: Add the bound check for events during
 config match
In-Reply-To: <20220719224615.3080494-3-atishp@rivosinc.com>
References: <20220719224615.3080494-1-atishp@rivosinc.com>
 <20220719224615.3080494-3-atishp@rivosinc.com>
Message-ID: <20220720102948.ffachuxzcfj5qwfa@kamzik>

On Tue, Jul 19, 2022 at 03:46:13PM -0700, Atish Patra wrote:
> Currently, there is no sanity check for firmware event code. We don't see
> any issue as Linux kernel driver does a bound check on firmware events
> already. However, OpenSBI can't assume sane supervisor mode software
> always. Thus, an invalid event idx can cause a buffer overflow error.
> For hardware events, the match will fail for invalid event code anyways.
> However, a search is unecessary if event code is invalid.
> 
> Add a event ID validation function to solve the issue.
> 
> Signed-off-by: Atish Patra <atishp at rivosinc.com>
> ---
>  lib/sbi/sbi_pmu.c | 39 +++++++++++++++++++++++++++++++++++++--
>  1 file changed, 37 insertions(+), 2 deletions(-)
> 
> diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
> index 31631a2fab80..1170cba43b3d 100644
> --- a/lib/sbi/sbi_pmu.c
> +++ b/lib/sbi/sbi_pmu.c
> @@ -107,6 +107,37 @@ static bool pmu_event_select_overlap(struct sbi_pmu_hw_event *evt,
>  	return FALSE;
>  }
>  
> +static int pmu_event_validate(unsigned long event_idx)
> +{
> +	uint32_t event_idx_type = get_cidx_type(event_idx);
> +	uint32_t event_idx_code = get_cidx_code(event_idx);
> +	uint32_t event_idx_code_max = -1;
> +
> +	switch(event_idx_type) {
> +	case SBI_PMU_EVENT_TYPE_HW:
> +		event_idx_code_max = SBI_PMU_HW_GENERAL_MAX;
> +		break;
> +	case SBI_PMU_EVENT_TYPE_FW:
> +		event_idx_code_max = SBI_PMU_FW_MAX;
> +		break;
> +	case SBI_PMU_EVENT_TYPE_HW_CACHE:
> +		event_idx_code_max = ((SBI_PMU_HW_CACHE_MAX & 0xFFF) << 0x03) |
> +				       (SBI_PMU_HW_CACHE_OP_MAX & 0x3) << 0x01 |
> +					0x01;

We need to check these fields independently, otherwise event_idx_code
could be something like 6, which is less than event_idx_code_max (63),
but represents a code with an invalid op_id.

> +		break;
> +	case SBI_PMU_EVENT_TYPE_HW_RAW:
> +		event_idx_code_max = 1; // event_idx.code should be zero
> +		break;
> +	default:
> +		return SBI_EINVAL;
> +	}
> +
> +	if (event_idx_code < event_idx_code_max)
> +		return event_idx_type;
> +	else

nit: no need for the 'else'

> +		return SBI_EINVAL;
> +}
> +
>  static int pmu_ctr_validate(uint32_t cidx, uint32_t *event_idx_code)
>  {
>  	uint32_t event_idx_val;
> @@ -614,12 +645,16 @@ int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask,
>  {
>  	int ctr_idx = SBI_ENOTSUPP;
>  	u32 hartid = current_hartid();
> -	int event_type = get_cidx_type(event_idx);
> +	int event_type;
>  	struct sbi_pmu_fw_event *fevent;
>  	uint32_t fw_evt_code;
>  
>  	/* Do a basic sanity check of counter base & mask */
> -	if ((cidx_base + sbi_fls(cidx_mask)) >= total_ctrs || event_type >= SBI_PMU_EVENT_TYPE_MAX)
> +	if ((cidx_base + sbi_fls(cidx_mask)) >= total_ctrs)
> +		return SBI_EINVAL;
> +
> +	event_type = pmu_event_validate(event_idx);
> +	if (event_type < 0)
>  		return SBI_EINVAL;
>  
>  	if (flags & SBI_PMU_CFG_FLAG_SKIP_MATCH) {
> -- 
> 2.25.1
>

Thanks,
drew 


From ajones at ventanamicro.com  Wed Jul 20 03:37:26 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Wed, 20 Jul 2022 12:37:26 +0200
Subject: [PATCH v2  3/4] lib: sbi: Fix possible buffer overrun in counter
 validation
In-Reply-To: <20220719224615.3080494-4-atishp@rivosinc.com>
References: <20220719224615.3080494-1-atishp@rivosinc.com>
 <20220719224615.3080494-4-atishp@rivosinc.com>
Message-ID: <20220720103726.c6g2c3q2hktbgpx3@kamzik>

On Tue, Jul 19, 2022 at 03:46:14PM -0700, Atish Patra wrote:
> The active_events array is accessed with counter ID passed from the supervisor
> software before the counter ID bound check. This may cause a buffer overrun
> if a supervisor passes an invalid counter ID.
> 
> Fix this by moving the access part after the bound check.
> 
> Signed-off-by: Atish Patra <atishp at rivosinc.com>
> ---
>  lib/sbi/sbi_pmu.c | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
> 
> diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
> index 1170cba43b3d..a159d72ac6dc 100644
> --- a/lib/sbi/sbi_pmu.c
> +++ b/lib/sbi/sbi_pmu.c
> @@ -144,13 +144,13 @@ static int pmu_ctr_validate(uint32_t cidx, uint32_t *event_idx_code)
>  	uint32_t event_idx_type;
>  	u32 hartid = current_hartid();
>  
> -	event_idx_val = active_events[hartid][cidx];
> -
> -	if (cidx >= total_ctrs || (event_idx_val == SBI_PMU_EVENT_IDX_INVALID))
> +	if (cidx >= total_ctrs)
>  		return SBI_EINVAL;
>  
> +	event_idx_val = active_events[hartid][cidx];
>  	event_idx_type = get_cidx_type(event_idx_val);
> -	if (event_idx_type >= SBI_PMU_EVENT_TYPE_MAX)
> +	if (event_idx_val == SBI_PMU_EVENT_IDX_INVALID ||
> +	    event_idx_type >= SBI_PMU_EVENT_TYPE_MAX)
>  		return SBI_EINVAL;
>  
>  	*event_idx_code = get_cidx_code(event_idx_val);
> -- 
> 2.25.1
> 

Reported-by: Andrew Jones <ajones at ventanamicro.com>
Reviewed-by: Andrew Jones <ajones at ventanamicro.com>


From ajones at ventanamicro.com  Wed Jul 20 03:40:08 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Wed, 20 Jul 2022 12:40:08 +0200
Subject: [PATCH v2  4/4] lib: sbi: Fix fw_event_map initialization
In-Reply-To: <20220719224615.3080494-5-atishp@rivosinc.com>
References: <20220719224615.3080494-1-atishp@rivosinc.com>
 <20220719224615.3080494-5-atishp@rivosinc.com>
Message-ID: <20220720104008.wkt5o6melyexci36@kamzik>

On Tue, Jul 19, 2022 at 03:46:15PM -0700, Atish Patra wrote:
> fw_event_map represents array of firmware events. It should initialized
                                                             ^ be
> for maximum number of firmware events not counters.
     ^ the
> 
> Signed-off-by: Atish Patra <atishp at rivosinc.com>
> ---
>  lib/sbi/sbi_pmu.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
> index a159d72ac6dc..a622054595c1 100644
> --- a/lib/sbi/sbi_pmu.c
> +++ b/lib/sbi/sbi_pmu.c
> @@ -757,7 +757,7 @@ static void pmu_reset_event_map(u32 hartid)
>  	/* Initialize the counter to event mapping table */
>  	for (j = 3; j < total_ctrs; j++)
>  		active_events[hartid][j] = SBI_PMU_EVENT_IDX_INVALID;
> -	for (j = 0; j < SBI_PMU_FW_CTR_MAX; j++)
> +	for (j = 0; j < SBI_PMU_FW_EVENT_MAX; j++)
>  		sbi_memset(&fw_event_map[hartid][j], 0,
>  			   sizeof(struct sbi_pmu_fw_event));
>  }
> -- 
> 2.25.1
>

Reviewed-by: Andrew Jones <ajones at ventanamicro.com>


From wxjstz at 126.com  Wed Jul 20 08:12:18 2022
From: wxjstz at 126.com (Xiang W)
Date: Wed, 20 Jul 2022 23:12:18 +0800
Subject: [PATCH 4/4] lib: utils/serial: Ensure baudrate is non-zero
 before using
In-Reply-To: <20220718172028.2006166-5-ajones@ventanamicro.com>
References: <20220718172028.2006166-1-ajones@ventanamicro.com>
	 <20220718172028.2006166-5-ajones@ventanamicro.com>
Message-ID: <a2f95ffcea25b6da6270c509e0dc7e5c64be7e92.camel@126.com>

? 2022-07-18???? 19:20 +0200?Andrew Jones???
> RISC-V doesn't generate exceptions on divide-by-zero, but the result,
> all bits set, is not likely what people expect either. In all cases
> where we divide by baudrate there's a chance it's zero (when the DT
> it came from is "bad"). To avoid difficult to debug situations, leave
> baudrate dependent registers alone when baudrate is zero, as, also in
> all cases, it appears we can skip initialization of those registers
> and still [hopefully] have a functioning UART.
> 
> Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
If the parameter is wrong we should skip initialization and should not
execute sbi_console_set_device.

Regards,
Xiang W
> ---
> ?lib/utils/serial/gaisler-uart.c | 2 +-
> ?lib/utils/serial/shakti-uart.c? | 8 ++++++--
> ?lib/utils/serial/sifive-uart.c? | 2 +-
> ?lib/utils/serial/uart8250.c???? | 7 +++++--
> ?4 files changed, 13 insertions(+), 6 deletions(-)
> 
> diff --git a/lib/utils/serial/gaisler-uart.c b/lib/utils/serial/gaisler-uart.c
> index 5f30ee43b719..eec75cca0c98 100644
> --- a/lib/utils/serial/gaisler-uart.c
> +++ b/lib/utils/serial/gaisler-uart.c
> @@ -70,7 +70,7 @@ int gaisler_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
> ????????uart_base = (volatile char *)base;
> ?
> ????????/* Configure baudrate */
> -???????if (in_freq)
> +???????if (in_freq && baudrate)
> ????????????????set_reg(UART_REG_SCALER, in_freq / (baudrate * 8 + 7));
> ?
> ????????ctrl = get_reg(UART_REG_CTRL);
> diff --git a/lib/utils/serial/shakti-uart.c b/lib/utils/serial/shakti-uart.c
> index 5f2fe75c9f33..2f09f141b9cf 100644
> --- a/lib/utils/serial/shakti-uart.c
> +++ b/lib/utils/serial/shakti-uart.c
> @@ -47,8 +47,12 @@ static struct sbi_console_device shakti_console = {
> ?int shakti_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
> ?{
> ????????uart_base = (volatile char *)base;
> -???????u16 baud = (u16)(in_freq/(16 * baudrate));
> -???????writew(baud, uart_base + REG_BAUD);
> +???????u16 baud;
> +
> +???????if (baudrate) {
> +???????????????baud = (u16)(in_freq / (16 * baudrate));
> +???????????????writew(baud, uart_base + REG_BAUD);
> +???????}
> ?
> ????????sbi_console_set_device(&shakti_console);
> ?
> diff --git a/lib/utils/serial/sifive-uart.c b/lib/utils/serial/sifive-uart.c
> index 7078611a5274..3581d47a6d4b 100644
> --- a/lib/utils/serial/sifive-uart.c
> +++ b/lib/utils/serial/sifive-uart.c
> @@ -97,7 +97,7 @@ int sifive_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
> ????????uart_baudrate = baudrate;
> ?
> ????????/* Configure baudrate */
> -???????if (in_freq)
> +???????if (in_freq && baudrate)
> ????????????????set_reg(UART_REG_DIV, uart_min_clk_divisor(in_freq, baudrate));
> ?
> ????????/* Disable interrupts */
> diff --git a/lib/utils/serial/uart8250.c b/lib/utils/serial/uart8250.c
> index 38ea11af4d31..99bf1bf733f2 100644
> --- a/lib/utils/serial/uart8250.c
> +++ b/lib/utils/serial/uart8250.c
> @@ -93,7 +93,7 @@ static struct sbi_console_device uart8250_console = {
> ?int uart8250_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift,
> ????????????????? u32 reg_width, u32 reg_offset)
> ?{
> -???????u16 bdiv;
> +???????u16 bdiv = 0;
> ?
> ????????uart8250_base????? = (volatile char *)base + reg_offset;
> ????????uart8250_reg_shift = reg_shift;
> @@ -101,7 +101,10 @@ int uart8250_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift,
> ????????uart8250_in_freq?? = in_freq;
> ????????uart8250_baudrate? = baudrate;
> ?
> -???????bdiv = (uart8250_in_freq + 8 * uart8250_baudrate) / (16 * uart8250_baudrate);
> +???????if (uart8250_baudrate) {
> +???????????????bdiv = (uart8250_in_freq + 8 * uart8250_baudrate) /
> +????????????????????? (16 * uart8250_baudrate);
> +???????}
> ?
> ????????/* Disable all interrupts */
> ????????set_reg(UART_IER_OFFSET, 0x00);
> -- 
> 2.36.1
> 
> 




From wxjstz at 126.com  Wed Jul 20 08:13:32 2022
From: wxjstz at 126.com (Xiang W)
Date: Wed, 20 Jul 2022 23:13:32 +0800
Subject: [PATCH 3/4] lib: serial: Clean up coding style in sifive-uart.c
In-Reply-To: <20220718172028.2006166-4-ajones@ventanamicro.com>
References: <20220718172028.2006166-1-ajones@ventanamicro.com>
	 <20220718172028.2006166-4-ajones@ventanamicro.com>
Message-ID: <bf6075cfca33cb1ad4971d9ce37da26c59276e23.camel@126.com>

? 2022-07-18???? 19:20 +0200?Andrew Jones???
> Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
Look good to me.

Reviewed-by: Xiang W <wxjstz at 126.com>
> ---
> ?lib/utils/serial/sifive-uart.c | 11 ++++++++---
> ?1 file changed, 8 insertions(+), 3 deletions(-)
> 
> diff --git a/lib/utils/serial/sifive-uart.c b/lib/utils/serial/sifive-uart.c
> index 9478a77f8054..7078611a5274 100644
> --- a/lib/utils/serial/sifive-uart.c
> +++ b/lib/utils/serial/sifive-uart.c
> @@ -48,12 +48,12 @@ static inline unsigned int uart_min_clk_divisor(uint64_t in_freq,
> ????????????????????????????????????????????????uint64_t max_target_hz)
> ?{
> ????????uint64_t quotient = (in_freq + max_target_hz - 1) / (max_target_hz);
> +
> ????????/* Avoid underflow */
> -???????if (quotient == 0) {
> +???????if (quotient == 0)
> ????????????????return 0;
> -???????} else {
> +???????else
> ????????????????return quotient - 1;
> -???????}
> ?}
> ?
> ?static u32 get_reg(u32 num)
> @@ -77,8 +77,10 @@ static void sifive_uart_putc(char ch)
> ?static int sifive_uart_getc(void)
> ?{
> ????????u32 ret = get_reg(UART_REG_RXFIFO);
> +
> ????????if (!(ret & UART_RXFIFO_EMPTY))
> ????????????????return ret & UART_RXFIFO_DATA;
> +
> ????????return -1;
> ?}
> ?
> @@ -97,10 +99,13 @@ int sifive_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
> ????????/* Configure baudrate */
> ????????if (in_freq)
> ????????????????set_reg(UART_REG_DIV, uart_min_clk_divisor(in_freq, baudrate));
> +
> ????????/* Disable interrupts */
> ????????set_reg(UART_REG_IE, 0);
> +
> ????????/* Enable TX */
> ????????set_reg(UART_REG_TXCTRL, UART_TXCTRL_TXEN);
> +
> ????????/* Enable Rx */
> ????????set_reg(UART_REG_RXCTRL, UART_RXCTRL_RXEN);
> ?
> -- 
> 2.36.1
> 
> 




From wxjstz at 126.com  Wed Jul 20 09:01:09 2022
From: wxjstz at 126.com (Xiang W)
Date: Thu, 21 Jul 2022 00:01:09 +0800
Subject: [PATCH v3 02/15] Makefile: Add initial kconfig support for each
 platform
In-Reply-To: <20220719131837.2389630-3-apatel@ventanamicro.com>
References: <20220719131837.2389630-1-apatel@ventanamicro.com>
	 <20220719131837.2389630-3-apatel@ventanamicro.com>
Message-ID: <740b3657b3fb01db3189a468f6aedaf94e0d9a9d.camel@126.com>

? 2022-07-19???? 18:48 +0530?Anup Patel???
> We extend the top-level makefile to allow kconfig based configuration
> for each platform where each platform has it's own set of configs with
> "defconfig" being the default config.
> 
> Signed-off-by: Anup Patel <apatel at ventanamicro.com>
> ---
> ?Kconfig?????????????????????????????????? | 23 ++++++++++
> ?Makefile????????????????????????????????? | 56 +++++++++++++++++++++--
> ?firmware/Kconfig????????????????????????? |? 1 +
> ?lib/utils/Kconfig???????????????????????? |? 1 +
> ?platform/andes/ae350/Kconfig????????????? |? 5 ++
> ?platform/andes/ae350/configs/defconfig??? |? 0
> ?platform/fpga/ariane/Kconfig????????????? |? 5 ++
> ?platform/fpga/ariane/configs/defconfig??? |? 0
> ?platform/fpga/openpiton/Kconfig?????????? |? 5 ++
> ?platform/fpga/openpiton/configs/defconfig |? 0
> ?platform/generic/Kconfig????????????????? |? 5 ++
> ?platform/generic/configs/defconfig??????? |? 0
> ?platform/kendryte/k210/Kconfig??????????? |? 5 ++
> ?platform/kendryte/k210/configs/defconfig? |? 0
> ?platform/nuclei/ux600/Kconfig???????????? |? 5 ++
> ?platform/nuclei/ux600/configs/defconfig?? |? 0
> ?platform/template/Kconfig???????????????? |? 5 ++
> ?platform/template/configs/defconfig?????? |? 0
> ?18 files changed, 113 insertions(+), 3 deletions(-)
> ?create mode 100644 Kconfig
> ?create mode 100644 firmware/Kconfig
> ?create mode 100644 lib/utils/Kconfig
> ?create mode 100644 platform/andes/ae350/Kconfig
> ?create mode 100644 platform/andes/ae350/configs/defconfig
> ?create mode 100644 platform/fpga/ariane/Kconfig
> ?create mode 100644 platform/fpga/ariane/configs/defconfig
> ?create mode 100644 platform/fpga/openpiton/Kconfig
> ?create mode 100644 platform/fpga/openpiton/configs/defconfig
> ?create mode 100644 platform/generic/Kconfig
> ?create mode 100644 platform/generic/configs/defconfig
> ?create mode 100644 platform/kendryte/k210/Kconfig
> ?create mode 100644 platform/kendryte/k210/configs/defconfig
> ?create mode 100644 platform/nuclei/ux600/Kconfig
> ?create mode 100644 platform/nuclei/ux600/configs/defconfig
> ?create mode 100644 platform/template/Kconfig
> ?create mode 100644 platform/template/configs/defconfig
> 
> diff --git a/Kconfig b/Kconfig
> new file mode 100644
> index 0000000..b3213a9
> --- /dev/null
> +++ b/Kconfig
> @@ -0,0 +1,23 @@
> +# SPDX-License-Identifier: BSD-2-Clause
> +
> +mainmenu "OpenSBI $(OPENSBI_PLATFORM) Configuration"
> +
> +config OPENSBI_SRC_DIR
> +???????string
> +???????option env="OPENSBI_SRC_DIR"
> +
> +config OPENSBI_PLATFORM
> +???????string
> +???????option env="OPENSBI_PLATFORM"
> +
> +config OPENSBI_PLATFORM_SRC_DIR
> +???????string
> +???????option env="OPENSBI_PLATFORM_SRC_DIR"
> +
> +menu "Platform Options"
> +source "$(OPENSBI_PLATFORM_SRC_DIR)/Kconfig"
> +endmenu
> +
> +source "$(OPENSBI_SRC_DIR)/lib/utils/Kconfig"
> +
> +source "$(OPENSBI_SRC_DIR)/firmware/Kconfig"
> diff --git a/Makefile b/Makefile
> index f619ef7..c13495c 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -52,6 +52,9 @@ ifdef PLATFORM_DIR
> ?else
> ? platform_parent_dir=$(src_dir)/platform
> ?endif
> +ifndef PLATFORM_DEFCONFIG
> +PLATFORM_DEFCONFIG=defconfig
> +endif
> ?
> ?# Check if verbosity is ON for build process
> ?CMD_PREFIX_DEFAULT := @
> @@ -70,6 +73,20 @@ export libsbi_dir=$(CURDIR)/lib/sbi
> ?export libsbiutils_dir=$(CURDIR)/lib/utils
> ?export firmware_dir=$(CURDIR)/firmware
> ?
> +# Setup variables for kconfig
> +ifdef PLATFORM
> +export PYTHONDONTWRITEBYTECODE=1.
There is an extra dot at the end of the line.

Regards,
Xiang W
> +export KCONFIG_DIR=$(platform_build_dir)/kconfig
> +export KCONFIG_AUTOLIST=$(KCONFIG_DIR)/auto.list
> +export KCONFIG_AUTOHEADER=$(KCONFIG_DIR)/autoconf.h
> +export KCONFIG_AUTOCMD=$(KCONFIG_DIR)/auto.conf.cmd
> +export KCONFIG_CONFIG=$(KCONFIG_DIR)/.config
> +# Additional exports for include paths in Kconfig files
> +export OPENSBI_SRC_DIR=$(src_dir)
> +export OPENSBI_PLATFORM=$(PLATFORM)
> +export OPENSBI_PLATFORM_SRC_DIR=$(platform_src_dir)
> +endif
> +
> ?# Find library version
> ?OPENSBI_VERSION_MAJOR=`grep "define OPENSBI_VERSION_MAJOR" $(include_dir)/sbi/sbi_version.h | sed 's/.*MAJOR.*\([0-9][0-9]*\)/\1/'`
> ?OPENSBI_VERSION_MINOR=`grep "define OPENSBI_VERSION_MINOR" $(include_dir)/sbi/sbi_version.h | sed 's/.*MINOR.*\([0-9][0-9]*\)/\1/'`
> @@ -183,8 +200,35 @@ libsbi-object-mks=$(shell if [ -d $(libsbi_dir) ]; then find $(libsbi_dir) -inam
> ?libsbiutils-object-mks=$(shell if [ -d $(libsbiutils_dir) ]; then find $(libsbiutils_dir) -iname "objects.mk" | sort -r; fi)
> ?firmware-object-mks=$(shell if [ -d $(firmware_dir) ]; then find $(firmware_dir) -iname "objects.mk" | sort -r; fi)
> ?
> -# Include platform specifig config.mk
> +# The "make all" rule should always be first rule
> +.PHONY: all
> +all:
> +
> +# Include platform specific .config and config.mk
> ?ifdef PLATFORM
> +.PHONY: menuconfig
> +menuconfig: $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
> +???????$(CMD_PREFIX)mkdir -p $(KCONFIG_DIR)
> +???????$(CMD_PREFIX)$(src_dir)/scripts/Kconfiglib/menuconfig.py $(src_dir)/Kconfig
> +
> +.PHONY: savedefconfig
> +savedefconfig: $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
> +???????$(CMD_PREFIX)mkdir -p $(KCONFIG_DIR)
> +???????$(CMD_PREFIX)$(src_dir)/scripts/Kconfiglib/savedefconfig.py --kconfig $(src_dir)/Kconfig --out $(KCONFIG_DIR)/defconfig
> +
> +$(KCONFIG_CONFIG): $(platform_src_dir)/configs/$(PLATFORM_DEFCONFIG) $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
> +???????$(CMD_PREFIX)mkdir -p $(KCONFIG_DIR)
> +???????$(CMD_PREFIX)$(src_dir)/scripts/Kconfiglib/defconfig.py --kconfig $(src_dir)/Kconfig $(platform_src_dir)/configs/$(PLATFORM_DEFCONFIG)
> +???????$(CMD_PREFIX)$(src_dir)/scripts/Kconfiglib/genconfig.py --header-path $(KCONFIG_AUTOHEADER) --sync-deps $(KCONFIG_DIR) --file-list
> $(KCONFIG_AUTOLIST) $(src_dir)/Kconfig
> +
> +$(KCONFIG_AUTOCMD): $(KCONFIG_CONFIG)
> +???????$(CMD_PREFIX)mkdir -p $(KCONFIG_DIR)
> +???????$(CMD_PREFIX)echo -n "$(KCONFIG_CONFIG): " > $(KCONFIG_AUTOCMD)
> +???????$(CMD_PREFIX)cat $(KCONFIG_AUTOLIST) | tr '\n' ' ' >> $(KCONFIG_AUTOCMD)
> +
> +include $(KCONFIG_CONFIG)
> +include $(KCONFIG_AUTOCMD)
> +
> ?include $(platform_src_dir)/config.mk
> ?endif
> ?
> @@ -280,6 +324,9 @@ ifeq ($(BUILD_INFO),y)
> ?GENFLAGS???????+=??????-DOPENSBI_BUILD_TIME_STAMP="\"$(OPENSBI_BUILD_TIME_STAMP)\""
> ?GENFLAGS???????+=??????-DOPENSBI_BUILD_COMPILER_VERSION="\"$(OPENSBI_BUILD_COMPILER_VERSION)\""
> ?endif
> +ifdef PLATFORM
> +GENFLAGS???????+=??????-include $(KCONFIG_AUTOHEADER)
> +endif
> ?GENFLAGS???????+=??????$(libsbiutils-genflags-y)
> ?GENFLAGS???????+=??????$(platform-genflags-y)
> ?GENFLAGS???????+=??????$(firmware-genflags-y)
> @@ -419,7 +466,7 @@ targets-y += $(platform_build_dir)/lib/libplatsbi.a
> ?endif
> ?targets-y += $(firmware-bins-path-y)
> ?
> -# Default rule "make" should always be first rule
> +# The default "make all" rule
> ?.PHONY: all
> ?all: $(targets-y)
> ?
> @@ -456,7 +503,7 @@ $(build_dir)/%.o: $(src_dir)/%.S
> ????????$(call compile_as,$@,$<)
> ?
> ?$(build_dir)/%.dep: $(src_dir)/%.carray
> -???????$(call compile_gen_dep,$@,.c,$<)
> +???????$(call compile_gen_dep,$@,.c,$< $(KCONFIG_CONFIG))
> ????????$(call compile_gen_dep,$@,.o,$(@:.dep=.c))
> ?
> ?$(build_dir)/%.c: $(src_dir)/%.carray
> @@ -523,6 +570,9 @@ docs: $(build_dir)/docs/latex/refman.pdf
> ?# They should not be included for any "xxxconfig" or "xxxclean" rule
> ?all-deps-1 = $(if $(findstring config,$(MAKECMDGOALS)),,$(deps-y))
> ?all-deps-2 = $(if $(findstring clean,$(MAKECMDGOALS)),,$(all-deps-1))
> +ifdef PLATFORM
> +$(all-deps-2): | $(KCONFIG_CONFIG)
> +endif
> ?-include $(all-deps-2)
> ?
> ?# Include external dependency of firmwares after default Makefile rules
> diff --git a/firmware/Kconfig b/firmware/Kconfig
> new file mode 100644
> index 0000000..d6e0506
> --- /dev/null
> +++ b/firmware/Kconfig
> @@ -0,0 +1 @@
> +# SPDX-License-Identifier: BSD-2-Clause
> diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
> new file mode 100644
> index 0000000..d6e0506
> --- /dev/null
> +++ b/lib/utils/Kconfig
> @@ -0,0 +1 @@
> +# SPDX-License-Identifier: BSD-2-Clause
> diff --git a/platform/andes/ae350/Kconfig b/platform/andes/ae350/Kconfig
> new file mode 100644
> index 0000000..4b73c76
> --- /dev/null
> +++ b/platform/andes/ae350/Kconfig
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: BSD-2-Clause
> +
> +config PLATFORM_ANDES_AE350
> +???????bool
> +???????default y
> diff --git a/platform/andes/ae350/configs/defconfig b/platform/andes/ae350/configs/defconfig
> new file mode 100644
> index 0000000..e69de29
> diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
> new file mode 100644
> index 0000000..70d2004
> --- /dev/null
> +++ b/platform/fpga/ariane/Kconfig
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: BSD-2-Clause
> +
> +config PLATFORM_ARIANE_FPGA
> +???????bool
> +???????default y
> diff --git a/platform/fpga/ariane/configs/defconfig b/platform/fpga/ariane/configs/defconfig
> new file mode 100644
> index 0000000..e69de29
> diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
> new file mode 100644
> index 0000000..81e4b76
> --- /dev/null
> +++ b/platform/fpga/openpiton/Kconfig
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: BSD-2-Clause
> +
> +config PLATFORM_OPENPITON_FPGA
> +???????bool
> +???????default y
> diff --git a/platform/fpga/openpiton/configs/defconfig b/platform/fpga/openpiton/configs/defconfig
> new file mode 100644
> index 0000000..e69de29
> diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
> new file mode 100644
> index 0000000..3eab282
> --- /dev/null
> +++ b/platform/generic/Kconfig
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: BSD-2-Clause
> +
> +config PLATFORM_GENERIC
> +???????bool
> +???????default y
> diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
> new file mode 100644
> index 0000000..e69de29
> diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
> new file mode 100644
> index 0000000..889f16b
> --- /dev/null
> +++ b/platform/kendryte/k210/Kconfig
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: BSD-2-Clause
> +
> +config PLATFORM_KENDRYTE_K210
> +???????bool
> +???????default y
> diff --git a/platform/kendryte/k210/configs/defconfig b/platform/kendryte/k210/configs/defconfig
> new file mode 100644
> index 0000000..e69de29
> diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
> new file mode 100644
> index 0000000..c368ef4
> --- /dev/null
> +++ b/platform/nuclei/ux600/Kconfig
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: BSD-2-Clause
> +
> +config PLATFORM_NUCLEI_UX600
> +???????bool
> +???????default y
> diff --git a/platform/nuclei/ux600/configs/defconfig b/platform/nuclei/ux600/configs/defconfig
> new file mode 100644
> index 0000000..e69de29
> diff --git a/platform/template/Kconfig b/platform/template/Kconfig
> new file mode 100644
> index 0000000..b9c4350
> --- /dev/null
> +++ b/platform/template/Kconfig
> @@ -0,0 +1,5 @@
> +# SPDX-License-Identifier: BSD-2-Clause
> +
> +config PLATFORM_TEMPLATE
> +???????bool
> +???????default y
> diff --git a/platform/template/configs/defconfig b/platform/template/configs/defconfig
> new file mode 100644
> index 0000000..e69de29
> -- 
> 2.34.1
> 
> 




From ajones at ventanamicro.com  Wed Jul 20 10:16:19 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Wed, 20 Jul 2022 19:16:19 +0200
Subject: [PATCH 4/4] lib: utils/serial: Ensure baudrate is non-zero
 before using
In-Reply-To: <a2f95ffcea25b6da6270c509e0dc7e5c64be7e92.camel@126.com>
References: <20220718172028.2006166-1-ajones@ventanamicro.com>
 <20220718172028.2006166-5-ajones@ventanamicro.com>
 <a2f95ffcea25b6da6270c509e0dc7e5c64be7e92.camel@126.com>
Message-ID: <20220720171619.rmaryfe3en2lftko@kamzik>

On Wed, Jul 20, 2022 at 11:12:18PM +0800, Xiang W wrote:
> ? 2022-07-18???? 19:20 +0200?Andrew Jones???
> > RISC-V doesn't generate exceptions on divide-by-zero, but the result,
> > all bits set, is not likely what people expect either. In all cases
> > where we divide by baudrate there's a chance it's zero (when the DT
> > it came from is "bad"). To avoid difficult to debug situations, leave
> > baudrate dependent registers alone when baudrate is zero, as, also in
> > all cases, it appears we can skip initialization of those registers
> > and still [hopefully] have a functioning UART.
> > 
> > Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
> If the parameter is wrong we should skip initialization and should not
> execute sbi_console_set_device.

Hi Xiang,

While there should be some DT validation somewhere that points out bad
parameters, I don't think opensbi is the best place to do it, especially
for UART configuration. While we certainly can't progress in all cases
we detect bad input, in the case of a zero baudrate I believe we can, as
it appears setting baudrate registers is typically optional for UARTs.

We could also consider adding a log buffer which early init can write
errors and warnings to. After the console has been initialized (with
our best effort) we can then output all the errors and warnings from the
log buffer. If we had something like that, then we could add a warning
about the baudrate to that log along with warnings/errors for any other
DT parameters we fail to validate but attempt to work around.

Thanks,
drew

> 
> Regards,
> Xiang W
> > ---
> > ?lib/utils/serial/gaisler-uart.c | 2 +-
> > ?lib/utils/serial/shakti-uart.c? | 8 ++++++--
> > ?lib/utils/serial/sifive-uart.c? | 2 +-
> > ?lib/utils/serial/uart8250.c???? | 7 +++++--
> > ?4 files changed, 13 insertions(+), 6 deletions(-)
> > 
> > diff --git a/lib/utils/serial/gaisler-uart.c b/lib/utils/serial/gaisler-uart.c
> > index 5f30ee43b719..eec75cca0c98 100644
> > --- a/lib/utils/serial/gaisler-uart.c
> > +++ b/lib/utils/serial/gaisler-uart.c
> > @@ -70,7 +70,7 @@ int gaisler_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
> > ????????uart_base = (volatile char *)base;
> > ?
> > ????????/* Configure baudrate */
> > -???????if (in_freq)
> > +???????if (in_freq && baudrate)
> > ????????????????set_reg(UART_REG_SCALER, in_freq / (baudrate * 8 + 7));
> > ?
> > ????????ctrl = get_reg(UART_REG_CTRL);
> > diff --git a/lib/utils/serial/shakti-uart.c b/lib/utils/serial/shakti-uart.c
> > index 5f2fe75c9f33..2f09f141b9cf 100644
> > --- a/lib/utils/serial/shakti-uart.c
> > +++ b/lib/utils/serial/shakti-uart.c
> > @@ -47,8 +47,12 @@ static struct sbi_console_device shakti_console = {
> > ?int shakti_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
> > ?{
> > ????????uart_base = (volatile char *)base;
> > -???????u16 baud = (u16)(in_freq/(16 * baudrate));
> > -???????writew(baud, uart_base + REG_BAUD);
> > +???????u16 baud;
> > +
> > +???????if (baudrate) {
> > +???????????????baud = (u16)(in_freq / (16 * baudrate));
> > +???????????????writew(baud, uart_base + REG_BAUD);
> > +???????}
> > ?
> > ????????sbi_console_set_device(&shakti_console);
> > ?
> > diff --git a/lib/utils/serial/sifive-uart.c b/lib/utils/serial/sifive-uart.c
> > index 7078611a5274..3581d47a6d4b 100644
> > --- a/lib/utils/serial/sifive-uart.c
> > +++ b/lib/utils/serial/sifive-uart.c
> > @@ -97,7 +97,7 @@ int sifive_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
> > ????????uart_baudrate = baudrate;
> > ?
> > ????????/* Configure baudrate */
> > -???????if (in_freq)
> > +???????if (in_freq && baudrate)
> > ????????????????set_reg(UART_REG_DIV, uart_min_clk_divisor(in_freq, baudrate));
> > ?
> > ????????/* Disable interrupts */
> > diff --git a/lib/utils/serial/uart8250.c b/lib/utils/serial/uart8250.c
> > index 38ea11af4d31..99bf1bf733f2 100644
> > --- a/lib/utils/serial/uart8250.c
> > +++ b/lib/utils/serial/uart8250.c
> > @@ -93,7 +93,7 @@ static struct sbi_console_device uart8250_console = {
> > ?int uart8250_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift,
> > ????????????????? u32 reg_width, u32 reg_offset)
> > ?{
> > -???????u16 bdiv;
> > +???????u16 bdiv = 0;
> > ?
> > ????????uart8250_base????? = (volatile char *)base + reg_offset;
> > ????????uart8250_reg_shift = reg_shift;
> > @@ -101,7 +101,10 @@ int uart8250_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift,
> > ????????uart8250_in_freq?? = in_freq;
> > ????????uart8250_baudrate? = baudrate;
> > ?
> > -???????bdiv = (uart8250_in_freq + 8 * uart8250_baudrate) / (16 * uart8250_baudrate);
> > +???????if (uart8250_baudrate) {
> > +???????????????bdiv = (uart8250_in_freq + 8 * uart8250_baudrate) /
> > +????????????????????? (16 * uart8250_baudrate);
> > +???????}
> > ?
> > ????????/* Disable all interrupts */
> > ????????set_reg(UART_IER_OFFSET, 0x00);
> > -- 
> > 2.36.1
> > 
> > 
> 
> 
> 
> -- 
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi


From wxjstz at 126.com  Wed Jul 20 10:25:44 2022
From: wxjstz at 126.com (Xiang W)
Date: Thu, 21 Jul 2022 01:25:44 +0800
Subject: [PATCH 4/4] lib: utils/serial: Ensure baudrate is non-zero
 before using
In-Reply-To: <20220720171619.rmaryfe3en2lftko@kamzik>
References: <20220718172028.2006166-1-ajones@ventanamicro.com>
	 <20220718172028.2006166-5-ajones@ventanamicro.com>
	 <a2f95ffcea25b6da6270c509e0dc7e5c64be7e92.camel@126.com>
	 <20220720171619.rmaryfe3en2lftko@kamzik>
Message-ID: <70f832b65f372623a87098c501e08c0e50b11b4b.camel@126.com>

? 2022-07-20???? 19:16 +0200?Andrew Jones???
> On Wed, Jul 20, 2022 at 11:12:18PM +0800, Xiang W wrote:
> > ? 2022-07-18???? 19:20 +0200?Andrew Jones???
> > > RISC-V doesn't generate exceptions on divide-by-zero, but the result,
> > > all bits set, is not likely what people expect either. In all cases
> > > where we divide by baudrate there's a chance it's zero (when the DT
> > > it came from is "bad"). To avoid difficult to debug situations, leave
> > > baudrate dependent registers alone when baudrate is zero, as, also in
> > > all cases, it appears we can skip initialization of those registers
> > > and still [hopefully] have a functioning UART.
> > > 
> > > Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
> > If the parameter is wrong we should skip initialization and should not
> > execute sbi_console_set_device.
> 
> Hi Xiang,
> 
> While there should be some DT validation somewhere that points out bad
> parameters, I don't think opensbi is the best place to do it, especially
> for UART configuration. While we certainly can't progress in all cases
> we detect bad input, in the case of a zero baudrate I believe we can, as
> it appears setting baudrate registers is typically optional for UARTs.
> 
> We could also consider adding a log buffer which early init can write
> errors and warnings to. After the console has been initialized (with
> our best effort) we can then output all the errors and warnings from the
> log buffer. If we had something like that, then we could add a warning
> about the baudrate to that log along with warnings/errors for any other
> DT parameters we fail to validate but attempt to work around.
> 
> Thanks,
> drew
I mean:

int xxx_uart_init(...)
{
	if (baudrate == 0)
		return 0;
	...
	// console_dev should not be set because initialization failed
}

> 
> > 
> > Regards,
> > Xiang W
> > > ---
> > > ?lib/utils/serial/gaisler-uart.c | 2 +-
> > > ?lib/utils/serial/shakti-uart.c? | 8 ++++++--
> > > ?lib/utils/serial/sifive-uart.c? | 2 +-
> > > ?lib/utils/serial/uart8250.c???? | 7 +++++--
> > > ?4 files changed, 13 insertions(+), 6 deletions(-)
> > > 
> > > diff --git a/lib/utils/serial/gaisler-uart.c b/lib/utils/serial/gaisler-uart.c
> > > index 5f30ee43b719..eec75cca0c98 100644
> > > --- a/lib/utils/serial/gaisler-uart.c
> > > +++ b/lib/utils/serial/gaisler-uart.c
> > > @@ -70,7 +70,7 @@ int gaisler_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
> > > ????????uart_base = (volatile char *)base;
> > > ?
> > > ????????/* Configure baudrate */
> > > -???????if (in_freq)
> > > +???????if (in_freq && baudrate)
> > > ????????????????set_reg(UART_REG_SCALER, in_freq / (baudrate * 8 + 7));
> > > ?
> > > ????????ctrl = get_reg(UART_REG_CTRL);
> > > diff --git a/lib/utils/serial/shakti-uart.c b/lib/utils/serial/shakti-uart.c
> > > index 5f2fe75c9f33..2f09f141b9cf 100644
> > > --- a/lib/utils/serial/shakti-uart.c
> > > +++ b/lib/utils/serial/shakti-uart.c
> > > @@ -47,8 +47,12 @@ static struct sbi_console_device shakti_console = {
> > > ?int shakti_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
> > > ?{
> > > ????????uart_base = (volatile char *)base;
> > > -???????u16 baud = (u16)(in_freq/(16 * baudrate));
> > > -???????writew(baud, uart_base + REG_BAUD);
> > > +???????u16 baud;
> > > +
> > > +???????if (baudrate) {
> > > +???????????????baud = (u16)(in_freq / (16 * baudrate));
> > > +???????????????writew(baud, uart_base + REG_BAUD);
> > > +???????}
> > > ?
> > > ????????sbi_console_set_device(&shakti_console);
> > > ?
> > > diff --git a/lib/utils/serial/sifive-uart.c b/lib/utils/serial/sifive-uart.c
> > > index 7078611a5274..3581d47a6d4b 100644
> > > --- a/lib/utils/serial/sifive-uart.c
> > > +++ b/lib/utils/serial/sifive-uart.c
> > > @@ -97,7 +97,7 @@ int sifive_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
> > > ????????uart_baudrate = baudrate;
> > > ?
> > > ????????/* Configure baudrate */
> > > -???????if (in_freq)
> > > +???????if (in_freq && baudrate)
> > > ????????????????set_reg(UART_REG_DIV, uart_min_clk_divisor(in_freq, baudrate));
> > > ?
> > > ????????/* Disable interrupts */
> > > diff --git a/lib/utils/serial/uart8250.c b/lib/utils/serial/uart8250.c
> > > index 38ea11af4d31..99bf1bf733f2 100644
> > > --- a/lib/utils/serial/uart8250.c
> > > +++ b/lib/utils/serial/uart8250.c
> > > @@ -93,7 +93,7 @@ static struct sbi_console_device uart8250_console = {
> > > ?int uart8250_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift,
> > > ????????????????? u32 reg_width, u32 reg_offset)
> > > ?{
> > > -???????u16 bdiv;
> > > +???????u16 bdiv = 0;
> > > ?
> > > ????????uart8250_base????? = (volatile char *)base + reg_offset;
> > > ????????uart8250_reg_shift = reg_shift;
> > > @@ -101,7 +101,10 @@ int uart8250_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift,
> > > ????????uart8250_in_freq?? = in_freq;
> > > ????????uart8250_baudrate? = baudrate;
> > > ?
> > > -???????bdiv = (uart8250_in_freq + 8 * uart8250_baudrate) / (16 * uart8250_baudrate);
> > > +???????if (uart8250_baudrate) {
> > > +???????????????bdiv = (uart8250_in_freq + 8 * uart8250_baudrate) /
> > > +????????????????????? (16 * uart8250_baudrate);
> > > +???????}
> > > ?
> > > ????????/* Disable all interrupts */
> > > ????????set_reg(UART_IER_OFFSET, 0x00);
> > > -- 
> > > 2.36.1
> > > 
> > > 
> > 
> > 
> > 
> > -- 
> > opensbi mailing list
> > opensbi at lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/opensbi




From atishp at atishpatra.org  Wed Jul 20 12:37:11 2022
From: atishp at atishpatra.org (Atish Patra)
Date: Wed, 20 Jul 2022 12:37:11 -0700
Subject: [PATCH v2 2/4] lib: sbi: Add the bound check for events during
 config match
In-Reply-To: <20220720102948.ffachuxzcfj5qwfa@kamzik>
References: <20220719224615.3080494-1-atishp@rivosinc.com> <20220719224615.3080494-3-atishp@rivosinc.com>
 <20220720102948.ffachuxzcfj5qwfa@kamzik>
Message-ID: <CAOnJCU+bXxMVLMvzjvCdYZf7TdqWpXQO-Oy=+yzJMR7b=Ce7+A@mail.gmail.com>

On Wed, Jul 20, 2022 at 3:30 AM Andrew Jones <ajones at ventanamicro.com> wrote:
>
> On Tue, Jul 19, 2022 at 03:46:13PM -0700, Atish Patra wrote:
> > Currently, there is no sanity check for firmware event code. We don't see
> > any issue as Linux kernel driver does a bound check on firmware events
> > already. However, OpenSBI can't assume sane supervisor mode software
> > always. Thus, an invalid event idx can cause a buffer overflow error.
> > For hardware events, the match will fail for invalid event code anyways.
> > However, a search is unecessary if event code is invalid.
> >
> > Add a event ID validation function to solve the issue.
> >
> > Signed-off-by: Atish Patra <atishp at rivosinc.com>
> > ---
> >  lib/sbi/sbi_pmu.c | 39 +++++++++++++++++++++++++++++++++++++--
> >  1 file changed, 37 insertions(+), 2 deletions(-)
> >
> > diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
> > index 31631a2fab80..1170cba43b3d 100644
> > --- a/lib/sbi/sbi_pmu.c
> > +++ b/lib/sbi/sbi_pmu.c
> > @@ -107,6 +107,37 @@ static bool pmu_event_select_overlap(struct sbi_pmu_hw_event *evt,
> >       return FALSE;
> >  }
> >
> > +static int pmu_event_validate(unsigned long event_idx)
> > +{
> > +     uint32_t event_idx_type = get_cidx_type(event_idx);
> > +     uint32_t event_idx_code = get_cidx_code(event_idx);
> > +     uint32_t event_idx_code_max = -1;
> > +
> > +     switch(event_idx_type) {
> > +     case SBI_PMU_EVENT_TYPE_HW:
> > +             event_idx_code_max = SBI_PMU_HW_GENERAL_MAX;
> > +             break;
> > +     case SBI_PMU_EVENT_TYPE_FW:
> > +             event_idx_code_max = SBI_PMU_FW_MAX;
> > +             break;
> > +     case SBI_PMU_EVENT_TYPE_HW_CACHE:
> > +             event_idx_code_max = ((SBI_PMU_HW_CACHE_MAX & 0xFFF) << 0x03) |
> > +                                    (SBI_PMU_HW_CACHE_OP_MAX & 0x3) << 0x01 |
> > +                                     0x01;
>
> We need to check these fields independently, otherwise event_idx_code
> could be something like 6, which is less than event_idx_code_max (63),
> but represents a code with an invalid op_id.
>

Yes. I did not add those additional checks initially as this is just
an optimization for hw & hw cache events.
For an invalid event id, config_match will never find a suitable
counter anyways and return failure.

However, I get your point. We should just check independently for
completeness and avoid confusion.
I will fix it in v3.

> > +             break;
> > +     case SBI_PMU_EVENT_TYPE_HW_RAW:
> > +             event_idx_code_max = 1; // event_idx.code should be zero
> > +             break;
> > +     default:
> > +             return SBI_EINVAL;
> > +     }
> > +
> > +     if (event_idx_code < event_idx_code_max)
> > +             return event_idx_type;
> > +     else
>
> nit: no need for the 'else'
>

Thanks. Will fix it.

> > +             return SBI_EINVAL;
> > +}
> > +
> >  static int pmu_ctr_validate(uint32_t cidx, uint32_t *event_idx_code)
> >  {
> >       uint32_t event_idx_val;
> > @@ -614,12 +645,16 @@ int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask,
> >  {
> >       int ctr_idx = SBI_ENOTSUPP;
> >       u32 hartid = current_hartid();
> > -     int event_type = get_cidx_type(event_idx);
> > +     int event_type;
> >       struct sbi_pmu_fw_event *fevent;
> >       uint32_t fw_evt_code;
> >
> >       /* Do a basic sanity check of counter base & mask */
> > -     if ((cidx_base + sbi_fls(cidx_mask)) >= total_ctrs || event_type >= SBI_PMU_EVENT_TYPE_MAX)
> > +     if ((cidx_base + sbi_fls(cidx_mask)) >= total_ctrs)
> > +             return SBI_EINVAL;
> > +
> > +     event_type = pmu_event_validate(event_idx);
> > +     if (event_type < 0)
> >               return SBI_EINVAL;
> >
> >       if (flags & SBI_PMU_CFG_FLAG_SKIP_MATCH) {
> > --
> > 2.25.1
> >
>
> Thanks,
> drew
>
> --
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi



-- 
Regards,
Atish


From atishp at rivosinc.com  Wed Jul 20 14:50:31 2022
From: atishp at rivosinc.com (Atish Patra)
Date: Wed, 20 Jul 2022 14:50:31 -0700
Subject: [PATCH v3  0/4] Miscellaneous PMU fixes
Message-ID: <20220720215035.3453553-1-atishp@rivosinc.com>

This series have a set of smaller PMU related fixes/improvements.
Most of the fixes are around possible buffer overrun errors in case of 
corrupt supervisor software.

Changes from v2->v3:
1. Improved patch 2.
2. Added RB tags.

Changes from v1->v2:
1. Split patch 1 to two patches with further improvements.
2. Added a fix for the issue pointed by Andrew.

Atish Patra (4):
lib: sbi: Fix counter index sanity check
lib: sbi: Add the bound check for events during config match
lib: sbi: Fix possible buffer overrun in counter validation
lib: sbi: Fix fw_event_map initialization

lib/sbi/sbi_pmu.c | 87 +++++++++++++++++++++++++++++++++++------------
1 file changed, 65 insertions(+), 22 deletions(-)

--
2.25.1



From atishp at rivosinc.com  Wed Jul 20 14:50:32 2022
From: atishp at rivosinc.com (Atish Patra)
Date: Wed, 20 Jul 2022 14:50:32 -0700
Subject: [PATCH v3  1/4] lib: sbi: Fix counter index sanity check
In-Reply-To: <20220720215035.3453553-1-atishp@rivosinc.com>
References: <20220720215035.3453553-1-atishp@rivosinc.com>
Message-ID: <20220720215035.3453553-2-atishp@rivosinc.com>

The current implementation computes the possible counter range
by doing a left shift of counter base. However, this may overflow depending
on the counter base value. In case of overflow, the highest counter id
may be computed incorrectly. As per the SBI specification, the respective
function should return an error if any of the counter is not valid.

Fix the counter index check by avoiding left shifting while doing the
sanity checks. Without the shift, the implementation just iterates
over the counter mask and computes the correct counter index by adding
the base to it.

Reviewed-by: Andrew Jones <ajones at ventanamicro.com>
Signed-off-by: Atish Patra <atishp at rivosinc.com>
---
 lib/sbi/sbi_pmu.c | 33 +++++++++++++++++----------------
 1 file changed, 17 insertions(+), 16 deletions(-)

diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
index 3f5fd1031b25..31631a2fab80 100644
--- a/lib/sbi/sbi_pmu.c
+++ b/lib/sbi/sbi_pmu.c
@@ -343,25 +343,26 @@ int sbi_pmu_ctr_start(unsigned long cbase, unsigned long cmask,
 {
 	int event_idx_type;
 	uint32_t event_code;
-	unsigned long ctr_mask = cmask << cbase;
 	int ret = SBI_EINVAL;
 	bool bUpdate = FALSE;
+	int i, cidx;
 
-	if (sbi_fls(ctr_mask) >= total_ctrs)
+	if ((cbase + sbi_fls(cmask)) >= total_ctrs)
 		return ret;
 
 	if (flags & SBI_PMU_START_FLAG_SET_INIT_VALUE)
 		bUpdate = TRUE;
 
-	for_each_set_bit_from(cbase, &ctr_mask, total_ctrs) {
-		event_idx_type = pmu_ctr_validate(cbase, &event_code);
+	for_each_set_bit(i, &cmask, total_ctrs) {
+		cidx = i + cbase;
+		event_idx_type = pmu_ctr_validate(cidx, &event_code);
 		if (event_idx_type < 0)
 			/* Continue the start operation for other counters */
 			continue;
 		else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW)
-			ret = pmu_ctr_start_fw(cbase, event_code, ival, bUpdate);
+			ret = pmu_ctr_start_fw(cidx, event_code, ival, bUpdate);
 		else
-			ret = pmu_ctr_start_hw(cbase, ival, bUpdate);
+			ret = pmu_ctr_start_hw(cidx, ival, bUpdate);
 	}
 
 	return ret;
@@ -421,25 +422,26 @@ int sbi_pmu_ctr_stop(unsigned long cbase, unsigned long cmask,
 	int ret = SBI_EINVAL;
 	int event_idx_type;
 	uint32_t event_code;
-	unsigned long ctr_mask = cmask << cbase;
+	int i, cidx;
 
-	if (sbi_fls(ctr_mask) >= total_ctrs)
+	if ((cbase + sbi_fls(cmask)) >= total_ctrs)
 		return SBI_EINVAL;
 
-	for_each_set_bit_from(cbase, &ctr_mask, total_ctrs) {
-		event_idx_type = pmu_ctr_validate(cbase, &event_code);
+	for_each_set_bit(i, &cmask, total_ctrs) {
+		cidx = i + cbase;
+		event_idx_type = pmu_ctr_validate(cidx, &event_code);
 		if (event_idx_type < 0)
 			/* Continue the stop operation for other counters */
 			continue;
 
 		else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW)
-			ret = pmu_ctr_stop_fw(cbase, event_code);
+			ret = pmu_ctr_stop_fw(cidx, event_code);
 		else
-			ret = pmu_ctr_stop_hw(cbase);
+			ret = pmu_ctr_stop_hw(cidx);
 
 		if (flag & SBI_PMU_STOP_FLAG_RESET) {
-			active_events[hartid][cbase] = SBI_PMU_EVENT_IDX_INVALID;
-			pmu_reset_hw_mhpmevent(cbase);
+			active_events[hartid][cidx] = SBI_PMU_EVENT_IDX_INVALID;
+			pmu_reset_hw_mhpmevent(cidx);
 		}
 	}
 
@@ -615,10 +617,9 @@ int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask,
 	int event_type = get_cidx_type(event_idx);
 	struct sbi_pmu_fw_event *fevent;
 	uint32_t fw_evt_code;
-	unsigned long tmp = cidx_mask << cidx_base;
 
 	/* Do a basic sanity check of counter base & mask */
-	if (sbi_fls(tmp) >= total_ctrs || event_type >= SBI_PMU_EVENT_TYPE_MAX)
+	if ((cidx_base + sbi_fls(cidx_mask)) >= total_ctrs || event_type >= SBI_PMU_EVENT_TYPE_MAX)
 		return SBI_EINVAL;
 
 	if (flags & SBI_PMU_CFG_FLAG_SKIP_MATCH) {
-- 
2.25.1



From atishp at rivosinc.com  Wed Jul 20 14:50:33 2022
From: atishp at rivosinc.com (Atish Patra)
Date: Wed, 20 Jul 2022 14:50:33 -0700
Subject: [PATCH v3  2/4] lib: sbi: Add the bound check for events during config match
In-Reply-To: <20220720215035.3453553-1-atishp@rivosinc.com>
References: <20220720215035.3453553-1-atishp@rivosinc.com>
Message-ID: <20220720215035.3453553-3-atishp@rivosinc.com>

Currently, there is no sanity check for firmware event code. We don't see
any issue as Linux kernel driver does a bound check on firmware events
already. However, OpenSBI can't assume sane supervisor mode software
always. Thus, an invalid event idx can cause a buffer overflow error.
For hardware events, the match will fail for invalid event code anyways.
However, a search is unecessary if event code is invalid.

Add a event ID validation function to solve the issue.

Signed-off-by: Atish Patra <atishp at rivosinc.com>
---
 lib/sbi/sbi_pmu.c | 46 ++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 44 insertions(+), 2 deletions(-)

diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
index 31631a2fab80..bd1ef8665a70 100644
--- a/lib/sbi/sbi_pmu.c
+++ b/lib/sbi/sbi_pmu.c
@@ -107,6 +107,44 @@ static bool pmu_event_select_overlap(struct sbi_pmu_hw_event *evt,
 	return FALSE;
 }
 
+static int pmu_event_validate(unsigned long event_idx)
+{
+	uint32_t event_idx_type = get_cidx_type(event_idx);
+	uint32_t event_idx_code = get_cidx_code(event_idx);
+	uint32_t event_idx_code_max = -1;
+	uint32_t cache_ops_result, cache_ops_id, cache_id;
+
+	switch(event_idx_type) {
+	case SBI_PMU_EVENT_TYPE_HW:
+		event_idx_code_max = SBI_PMU_HW_GENERAL_MAX;
+		break;
+	case SBI_PMU_EVENT_TYPE_FW:
+		event_idx_code_max = SBI_PMU_FW_MAX;
+		break;
+	case SBI_PMU_EVENT_TYPE_HW_CACHE:
+		cache_ops_result = event_idx_code & 0x1;
+		cache_ops_id = (event_idx_code & 0x06) >> 1;
+		cache_id = (event_idx_code & 0xfff8) >> 3;
+		if ((cache_ops_result < SBI_PMU_HW_CACHE_RESULT_MAX) &&
+		    (cache_ops_id < SBI_PMU_HW_CACHE_OP_MAX) &&
+		    (cache_id < SBI_PMU_HW_CACHE_MAX))
+			return event_idx_type;
+		else
+			return SBI_EINVAL;
+		break;
+	case SBI_PMU_EVENT_TYPE_HW_RAW:
+		event_idx_code_max = 1; // event_idx.code should be zero
+		break;
+	default:
+		return SBI_EINVAL;
+	}
+
+	if (event_idx_code < event_idx_code_max)
+		return event_idx_type;
+
+	return SBI_EINVAL;
+}
+
 static int pmu_ctr_validate(uint32_t cidx, uint32_t *event_idx_code)
 {
 	uint32_t event_idx_val;
@@ -614,12 +652,16 @@ int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask,
 {
 	int ctr_idx = SBI_ENOTSUPP;
 	u32 hartid = current_hartid();
-	int event_type = get_cidx_type(event_idx);
+	int event_type;
 	struct sbi_pmu_fw_event *fevent;
 	uint32_t fw_evt_code;
 
 	/* Do a basic sanity check of counter base & mask */
-	if ((cidx_base + sbi_fls(cidx_mask)) >= total_ctrs || event_type >= SBI_PMU_EVENT_TYPE_MAX)
+	if ((cidx_base + sbi_fls(cidx_mask)) >= total_ctrs)
+		return SBI_EINVAL;
+
+	event_type = pmu_event_validate(event_idx);
+	if (event_type < 0)
 		return SBI_EINVAL;
 
 	if (flags & SBI_PMU_CFG_FLAG_SKIP_MATCH) {
-- 
2.25.1



From atishp at rivosinc.com  Wed Jul 20 14:50:35 2022
From: atishp at rivosinc.com (Atish Patra)
Date: Wed, 20 Jul 2022 14:50:35 -0700
Subject: [PATCH v3  4/4] lib: sbi: Fix fw_event_map initialization
In-Reply-To: <20220720215035.3453553-1-atishp@rivosinc.com>
References: <20220720215035.3453553-1-atishp@rivosinc.com>
Message-ID: <20220720215035.3453553-5-atishp@rivosinc.com>

fw_event_map represents array of firmware events. It should initialized
for maximum number of firmware events not counters.

Reviewed-by: Andrew Jones <ajones at ventanamicro.com>
Signed-off-by: Atish Patra <atishp at rivosinc.com>
---
 lib/sbi/sbi_pmu.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
index ae3c00374058..b4342971bcf1 100644
--- a/lib/sbi/sbi_pmu.c
+++ b/lib/sbi/sbi_pmu.c
@@ -764,7 +764,7 @@ static void pmu_reset_event_map(u32 hartid)
 	/* Initialize the counter to event mapping table */
 	for (j = 3; j < total_ctrs; j++)
 		active_events[hartid][j] = SBI_PMU_EVENT_IDX_INVALID;
-	for (j = 0; j < SBI_PMU_FW_CTR_MAX; j++)
+	for (j = 0; j < SBI_PMU_FW_EVENT_MAX; j++)
 		sbi_memset(&fw_event_map[hartid][j], 0,
 			   sizeof(struct sbi_pmu_fw_event));
 }
-- 
2.25.1



From atishp at rivosinc.com  Wed Jul 20 14:50:34 2022
From: atishp at rivosinc.com (Atish Patra)
Date: Wed, 20 Jul 2022 14:50:34 -0700
Subject: [PATCH v3  3/4] lib: sbi: Fix possible buffer overrun in counter validation
In-Reply-To: <20220720215035.3453553-1-atishp@rivosinc.com>
References: <20220720215035.3453553-1-atishp@rivosinc.com>
Message-ID: <20220720215035.3453553-4-atishp@rivosinc.com>

The active_events array is accessed with counter ID passed from the supervisor
software before the counter ID bound check. This may cause a buffer overrun
if a supervisor passes an invalid counter ID.

Fix this by moving the access part after the bound check.

Reported-by: Andrew Jones <ajones at ventanamicro.com>
Reviewed-by: Andrew Jones <ajones at ventanamicro.com>
Signed-off-by: Atish Patra <atishp at rivosinc.com>
---
 lib/sbi/sbi_pmu.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
index bd1ef8665a70..ae3c00374058 100644
--- a/lib/sbi/sbi_pmu.c
+++ b/lib/sbi/sbi_pmu.c
@@ -151,13 +151,13 @@ static int pmu_ctr_validate(uint32_t cidx, uint32_t *event_idx_code)
 	uint32_t event_idx_type;
 	u32 hartid = current_hartid();
 
-	event_idx_val = active_events[hartid][cidx];
-
-	if (cidx >= total_ctrs || (event_idx_val == SBI_PMU_EVENT_IDX_INVALID))
+	if (cidx >= total_ctrs)
 		return SBI_EINVAL;
 
+	event_idx_val = active_events[hartid][cidx];
 	event_idx_type = get_cidx_type(event_idx_val);
-	if (event_idx_type >= SBI_PMU_EVENT_TYPE_MAX)
+	if (event_idx_val == SBI_PMU_EVENT_IDX_INVALID ||
+	    event_idx_type >= SBI_PMU_EVENT_TYPE_MAX)
 		return SBI_EINVAL;
 
 	*event_idx_code = get_cidx_code(event_idx_val);
-- 
2.25.1



From ajones at ventanamicro.com  Wed Jul 20 22:35:57 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Thu, 21 Jul 2022 07:35:57 +0200
Subject: [PATCH 4/4] lib: utils/serial: Ensure baudrate is non-zero
 before using
In-Reply-To: <70f832b65f372623a87098c501e08c0e50b11b4b.camel@126.com>
References: <20220718172028.2006166-1-ajones@ventanamicro.com>
 <20220718172028.2006166-5-ajones@ventanamicro.com>
 <a2f95ffcea25b6da6270c509e0dc7e5c64be7e92.camel@126.com>
 <20220720171619.rmaryfe3en2lftko@kamzik>
 <70f832b65f372623a87098c501e08c0e50b11b4b.camel@126.com>
Message-ID: <20220721053557.q5xgwsrihgj3mu7a@kamzik>

On Thu, Jul 21, 2022 at 01:25:44AM +0800, Xiang W wrote:
> ? 2022-07-20???? 19:16 +0200?Andrew Jones???
> > On Wed, Jul 20, 2022 at 11:12:18PM +0800, Xiang W wrote:
> > > ? 2022-07-18???? 19:20 +0200?Andrew Jones???
> > > > RISC-V doesn't generate exceptions on divide-by-zero, but the result,
> > > > all bits set, is not likely what people expect either. In all cases
> > > > where we divide by baudrate there's a chance it's zero (when the DT
> > > > it came from is "bad"). To avoid difficult to debug situations, leave
> > > > baudrate dependent registers alone when baudrate is zero, as, also in
> > > > all cases, it appears we can skip initialization of those registers
> > > > and still [hopefully] have a functioning UART.
> > > > 
> > > > Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
> > > If the parameter is wrong we should skip initialization and should not
> > > execute sbi_console_set_device.
> > 
> > Hi Xiang,
> > 
> > While there should be some DT validation somewhere that points out bad
> > parameters, I don't think opensbi is the best place to do it, especially
> > for UART configuration. While we certainly can't progress in all cases
> > we detect bad input, in the case of a zero baudrate I believe we can, as
> > it appears setting baudrate registers is typically optional for UARTs.
> > 
> > We could also consider adding a log buffer which early init can write
> > errors and warnings to. After the console has been initialized (with
> > our best effort) we can then output all the errors and warnings from the
> > log buffer. If we had something like that, then we could add a warning
> > about the baudrate to that log along with warnings/errors for any other
> > DT parameters we fail to validate but attempt to work around.
> > 
> > Thanks,
> > drew
> I mean:
> 
> int xxx_uart_init(...)
> {
> 	if (baudrate == 0)
> 		return 0;
> 	...
> 	// console_dev should not be set because initialization failed
> }

I understood your suggestion, but I disagree. If it's possible to
initialize a device without all information in the DT being present or
valid, then, IMHO, opensbi should do so. Outputting errors/warnings
is good, but giving up turns opensbi into a DT validator which it isn't
well equipped to be. It's a particularly bad DT validator when parsing
UART DT nodes or any other nodes before the console is available.

I do agree that if we try to initialize the console despite detecting
bad information in the DT that we risk not having a working console
and not easily understanding why. I think the early log buffer I
suggest above could help with that. When debugging the first thing one
would do is to dump the contents of that buffer to see if any messages
can point them in the right direction.

I'll try to pull together some log buffer patches and post for
discussion.

Thanks,
drew

> 
> > 
> > > 
> > > Regards,
> > > Xiang W
> > > > ---
> > > > ?lib/utils/serial/gaisler-uart.c | 2 +-
> > > > ?lib/utils/serial/shakti-uart.c? | 8 ++++++--
> > > > ?lib/utils/serial/sifive-uart.c? | 2 +-
> > > > ?lib/utils/serial/uart8250.c???? | 7 +++++--
> > > > ?4 files changed, 13 insertions(+), 6 deletions(-)
> > > > 
> > > > diff --git a/lib/utils/serial/gaisler-uart.c b/lib/utils/serial/gaisler-uart.c
> > > > index 5f30ee43b719..eec75cca0c98 100644
> > > > --- a/lib/utils/serial/gaisler-uart.c
> > > > +++ b/lib/utils/serial/gaisler-uart.c
> > > > @@ -70,7 +70,7 @@ int gaisler_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
> > > > ????????uart_base = (volatile char *)base;
> > > > ?
> > > > ????????/* Configure baudrate */
> > > > -???????if (in_freq)
> > > > +???????if (in_freq && baudrate)
> > > > ????????????????set_reg(UART_REG_SCALER, in_freq / (baudrate * 8 + 7));
> > > > ?
> > > > ????????ctrl = get_reg(UART_REG_CTRL);
> > > > diff --git a/lib/utils/serial/shakti-uart.c b/lib/utils/serial/shakti-uart.c
> > > > index 5f2fe75c9f33..2f09f141b9cf 100644
> > > > --- a/lib/utils/serial/shakti-uart.c
> > > > +++ b/lib/utils/serial/shakti-uart.c
> > > > @@ -47,8 +47,12 @@ static struct sbi_console_device shakti_console = {
> > > > ?int shakti_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
> > > > ?{
> > > > ????????uart_base = (volatile char *)base;
> > > > -???????u16 baud = (u16)(in_freq/(16 * baudrate));
> > > > -???????writew(baud, uart_base + REG_BAUD);
> > > > +???????u16 baud;
> > > > +
> > > > +???????if (baudrate) {
> > > > +???????????????baud = (u16)(in_freq / (16 * baudrate));
> > > > +???????????????writew(baud, uart_base + REG_BAUD);
> > > > +???????}
> > > > ?
> > > > ????????sbi_console_set_device(&shakti_console);
> > > > ?
> > > > diff --git a/lib/utils/serial/sifive-uart.c b/lib/utils/serial/sifive-uart.c
> > > > index 7078611a5274..3581d47a6d4b 100644
> > > > --- a/lib/utils/serial/sifive-uart.c
> > > > +++ b/lib/utils/serial/sifive-uart.c
> > > > @@ -97,7 +97,7 @@ int sifive_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
> > > > ????????uart_baudrate = baudrate;
> > > > ?
> > > > ????????/* Configure baudrate */
> > > > -???????if (in_freq)
> > > > +???????if (in_freq && baudrate)
> > > > ????????????????set_reg(UART_REG_DIV, uart_min_clk_divisor(in_freq, baudrate));
> > > > ?
> > > > ????????/* Disable interrupts */
> > > > diff --git a/lib/utils/serial/uart8250.c b/lib/utils/serial/uart8250.c
> > > > index 38ea11af4d31..99bf1bf733f2 100644
> > > > --- a/lib/utils/serial/uart8250.c
> > > > +++ b/lib/utils/serial/uart8250.c
> > > > @@ -93,7 +93,7 @@ static struct sbi_console_device uart8250_console = {
> > > > ?int uart8250_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift,
> > > > ????????????????? u32 reg_width, u32 reg_offset)
> > > > ?{
> > > > -???????u16 bdiv;
> > > > +???????u16 bdiv = 0;
> > > > ?
> > > > ????????uart8250_base????? = (volatile char *)base + reg_offset;
> > > > ????????uart8250_reg_shift = reg_shift;
> > > > @@ -101,7 +101,10 @@ int uart8250_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift,
> > > > ????????uart8250_in_freq?? = in_freq;
> > > > ????????uart8250_baudrate? = baudrate;
> > > > ?
> > > > -???????bdiv = (uart8250_in_freq + 8 * uart8250_baudrate) / (16 * uart8250_baudrate);
> > > > +???????if (uart8250_baudrate) {
> > > > +???????????????bdiv = (uart8250_in_freq + 8 * uart8250_baudrate) /
> > > > +????????????????????? (16 * uart8250_baudrate);
> > > > +???????}
> > > > ?
> > > > ????????/* Disable all interrupts */
> > > > ????????set_reg(UART_IER_OFFSET, 0x00);
> > > > -- 
> > > > 2.36.1
> > > > 
> > > > 
> > > 
> > > 
> > > 
> > > -- 
> > > opensbi mailing list
> > > opensbi at lists.infradead.org
> > > http://lists.infradead.org/mailman/listinfo/opensbi
> 
> 


From ajones at ventanamicro.com  Wed Jul 20 22:46:29 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Thu, 21 Jul 2022 07:46:29 +0200
Subject: [PATCH v3  2/4] lib: sbi: Add the bound check for events during
 config match
In-Reply-To: <20220720215035.3453553-3-atishp@rivosinc.com>
References: <20220720215035.3453553-1-atishp@rivosinc.com>
 <20220720215035.3453553-3-atishp@rivosinc.com>
Message-ID: <20220721054629.lurwim3jm7ggr26h@kamzik>

On Wed, Jul 20, 2022 at 02:50:33PM -0700, Atish Patra wrote:
> Currently, there is no sanity check for firmware event code. We don't see
> any issue as Linux kernel driver does a bound check on firmware events
> already. However, OpenSBI can't assume sane supervisor mode software
> always. Thus, an invalid event idx can cause a buffer overflow error.
> For hardware events, the match will fail for invalid event code anyways.
> However, a search is unecessary if event code is invalid.
> 
> Add a event ID validation function to solve the issue.
> 
> Signed-off-by: Atish Patra <atishp at rivosinc.com>
> ---
>  lib/sbi/sbi_pmu.c | 46 ++++++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 44 insertions(+), 2 deletions(-)
> 
> diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
> index 31631a2fab80..bd1ef8665a70 100644
> --- a/lib/sbi/sbi_pmu.c
> +++ b/lib/sbi/sbi_pmu.c
> @@ -107,6 +107,44 @@ static bool pmu_event_select_overlap(struct sbi_pmu_hw_event *evt,
>  	return FALSE;
>  }
>  
> +static int pmu_event_validate(unsigned long event_idx)
> +{
> +	uint32_t event_idx_type = get_cidx_type(event_idx);
> +	uint32_t event_idx_code = get_cidx_code(event_idx);
> +	uint32_t event_idx_code_max = -1;
> +	uint32_t cache_ops_result, cache_ops_id, cache_id;
> +
> +	switch(event_idx_type) {
> +	case SBI_PMU_EVENT_TYPE_HW:
> +		event_idx_code_max = SBI_PMU_HW_GENERAL_MAX;
> +		break;
> +	case SBI_PMU_EVENT_TYPE_FW:
> +		event_idx_code_max = SBI_PMU_FW_MAX;
> +		break;
> +	case SBI_PMU_EVENT_TYPE_HW_CACHE:
> +		cache_ops_result = event_idx_code & 0x1;
> +		cache_ops_id = (event_idx_code & 0x06) >> 1;
> +		cache_id = (event_idx_code & 0xfff8) >> 3;

It'd be nice to introduce helper macros for these.

> +		if ((cache_ops_result < SBI_PMU_HW_CACHE_RESULT_MAX) &&
> +		    (cache_ops_id < SBI_PMU_HW_CACHE_OP_MAX) &&
> +		    (cache_id < SBI_PMU_HW_CACHE_MAX))
> +			return event_idx_type;
> +		else
> +			return SBI_EINVAL;
> +		break;
> +	case SBI_PMU_EVENT_TYPE_HW_RAW:
> +		event_idx_code_max = 1; // event_idx.code should be zero
> +		break;
> +	default:
> +		return SBI_EINVAL;
> +	}
> +
> +	if (event_idx_code < event_idx_code_max)
> +		return event_idx_type;
> +
> +	return SBI_EINVAL;
> +}
> +
>  static int pmu_ctr_validate(uint32_t cidx, uint32_t *event_idx_code)
>  {
>  	uint32_t event_idx_val;
> @@ -614,12 +652,16 @@ int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask,
>  {
>  	int ctr_idx = SBI_ENOTSUPP;
>  	u32 hartid = current_hartid();
> -	int event_type = get_cidx_type(event_idx);
> +	int event_type;
>  	struct sbi_pmu_fw_event *fevent;
>  	uint32_t fw_evt_code;
>  
>  	/* Do a basic sanity check of counter base & mask */
> -	if ((cidx_base + sbi_fls(cidx_mask)) >= total_ctrs || event_type >= SBI_PMU_EVENT_TYPE_MAX)
> +	if ((cidx_base + sbi_fls(cidx_mask)) >= total_ctrs)
> +		return SBI_EINVAL;
> +
> +	event_type = pmu_event_validate(event_idx);
> +	if (event_type < 0)
>  		return SBI_EINVAL;
>  
>  	if (flags & SBI_PMU_CFG_FLAG_SKIP_MATCH) {
> -- 
> 2.25.1
> 

Otherwise,

Reviewed-by: Andrew Jones <ajones at ventanamicro.com>


From ptprasanna at gmail.com  Wed Jul 20 23:59:11 2022
From: ptprasanna at gmail.com (Prasanna Thandabani)
Date: Thu, 21 Jul 2022 12:29:11 +0530
Subject: [PATCH v3] lib: utils/serial: Update Shakti UART based on latest implementation
In-Reply-To: <CAAhSdy1PGGVHcbEgNr3vhOntLyoGqXZH-_qz3WkfnEC_Muezzw@mail.gmail.com>
References: <20220708115244.556074-1-apatel@ventanamicro.com> <CAAhSdy1PGGVHcbEgNr3vhOntLyoGqXZH-_qz3WkfnEC_Muezzw@mail.gmail.com>
Message-ID: <CAKMAqeePJr0pUObdtTHCsxTOtoZ7EQKZFV0qSTh=WieE9dhOXA@mail.gmail.com>

Sure Anup,

That's the ideal way for a longer run. Thanks for applying the patch.

Thanks
PT

On Wed, Jul 20, 2022 at 10:13 AM Anup Patel <anup at brainfault.org> wrote:
>
> On Fri, Jul 8, 2022 at 5:23 PM Anup Patel <apatel at ventanamicro.com> wrote:
> >
> > From: Prasanna T <ptprasanna at gmail.com>
> >
> > The age old version of Shakti UART was upgraded long back, but we missed
> > updating the driver in OpenSBI. The old version of UART is not supported
> > anymore, hence removed the inline comment which is also outdated now.
> >
> > Signed-off-by: Prasanna T <ptprasanna at gmail.com>
> > Signed-off-by: Anup Patel <apatel at ventanamicro.com>
>
> @Prasanna, Please make sure that Shakti UART compatible string is
> documented in the upstream Linux kernel. This time it's okay since there
> is no HW with the old version of Shakti UART but subsequent changes to
> Shakti UART should require a new compatible string.
>
> Applied this patch to the riscv/opensbi repo.
>
> Thanks,
> Anup
>
> > ---
> > Changes since v2:
> >  - Updated patch subject and description
> >  - Minor fix in shakti_uart_getc()
> > ---
> >  lib/utils/serial/shakti-uart.c | 6 +++---
> >  1 file changed, 3 insertions(+), 3 deletions(-)
> >
> > diff --git a/lib/utils/serial/shakti-uart.c b/lib/utils/serial/shakti-uart.c
> > index 5f2fe75..3556935 100644
> > --- a/lib/utils/serial/shakti-uart.c
> > +++ b/lib/utils/serial/shakti-uart.c
> > @@ -19,21 +19,21 @@
> >  #define REG_RX_THRES   0x20
> >
> >  #define UART_TX_FULL  0x2
> > +#define UART_RX_NOT_EMPTY 0x4
> >  #define UART_RX_FULL  0x8
> >
> >  static volatile char *uart_base;
> >
> >  static void shakti_uart_putc(char ch)
> >  {
> > -       while((readw(uart_base + REG_STATUS) & UART_TX_FULL))
> > +       while ((readb(uart_base + REG_STATUS) & UART_TX_FULL))
> >                 ;
> >         writeb(ch, uart_base + REG_TX);
> >  }
> >
> >  static int shakti_uart_getc(void)
> >  {
> > -       u16 status = readw(uart_base + REG_STATUS);
> > -       if (status & UART_RX_FULL)
> > +       if (readb(uart_base + REG_STATUS) & UART_RX_NOT_EMPTY)
> >                 return readb(uart_base + REG_RX);
> >         return -1;
> >  }
> > --
> > 2.34.1
> >


From ajones at ventanamicro.com  Tue Jul 26 04:25:22 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Tue, 26 Jul 2022 13:25:22 +0200
Subject: [PATCH] lib: sbi: Fix sbi_snprintf
Message-ID: <20220726112522.5070-1-ajones@ventanamicro.com>

printc would happily write to 'out' even when 'out_len' was zero,
potentially overflowing buffers. Rework printc to not do that and
also ensure the null byte is written at the last position when
necessary, as stated in the snprintf man page. Finally, ensure all
writes to 'out' go through printc and rename a goto label which
clashed with it.

Fixes: 9e8ff05cb61f ("Initial commit.")
Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
---
 lib/sbi/sbi_console.c | 35 ++++++++++++++++++++---------------
 1 file changed, 20 insertions(+), 15 deletions(-)

diff --git a/lib/sbi/sbi_console.c b/lib/sbi/sbi_console.c
index 34c843d3f9e3..47f361705fc7 100644
--- a/lib/sbi/sbi_console.c
+++ b/lib/sbi/sbi_console.c
@@ -76,20 +76,24 @@ typedef __builtin_va_list va_list;
 
 static void printc(char **out, u32 *out_len, char ch)
 {
-	if (out) {
-		if (*out) {
-			if (out_len && (0 < *out_len)) {
-				**out = ch;
-				++(*out);
-				(*out_len)--;
-			} else {
-				**out = ch;
-				++(*out);
-			}
-		}
-	} else {
+	if (!out) {
 		sbi_putc(ch);
+		return;
 	}
+
+	if (!*out)
+		return;
+
+	if (!out_len || *out_len > 1)
+		**out = ch;
+	else if (*out_len == 1)
+		**out = '\0';
+
+	if (out_len && *out_len > 0)
+		--(*out_len);
+
+	if (!out_len || *out_len > 0)
+		++(*out);
 }
 
 static int prints(char **out, u32 *out_len, const char *string, int width,
@@ -193,7 +197,7 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
 			if (*format == '\0')
 				break;
 			if (*format == '%')
-				goto out;
+				goto literal;
 			/* Get flags */
 			if (*format == '-') {
 				++format;
@@ -332,13 +336,14 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
 				continue;
 			}
 		} else {
-		out:
+literal:
 			printc(out, out_len, *format);
 			++pc;
 		}
 	}
+
 	if (out)
-		**out = '\0';
+		printc(out, out_len, '\0');
 
 	return pc;
 }
-- 
2.36.1



From anup at brainfault.org  Tue Jul 26 04:42:47 2022
From: anup at brainfault.org (Anup Patel)
Date: Tue, 26 Jul 2022 17:12:47 +0530
Subject: [PATCH] lib: sbi: Fix sbi_snprintf
In-Reply-To: <20220726112522.5070-1-ajones@ventanamicro.com>
References: <20220726112522.5070-1-ajones@ventanamicro.com>
Message-ID: <CAAhSdy2STYM0nP94wWxP_1U-VNQvFJmPyF=zP00V9tCfFJ8Gbg@mail.gmail.com>

On Tue, Jul 26, 2022 at 4:55 PM Andrew Jones <ajones at ventanamicro.com> wrote:
>
> printc would happily write to 'out' even when 'out_len' was zero,
> potentially overflowing buffers. Rework printc to not do that and
> also ensure the null byte is written at the last position when
> necessary, as stated in the snprintf man page. Finally, ensure all
> writes to 'out' go through printc and rename a goto label which
> clashed with it.
>
> Fixes: 9e8ff05cb61f ("Initial commit.")
> Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
> ---
>  lib/sbi/sbi_console.c | 35 ++++++++++++++++++++---------------
>  1 file changed, 20 insertions(+), 15 deletions(-)
>
> diff --git a/lib/sbi/sbi_console.c b/lib/sbi/sbi_console.c
> index 34c843d3f9e3..47f361705fc7 100644
> --- a/lib/sbi/sbi_console.c
> +++ b/lib/sbi/sbi_console.c
> @@ -76,20 +76,24 @@ typedef __builtin_va_list va_list;
>
>  static void printc(char **out, u32 *out_len, char ch)
>  {
> -       if (out) {
> -               if (*out) {
> -                       if (out_len && (0 < *out_len)) {
> -                               **out = ch;
> -                               ++(*out);
> -                               (*out_len)--;
> -                       } else {
> -                               **out = ch;
> -                               ++(*out);
> -                       }
> -               }
> -       } else {
> +       if (!out) {
>                 sbi_putc(ch);
> +               return;
>         }
> +
> +       if (!*out)
> +               return;

What if the output buffer is filled with zeros ?

> +
> +       if (!out_len || *out_len > 1)
> +               **out = ch;
> +       else if (*out_len == 1)
> +               **out = '\0';
> +
> +       if (out_len && *out_len > 0)
> +               --(*out_len);
> +
> +       if (!out_len || *out_len > 0)
> +               ++(*out);
>  }
>
>  static int prints(char **out, u32 *out_len, const char *string, int width,
> @@ -193,7 +197,7 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
>                         if (*format == '\0')
>                                 break;
>                         if (*format == '%')
> -                               goto out;
> +                               goto literal;
>                         /* Get flags */
>                         if (*format == '-') {
>                                 ++format;
> @@ -332,13 +336,14 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
>                                 continue;
>                         }
>                 } else {
> -               out:
> +literal:
>                         printc(out, out_len, *format);
>                         ++pc;
>                 }
>         }
> +
>         if (out)
> -               **out = '\0';
> +               printc(out, out_len, '\0');
>
>         return pc;
>  }
> --
> 2.36.1
>
>
> --
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi

Regards,
Anup


From ajones at ventanamicro.com  Tue Jul 26 06:26:15 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Tue, 26 Jul 2022 15:26:15 +0200
Subject: [PATCH] lib: sbi: Fix sbi_snprintf
In-Reply-To: <CAAhSdy2STYM0nP94wWxP_1U-VNQvFJmPyF=zP00V9tCfFJ8Gbg@mail.gmail.com>
References: <20220726112522.5070-1-ajones@ventanamicro.com>
 <CAAhSdy2STYM0nP94wWxP_1U-VNQvFJmPyF=zP00V9tCfFJ8Gbg@mail.gmail.com>
Message-ID: <20220726132615.wh4c5l3jw5omzi62@kamzik>

On Tue, Jul 26, 2022 at 05:12:47PM +0530, Anup Patel wrote:
> On Tue, Jul 26, 2022 at 4:55 PM Andrew Jones <ajones at ventanamicro.com> wrote:
> >
> > printc would happily write to 'out' even when 'out_len' was zero,
> > potentially overflowing buffers. Rework printc to not do that and
> > also ensure the null byte is written at the last position when
> > necessary, as stated in the snprintf man page. Finally, ensure all
> > writes to 'out' go through printc and rename a goto label which
> > clashed with it.
> >
> > Fixes: 9e8ff05cb61f ("Initial commit.")
> > Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
> > ---
> >  lib/sbi/sbi_console.c | 35 ++++++++++++++++++++---------------
> >  1 file changed, 20 insertions(+), 15 deletions(-)
> >
> > diff --git a/lib/sbi/sbi_console.c b/lib/sbi/sbi_console.c
> > index 34c843d3f9e3..47f361705fc7 100644
> > --- a/lib/sbi/sbi_console.c
> > +++ b/lib/sbi/sbi_console.c
> > @@ -76,20 +76,24 @@ typedef __builtin_va_list va_list;
> >
> >  static void printc(char **out, u32 *out_len, char ch)
> >  {
> > -       if (out) {
> > -               if (*out) {
> > -                       if (out_len && (0 < *out_len)) {
> > -                               **out = ch;
> > -                               ++(*out);
> > -                               (*out_len)--;
> > -                       } else {
> > -                               **out = ch;
> > -                               ++(*out);
> > -                       }
> > -               }
> > -       } else {
> > +       if (!out) {
> >                 sbi_putc(ch);
> > +               return;
> >         }
> > +
> > +       if (!*out)
> > +               return;
> 
> What if the output buffer is filled with zeros ?

This single dereferencing of 'out' won't reach the buffer. This check
ensures we don't dereference NULL if sprintf/snprintf are called with
str=NULL and is just a rewording of the original code. Actually, we
probably shouldn't allow str=NULL for sprintf nor for snprintf with
size != 0 at all. We could hang in sprintf and snprintf if we detect
that.

Thanks,
drew


From wxjstz at 126.com  Wed Jul 27 01:47:03 2022
From: wxjstz at 126.com (Xiang W)
Date: Wed, 27 Jul 2022 16:47:03 +0800
Subject: [PATCH] lib: sbi: Fix sbi_snprintf
In-Reply-To: <20220726112522.5070-1-ajones@ventanamicro.com>
References: <20220726112522.5070-1-ajones@ventanamicro.com>
Message-ID: <49df039152c4205e58666862c5412b30322646a3.camel@126.com>

? 2022-07-26???? 13:25 +0200?Andrew Jones???
> printc would happily write to 'out' even when 'out_len' was zero,
> potentially overflowing buffers. Rework printc to not do that and
> also ensure the null byte is written at the last position when
> necessary, as stated in the snprintf man page. Finally, ensure all
> writes to 'out' go through printc and rename a goto label which
> clashed with it.
> 
> Fixes: 9e8ff05cb61f ("Initial commit.")
> Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
> ---
> ?lib/sbi/sbi_console.c | 35 ++++++++++++++++++++---------------
> ?1 file changed, 20 insertions(+), 15 deletions(-)
> 
> diff --git a/lib/sbi/sbi_console.c b/lib/sbi/sbi_console.c
> index 34c843d3f9e3..47f361705fc7 100644
> --- a/lib/sbi/sbi_console.c
> +++ b/lib/sbi/sbi_console.c
> @@ -76,20 +76,24 @@ typedef __builtin_va_list va_list;
> ?
> ?static void printc(char **out, u32 *out_len, char ch)
> ?{
> -???????if (out) {
> -???????????????if (*out) {
> -???????????????????????if (out_len && (0 < *out_len)) {
> -???????????????????????????????**out = ch;
> -???????????????????????????????++(*out);
> -???????????????????????????????(*out_len)--;
> -???????????????????????} else {
> -???????????????????????????????**out = ch;
> -???????????????????????????????++(*out);
> -???????????????????????}
> -???????????????}
> -???????} else {
> +???????if (!out) {
> ????????????????sbi_putc(ch);
> +???????????????return;
> ????????}
> +
> +???????if (!*out)
> +???????????????return;
> +
> +???????if (!out_len || *out_len > 1)
> +???????????????**out = ch;
> +???????else if (*out_len == 1)
> +???????????????**out = '\0';
> +
> +???????if (out_len && *out_len > 0)
> +???????????????--(*out_len);
> +
> +???????if (!out_len || *out_len > 0)
> +???????????????++(*out);
Too many branches

static void printc(char **out, u32 *out_len, char ch)
{
	if (out && *out && out_len) {
		if (*out_len > 0) {
			if (*out_len > 1) {
				(*out)[0] = ch;
				(*out)[1] = '\0';
			}
			(*out)++;
			(*out_len)--;
		} else
			// add error message print
	} else {
		sbi_putc(ch);
	}
}

> ?}
> ?
> ?static int prints(char **out, u32 *out_len, const char *string, int width,
> @@ -193,7 +197,7 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
> ????????????????????????if (*format == '\0')
> ????????????????????????????????break;
> ????????????????????????if (*format == '%')
> -???????????????????????????????goto out;
> +???????????????????????????????goto literal;
> ????????????????????????/* Get flags */
> ????????????????????????if (*format == '-') {
> ????????????????????????????????++format;
> @@ -332,13 +336,14 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
> ????????????????????????????????continue;
> ????????????????????????}
> ????????????????} else {
> -???????????????out:
> +literal:
> ????????????????????????printc(out, out_len, *format);
> ????????????????????????++pc;
> ????????????????}
> ????????}
> +
> ????????if (out)
> -???????????????**out = '\0';
> +???????????????printc(out, out_len, '\0');
this is not necessary
> ?
> ????????return pc;
> ?}
> -- 
> 2.36.1
> 
> 

Regards,
Xiang W



From ajones at ventanamicro.com  Wed Jul 27 03:35:04 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Wed, 27 Jul 2022 12:35:04 +0200
Subject: [PATCH] lib: sbi: Fix sbi_snprintf
In-Reply-To: <49df039152c4205e58666862c5412b30322646a3.camel@126.com>
References: <20220726112522.5070-1-ajones@ventanamicro.com>
 <49df039152c4205e58666862c5412b30322646a3.camel@126.com>
Message-ID: <20220727103504.5okcp5igkdwbc2s3@kamzik>

On Wed, Jul 27, 2022 at 04:47:03PM +0800, Xiang W wrote:
> ? 2022-07-26???? 13:25 +0200?Andrew Jones???
> > printc would happily write to 'out' even when 'out_len' was zero,
> > potentially overflowing buffers. Rework printc to not do that and
> > also ensure the null byte is written at the last position when
> > necessary, as stated in the snprintf man page. Finally, ensure all
> > writes to 'out' go through printc and rename a goto label which
> > clashed with it.
> > 
> > Fixes: 9e8ff05cb61f ("Initial commit.")
> > Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
> > ---
> > ?lib/sbi/sbi_console.c | 35 ++++++++++++++++++++---------------
> > ?1 file changed, 20 insertions(+), 15 deletions(-)
> > 
> > diff --git a/lib/sbi/sbi_console.c b/lib/sbi/sbi_console.c
> > index 34c843d3f9e3..47f361705fc7 100644
> > --- a/lib/sbi/sbi_console.c
> > +++ b/lib/sbi/sbi_console.c
> > @@ -76,20 +76,24 @@ typedef __builtin_va_list va_list;
> > ?
> > ?static void printc(char **out, u32 *out_len, char ch)
> > ?{
> > -???????if (out) {
> > -???????????????if (*out) {
> > -???????????????????????if (out_len && (0 < *out_len)) {
> > -???????????????????????????????**out = ch;
> > -???????????????????????????????++(*out);
> > -???????????????????????????????(*out_len)--;
> > -???????????????????????} else {
> > -???????????????????????????????**out = ch;
> > -???????????????????????????????++(*out);
> > -???????????????????????}
> > -???????????????}
> > -???????} else {
> > +???????if (!out) {
> > ????????????????sbi_putc(ch);
> > +???????????????return;
> > ????????}
> > +
> > +???????if (!*out)
> > +???????????????return;
> > +
> > +???????if (!out_len || *out_len > 1)
> > +???????????????**out = ch;
> > +???????else if (*out_len == 1)
> > +???????????????**out = '\0';
> > +
> > +???????if (out_len && *out_len > 0)
> > +???????????????--(*out_len);
> > +
> > +???????if (!out_len || *out_len > 0)
> > +???????????????++(*out);
> Too many branches
> 
> static void printc(char **out, u32 *out_len, char ch)
> {
> 	if (out && *out && out_len) {

It's possible to have non-NULL out/*out and NULL out_len (when sprintf is
used), so we need at least one other branch.

> 		if (*out_len > 0) {
> 			if (*out_len > 1) {
> 				(*out)[0] = ch;
> 				(*out)[1] = '\0';

Sure. It saves a branch and allows us to drop the extra if (out) branch
in print() below which you pointed out.

> 			}
> 			(*out)++;
> 			(*out_len)--;
> 		} else
> 			// add error message print
> 	} else {
> 		sbi_putc(ch);
> 	}
> }
> 
> > ?}
> > ?
> > ?static int prints(char **out, u32 *out_len, const char *string, int width,
> > @@ -193,7 +197,7 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
> > ????????????????????????if (*format == '\0')
> > ????????????????????????????????break;
> > ????????????????????????if (*format == '%')
> > -???????????????????????????????goto out;
> > +???????????????????????????????goto literal;
> > ????????????????????????/* Get flags */
> > ????????????????????????if (*format == '-') {
> > ????????????????????????????????++format;
> > @@ -332,13 +336,14 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
> > ????????????????????????????????continue;
> > ????????????????????????}
> > ????????????????} else {
> > -???????????????out:
> > +literal:
> > ????????????????????????printc(out, out_len, *format);
> > ????????????????????????++pc;
> > ????????????????}
> > ????????}
> > +
> > ????????if (out)
> > -???????????????**out = '\0';
> > +???????????????printc(out, out_len, '\0');
> this is not necessary

I'll send a v2 with the "always write '\0'" approach integrated.

Thanks,
drew


From ajones at ventanamicro.com  Wed Jul 27 05:12:01 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Wed, 27 Jul 2022 14:12:01 +0200
Subject: [PATCH v2] lib: sbi: Fix sbi_snprintf
Message-ID: <20220727121201.11508-1-ajones@ventanamicro.com>

printc would happily write to 'out' even when 'out_len' was zero,
potentially overflowing buffers. Rework printc to not do that and
also ensure the null byte is written at the last position when
necessary, as stated in the snprintf man page. Also, panic if
sprintf or snprintf are called with NULL output strings (except
the special case of snprintf having a NULL output string and
a zero output size, allowing it to be used to get the number of
characters that would have been written). Finally, rename a
goto label which clashed with 'out'.

Fixes: 9e8ff05cb61f ("Initial commit.")
Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
---

v2:
  - Simply forbid *out == NULL by panicing when it's detected.
    (The error message for snprintf has been split over two
     lines to avoid going over 80 chars. I'd prefer error messages
     not be split, but that seems like the general practice for
     opensbi.)
  - Drop some branches, particularly the extra 'if (out)' in print(),
    by always writing a '\0' on each printc [Xiang W]


 lib/sbi/sbi_console.c | 35 +++++++++++++++++++----------------
 1 file changed, 19 insertions(+), 16 deletions(-)

diff --git a/lib/sbi/sbi_console.c b/lib/sbi/sbi_console.c
index 34c843d3f9e3..e300d710b5c8 100644
--- a/lib/sbi/sbi_console.c
+++ b/lib/sbi/sbi_console.c
@@ -76,20 +76,18 @@ typedef __builtin_va_list va_list;
 
 static void printc(char **out, u32 *out_len, char ch)
 {
-	if (out) {
-		if (*out) {
-			if (out_len && (0 < *out_len)) {
-				**out = ch;
-				++(*out);
-				(*out_len)--;
-			} else {
-				**out = ch;
-				++(*out);
-			}
-		}
-	} else {
+	if (!out) {
 		sbi_putc(ch);
+		return;
 	}
+
+	if (!out_len || *out_len > 1) {
+		*(*out)++ = ch;
+		**out = '\0';
+	}
+
+	if (out_len && *out_len > 0)
+		--(*out_len);
 }
 
 static int prints(char **out, u32 *out_len, const char *string, int width,
@@ -193,7 +191,7 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
 			if (*format == '\0')
 				break;
 			if (*format == '%')
-				goto out;
+				goto literal;
 			/* Get flags */
 			if (*format == '-') {
 				++format;
@@ -332,13 +330,11 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
 				continue;
 			}
 		} else {
-		out:
+literal:
 			printc(out, out_len, *format);
 			++pc;
 		}
 	}
-	if (out)
-		**out = '\0';
 
 	return pc;
 }
@@ -348,6 +344,9 @@ int sbi_sprintf(char *out, const char *format, ...)
 	va_list args;
 	int retval;
 
+	if (unlikely(!out))
+		sbi_panic("sbi_sprintf called with NULL output string\n");
+
 	va_start(args, format);
 	retval = print(&out, NULL, format, args);
 	va_end(args);
@@ -360,6 +359,10 @@ int sbi_snprintf(char *out, u32 out_sz, const char *format, ...)
 	va_list args;
 	int retval;
 
+	if (unlikely(!out && out_sz != 0))
+		sbi_panic("sbi_snprintf called with NULL output string and "
+			  "output size is not zero\n");
+
 	va_start(args, format);
 	retval = print(&out, &out_sz, format, args);
 	va_end(args);
-- 
2.36.1



From wxjstz at 126.com  Wed Jul 27 06:27:52 2022
From: wxjstz at 126.com (Xiang W)
Date: Wed, 27 Jul 2022 21:27:52 +0800
Subject: [PATCH v2] lib: sbi: Fix sbi_snprintf
In-Reply-To: <20220727121201.11508-1-ajones@ventanamicro.com>
References: <20220727121201.11508-1-ajones@ventanamicro.com>
Message-ID: <1859b4266df1dd0c6e84c668fdf921c6aedd3ce6.camel@126.com>

? 2022-07-27???? 14:12 +0200?Andrew Jones???
> printc would happily write to 'out' even when 'out_len' was zero,
> potentially overflowing buffers. Rework printc to not do that and
> also ensure the null byte is written at the last position when
> necessary, as stated in the snprintf man page. Also, panic if
> sprintf or snprintf are called with NULL output strings (except
> the special case of snprintf having a NULL output string and
> a zero output size, allowing it to be used to get the number of
> characters that would have been written). Finally, rename a
> goto label which clashed with 'out'.
> 
> Fixes: 9e8ff05cb61f ("Initial commit.")
> Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
> ---
> 
> v2:
> ? - Simply forbid *out == NULL by panicing when it's detected.
> ??? (The error message for snprintf has been split over two
> ???? lines to avoid going over 80 chars. I'd prefer error messages
> ???? not be split, but that seems like the general practice for
> ???? opensbi.)
> ? - Drop some branches, particularly the extra 'if (out)' in print(),
> ??? by always writing a '\0' on each printc [Xiang W]
> 
> 
> ?lib/sbi/sbi_console.c | 35 +++++++++++++++++++----------------
> ?1 file changed, 19 insertions(+), 16 deletions(-)
> 
> diff --git a/lib/sbi/sbi_console.c b/lib/sbi/sbi_console.c
> index 34c843d3f9e3..e300d710b5c8 100644
> --- a/lib/sbi/sbi_console.c
> +++ b/lib/sbi/sbi_console.c
> @@ -76,20 +76,18 @@ typedef __builtin_va_list va_list;
> ?
> ?static void printc(char **out, u32 *out_len, char ch)
> ?{
> -???????if (out) {
> -???????????????if (*out) {
> -???????????????????????if (out_len && (0 < *out_len)) {
> -???????????????????????????????**out = ch;
> -???????????????????????????????++(*out);
> -???????????????????????????????(*out_len)--;
> -???????????????????????} else {
> -???????????????????????????????**out = ch;
> -???????????????????????????????++(*out);
> -???????????????????????}
> -???????????????}
> -???????} else {
> +???????if (!out) {
> ????????????????sbi_putc(ch);
> +???????????????return;
> ????????}
> +
> +???????if (!out_len || *out_len > 1) {
> +???????????????*(*out)++ = ch;
Before this, it was not determined that *out was not NULL.

Argument checking before calling the function is not a substitute
for checking the function itself, the function should do what it
has to do. Otherwise, others need more understanding work during
secondary development.

Regards,
Xiang W
> +???????????????**out = '\0';
> +???????}
> +
> +???????if (out_len && *out_len > 0)
> +???????????????--(*out_len);
> ?}
> ?
> ?static int prints(char **out, u32 *out_len, const char *string, int width,
> @@ -193,7 +191,7 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
> ????????????????????????if (*format == '\0')
> ????????????????????????????????break;
> ????????????????????????if (*format == '%')
> -???????????????????????????????goto out;
> +???????????????????????????????goto literal;
> ????????????????????????/* Get flags */
> ????????????????????????if (*format == '-') {
> ????????????????????????????????++format;
> @@ -332,13 +330,11 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
> ????????????????????????????????continue;
> ????????????????????????}
> ????????????????} else {
> -???????????????out:
> +literal:
> ????????????????????????printc(out, out_len, *format);
> ????????????????????????++pc;
> ????????????????}
> ????????}
> -???????if (out)
> -???????????????**out = '\0';
> ?
> ????????return pc;
> ?}
> @@ -348,6 +344,9 @@ int sbi_sprintf(char *out, const char *format, ...)
> ????????va_list args;
> ????????int retval;
> ?
> +???????if (unlikely(!out))
> +???????????????sbi_panic("sbi_sprintf called with NULL output string\n");
> +
> ????????va_start(args, format);
> ????????retval = print(&out, NULL, format, args);
> ????????va_end(args);
> @@ -360,6 +359,10 @@ int sbi_snprintf(char *out, u32 out_sz, const char *format, ...)
> ????????va_list args;
> ????????int retval;
> ?
> +???????if (unlikely(!out && out_sz != 0))
> +???????????????sbi_panic("sbi_snprintf called with NULL output string and "
> +???????????????????????? "output size is not zero\n");
> +
> ????????va_start(args, format);
> ????????retval = print(&out, &out_sz, format, args);
> ????????va_end(args);
> -- 
> 2.36.1
> 
> 




From ajones at ventanamicro.com  Wed Jul 27 06:45:07 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Wed, 27 Jul 2022 15:45:07 +0200
Subject: [PATCH v2] lib: sbi: Fix sbi_snprintf
In-Reply-To: <1859b4266df1dd0c6e84c668fdf921c6aedd3ce6.camel@126.com>
References: <20220727121201.11508-1-ajones@ventanamicro.com>
 <1859b4266df1dd0c6e84c668fdf921c6aedd3ce6.camel@126.com>
Message-ID: <20220727134507.sx4njywnk5zu7go4@kamzik>

On Wed, Jul 27, 2022 at 09:27:52PM +0800, Xiang W wrote:
> ? 2022-07-27???? 14:12 +0200?Andrew Jones???
> > printc would happily write to 'out' even when 'out_len' was zero,
> > potentially overflowing buffers. Rework printc to not do that and
> > also ensure the null byte is written at the last position when
> > necessary, as stated in the snprintf man page. Also, panic if
> > sprintf or snprintf are called with NULL output strings (except
> > the special case of snprintf having a NULL output string and
> > a zero output size, allowing it to be used to get the number of
> > characters that would have been written). Finally, rename a
> > goto label which clashed with 'out'.
> > 
> > Fixes: 9e8ff05cb61f ("Initial commit.")
> > Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
> > ---
> > 
> > v2:
> > ? - Simply forbid *out == NULL by panicing when it's detected.
> > ??? (The error message for snprintf has been split over two
> > ???? lines to avoid going over 80 chars. I'd prefer error messages
> > ???? not be split, but that seems like the general practice for
> > ???? opensbi.)
> > ? - Drop some branches, particularly the extra 'if (out)' in print(),
> > ??? by always writing a '\0' on each printc [Xiang W]
> > 
> > 
> > ?lib/sbi/sbi_console.c | 35 +++++++++++++++++++----------------
> > ?1 file changed, 19 insertions(+), 16 deletions(-)
> > 
> > diff --git a/lib/sbi/sbi_console.c b/lib/sbi/sbi_console.c
> > index 34c843d3f9e3..e300d710b5c8 100644
> > --- a/lib/sbi/sbi_console.c
> > +++ b/lib/sbi/sbi_console.c
> > @@ -76,20 +76,18 @@ typedef __builtin_va_list va_list;
> > ?
> > ?static void printc(char **out, u32 *out_len, char ch)
> > ?{
> > -???????if (out) {
> > -???????????????if (*out) {
> > -???????????????????????if (out_len && (0 < *out_len)) {
> > -???????????????????????????????**out = ch;
> > -???????????????????????????????++(*out);
> > -???????????????????????????????(*out_len)--;
> > -???????????????????????} else {
> > -???????????????????????????????**out = ch;
> > -???????????????????????????????++(*out);
> > -???????????????????????}
> > -???????????????}
> > -???????} else {
> > +???????if (!out) {
> > ????????????????sbi_putc(ch);
> > +???????????????return;
> > ????????}
> > +
> > +???????if (!out_len || *out_len > 1) {
> > +???????????????*(*out)++ = ch;
> Before this, it was not determined that *out was not NULL.
> 
> Argument checking before calling the function is not a substitute
> for checking the function itself, the function should do what it
> has to do. Otherwise, others need more understanding work during
> secondary development.

I disagree. Argument checking in private helper functions is overly
defensive programming. Indeed, helper functions should expect the
entry points of its module to do the argument checking. It is
reasonable to add a comment pointing out that we know *out can't be
NULL here, though, as printc is the only place we do the dereferencing.

Thanks,
drew


From wxjstz at 126.com  Wed Jul 27 06:56:17 2022
From: wxjstz at 126.com (Xiang W)
Date: Wed, 27 Jul 2022 21:56:17 +0800
Subject: [PATCH v2] lib: sbi: Fix sbi_snprintf
In-Reply-To: <20220727134507.sx4njywnk5zu7go4@kamzik>
References: <20220727121201.11508-1-ajones@ventanamicro.com>
	 <1859b4266df1dd0c6e84c668fdf921c6aedd3ce6.camel@126.com>
	 <20220727134507.sx4njywnk5zu7go4@kamzik>
Message-ID: <36cb86bff5459ab8f9ed2e45ae20fda6bb741661.camel@126.com>

? 2022-07-27???? 15:45 +0200?Andrew Jones???
> On Wed, Jul 27, 2022 at 09:27:52PM +0800, Xiang W wrote:
> > ? 2022-07-27???? 14:12 +0200?Andrew Jones???
> > > printc would happily write to 'out' even when 'out_len' was zero,
> > > potentially overflowing buffers. Rework printc to not do that and
> > > also ensure the null byte is written at the last position when
> > > necessary, as stated in the snprintf man page. Also, panic if
> > > sprintf or snprintf are called with NULL output strings (except
> > > the special case of snprintf having a NULL output string and
> > > a zero output size, allowing it to be used to get the number of
> > > characters that would have been written). Finally, rename a
> > > goto label which clashed with 'out'.
> > > 
> > > Fixes: 9e8ff05cb61f ("Initial commit.")
> > > Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
> > > ---
> > > 
> > > v2:
> > > ? - Simply forbid *out == NULL by panicing when it's detected.
> > > ??? (The error message for snprintf has been split over two
> > > ???? lines to avoid going over 80 chars. I'd prefer error messages
> > > ???? not be split, but that seems like the general practice for
> > > ???? opensbi.)
> > > ? - Drop some branches, particularly the extra 'if (out)' in print(),
> > > ??? by always writing a '\0' on each printc [Xiang W]
> > > 
> > > 
> > > ?lib/sbi/sbi_console.c | 35 +++++++++++++++++++----------------
> > > ?1 file changed, 19 insertions(+), 16 deletions(-)
> > > 
> > > diff --git a/lib/sbi/sbi_console.c b/lib/sbi/sbi_console.c
> > > index 34c843d3f9e3..e300d710b5c8 100644
> > > --- a/lib/sbi/sbi_console.c
> > > +++ b/lib/sbi/sbi_console.c
> > > @@ -76,20 +76,18 @@ typedef __builtin_va_list va_list;
> > > ?
> > > ?static void printc(char **out, u32 *out_len, char ch)
> > > ?{
> > > -???????if (out) {
> > > -???????????????if (*out) {
> > > -???????????????????????if (out_len && (0 < *out_len)) {
> > > -???????????????????????????????**out = ch;
> > > -???????????????????????????????++(*out);
> > > -???????????????????????????????(*out_len)--;
> > > -???????????????????????} else {
> > > -???????????????????????????????**out = ch;
> > > -???????????????????????????????++(*out);
> > > -???????????????????????}
> > > -???????????????}
> > > -???????} else {
> > > +???????if (!out) {
> > > ????????????????sbi_putc(ch);
> > > +???????????????return;
> > > ????????}
> > > +
> > > +???????if (!out_len || *out_len > 1) {
> > > +???????????????*(*out)++ = ch;
> > Before this, it was not determined that *out was not NULL.
> > 
> > Argument checking before calling the function is not a substitute
> > for checking the function itself, the function should do what it
> > has to do. Otherwise, others need more understanding work during
> > secondary development.
> 
> I disagree. Argument checking in private helper functions is overly
> defensive programming. Indeed, helper functions should expect the
> entry points of its module to do the argument checking. It is
> reasonable to add a comment pointing out that we know *out can't be
> NULL here, though, as printc is the only place we do the dereferencing.

Yes, I agree to add a comment to clarify

Regards,
Xiang W
> 
> Thanks,
> drew




From dramforever at live.com  Wed Jul 27 07:07:39 2022
From: dramforever at live.com (dramforever)
Date: Wed, 27 Jul 2022 22:07:39 +0800
Subject: [PATCH] lib: sbi: Fix printf handling of long long
Message-ID: <TYYP286MB1439FD2B8C624B5346185D7DC6979@TYYP286MB1439.JPNP286.PROD.OUTLOOK.COM>

Do not attempt to unnecessarily align va_list before reading long long
arguments.

Signed-off-by: dramforever <dramforever at live.com>
---
 lib/sbi/sbi_console.c | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/lib/sbi/sbi_console.c b/lib/sbi/sbi_console.c
index 34c843d..7af50e5 100644
--- a/lib/sbi/sbi_console.c
+++ b/lib/sbi/sbi_console.c
@@ -261,11 +261,6 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
 				continue;
 			}
 			if (*format == 'l' && *(format + 1) == 'l') {
-				while (acnt &
-				       (sizeof(unsigned long long) - 1)) {
-					va_arg(args, int);
-					acnt += sizeof(int);
-				}
 				if (sizeof(unsigned long long) ==
 				    sizeof(unsigned long)) {
 					tmp = va_arg(args, unsigned long long);
-- 
2.37.0



From ajones at ventanamicro.com  Wed Jul 27 07:17:47 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Wed, 27 Jul 2022 16:17:47 +0200
Subject: [PATCH v3] lib: sbi: Fix sbi_snprintf
Message-ID: <20220727141747.4793-1-ajones@ventanamicro.com>

printc would happily write to 'out' even when 'out_len' was zero,
potentially overflowing buffers. Rework printc to not do that and
also ensure the null byte is written at the last position when
necessary, as stated in the snprintf man page. Also, panic if
sprintf or snprintf are called with NULL output strings (except
the special case of snprintf having a NULL output string and
a zero output size, allowing it to be used to get the number of
characters that would have been written). Finally, rename a
goto label which clashed with 'out'.

Fixes: 9e8ff05cb61f ("Initial commit.")
Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
---

v3:
  - Add comment in printc stating our expectations of *out

v2:
  - Simply forbid *out == NULL by panicing when it's detected.
    (The error message for snprintf has been split over two
     lines to avoid going over 80 chars. I'd prefer error messages
     not be split, but that seems like the general practice for
     opensbi.)
  - Drop some branches, particularly the extra 'if (out)' in print(),
    by always writing a '\0' on each printc [Xiang W]


 lib/sbi/sbi_console.c | 39 +++++++++++++++++++++++----------------
 1 file changed, 23 insertions(+), 16 deletions(-)

diff --git a/lib/sbi/sbi_console.c b/lib/sbi/sbi_console.c
index 34c843d3f9e3..7b9be4a4667f 100644
--- a/lib/sbi/sbi_console.c
+++ b/lib/sbi/sbi_console.c
@@ -76,20 +76,22 @@ typedef __builtin_va_list va_list;
 
 static void printc(char **out, u32 *out_len, char ch)
 {
-	if (out) {
-		if (*out) {
-			if (out_len && (0 < *out_len)) {
-				**out = ch;
-				++(*out);
-				(*out_len)--;
-			} else {
-				**out = ch;
-				++(*out);
-			}
-		}
-	} else {
+	if (!out) {
 		sbi_putc(ch);
+		return;
 	}
+
+	/*
+	 * The *printf entry point functions have enforced that (*out) can
+	 * only be null when out_len is non-null and its value is zero.
+	 */
+	if (!out_len || *out_len > 1) {
+		*(*out)++ = ch;
+		**out = '\0';
+	}
+
+	if (out_len && *out_len > 0)
+		--(*out_len);
 }
 
 static int prints(char **out, u32 *out_len, const char *string, int width,
@@ -193,7 +195,7 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
 			if (*format == '\0')
 				break;
 			if (*format == '%')
-				goto out;
+				goto literal;
 			/* Get flags */
 			if (*format == '-') {
 				++format;
@@ -332,13 +334,11 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
 				continue;
 			}
 		} else {
-		out:
+literal:
 			printc(out, out_len, *format);
 			++pc;
 		}
 	}
-	if (out)
-		**out = '\0';
 
 	return pc;
 }
@@ -348,6 +348,9 @@ int sbi_sprintf(char *out, const char *format, ...)
 	va_list args;
 	int retval;
 
+	if (unlikely(!out))
+		sbi_panic("sbi_sprintf called with NULL output string\n");
+
 	va_start(args, format);
 	retval = print(&out, NULL, format, args);
 	va_end(args);
@@ -360,6 +363,10 @@ int sbi_snprintf(char *out, u32 out_sz, const char *format, ...)
 	va_list args;
 	int retval;
 
+	if (unlikely(!out && out_sz != 0))
+		sbi_panic("sbi_snprintf called with NULL output string and "
+			  "output size is not zero\n");
+
 	va_start(args, format);
 	retval = print(&out, &out_sz, format, args);
 	va_end(args);
-- 
2.36.1



From ajones at ventanamicro.com  Wed Jul 27 07:32:54 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Wed, 27 Jul 2022 16:32:54 +0200
Subject: [PATCH] lib: sbi: Fix printf handling of long long
In-Reply-To: <TYYP286MB1439FD2B8C624B5346185D7DC6979@TYYP286MB1439.JPNP286.PROD.OUTLOOK.COM>
References: <TYYP286MB1439FD2B8C624B5346185D7DC6979@TYYP286MB1439.JPNP286.PROD.OUTLOOK.COM>
Message-ID: <20220727143254.kb7nrdccubbdzsxe@kamzik>

On Wed, Jul 27, 2022 at 10:07:39PM +0800, dramforever wrote:
> Do not attempt to unnecessarily align va_list before reading long long
> arguments.

Can you please elaborate on why it was presumed necessary once, but is now
deemed not necessary?

> 
> Signed-off-by: dramforever <dramforever at live.com>
> ---
>  lib/sbi/sbi_console.c | 5 -----
>  1 file changed, 5 deletions(-)
> 
> diff --git a/lib/sbi/sbi_console.c b/lib/sbi/sbi_console.c
> index 34c843d..7af50e5 100644
> --- a/lib/sbi/sbi_console.c
> +++ b/lib/sbi/sbi_console.c
> @@ -261,11 +261,6 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
>  				continue;
>  			}
>  			if (*format == 'l' && *(format + 1) == 'l') {
> -				while (acnt &
> -				       (sizeof(unsigned long long) - 1)) {
> -					va_arg(args, int);
> -					acnt += sizeof(int);
> -				}

I think this should generate a "set but not used" warning with clang, as
this was the only place acnt was getting used. We can just remove all of
it.

Thanks,
drew

>  				if (sizeof(unsigned long long) ==
>  				    sizeof(unsigned long)) {
>  					tmp = va_arg(args, unsigned long long);
> -- 
> 2.37.0
> 
> 
> -- 
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi


From jrtc27 at jrtc27.com  Wed Jul 27 07:48:47 2022
From: jrtc27 at jrtc27.com (Jessica Clarke)
Date: Wed, 27 Jul 2022 15:48:47 +0100
Subject: [PATCH] lib: sbi: Fix printf handling of long long
In-Reply-To: <20220727143254.kb7nrdccubbdzsxe@kamzik>
References: <TYYP286MB1439FD2B8C624B5346185D7DC6979@TYYP286MB1439.JPNP286.PROD.OUTLOOK.COM>
 <20220727143254.kb7nrdccubbdzsxe@kamzik>
Message-ID: <AF7FE4B0-2DBE-4827-AC1C-05E6709784C5@jrtc27.com>

On 27 Jul 2022, at 15:32, Andrew Jones <ajones at ventanamicro.com> wrote:
> 
> On Wed, Jul 27, 2022 at 10:07:39PM +0800, dramforever wrote:
>> Do not attempt to unnecessarily align va_list before reading long long
>> arguments.
> 
> Can you please elaborate on why it was presumed necessary once, but is now
> deemed not necessary?

Because of this nonsense that follows:

	if (sizeof(unsigned long long) ==
			sizeof(unsigned long)) {
		tmp = va_arg(args, unsigned long long);
		acnt += sizeof(unsigned long long);
	} else {
		((unsigned long *)&tmp)[0] =
			va_arg(args, unsigned long);
		((unsigned long *)&tmp)[1] =
			va_arg(args, unsigned long);
		acnt += 2*sizeof(unsigned long);
	}

For RV64 the condition of the while is always false as variadic
arguments are already padded to 8 bytes. For RV32 the implementation of
va_arg will dynamically align the pointer to 8 bytes at run time
(mirroring what this loop does) so it would seem unnecessary, except
for the nonsense that follows. Since unsigned long is only 32-bit, it
ends up taking the else branch and reading two unsigned longs, which
will *not* get the same dynamic alignment from the compiler. So long as
that nonsense also exists the code is needed on RV32. Where this gets
more problematic, though, is on RV64, since acnt does not stay in sync
with how much args has been offset; all variadic arguments are padded
to 8 bytes, but for (unsigned) int the printf implementation here only
increments acnt by sizeof(int). Thus, on RV64, if you pass an odd
number of ints and then a long long the implementation will erroneously
skip an entry, which is presumably what resulted in this being found
and the patch in question.

This should all be changed to just reading an unsigned long long
because that will do the right thing (if not that is a serious compiler
bug), not try to buggily reimplement va_arg ABI lowering.

Note this code has existed right from 9e8ff05cb61f ("Initial commit.?).

Jess

>> Signed-off-by: dramforever <dramforever at live.com>
>> ---
>> lib/sbi/sbi_console.c | 5 -----
>> 1 file changed, 5 deletions(-)
>> 
>> diff --git a/lib/sbi/sbi_console.c b/lib/sbi/sbi_console.c
>> index 34c843d..7af50e5 100644
>> --- a/lib/sbi/sbi_console.c
>> +++ b/lib/sbi/sbi_console.c
>> @@ -261,11 +261,6 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
>> 				continue;
>> 			}
>> 			if (*format == 'l' && *(format + 1) == 'l') {
>> -				while (acnt &
>> -				 (sizeof(unsigned long long) - 1)) {
>> -					va_arg(args, int);
>> -					acnt += sizeof(int);
>> -				}
> 
> I think this should generate a "set but not used" warning with clang, as
> this was the only place acnt was getting used. We can just remove all of
> it.
> 
> Thanks,
> drew
> 
>> 				if (sizeof(unsigned long long) ==
>> 				 sizeof(unsigned long)) {
>> 					tmp = va_arg(args, unsigned long long);
>> -- 
>> 2.37.0
>> 
>> 
>> -- 
>> opensbi mailing list
>> opensbi at lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/opensbi
> 
> -- 
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi



From dramforever at live.com  Wed Jul 27 07:51:39 2022
From: dramforever at live.com (dramforever)
Date: Wed, 27 Jul 2022 22:51:39 +0800
Subject: [PATCH] lib: sbi: Fix printf handling of long long
In-Reply-To: <AF7FE4B0-2DBE-4827-AC1C-05E6709784C5@jrtc27.com>
References: <TYYP286MB1439FD2B8C624B5346185D7DC6979@TYYP286MB1439.JPNP286.PROD.OUTLOOK.COM>
 <20220727143254.kb7nrdccubbdzsxe@kamzik>
 <AF7FE4B0-2DBE-4827-AC1C-05E6709784C5@jrtc27.com>
Message-ID: <TYYP286MB1439B3BB87F7CEB60F6D1A59C6979@TYYP286MB1439.JPNP286.PROD.OUTLOOK.COM>

On 7/27/22 22:48, Jessica Clarke wrote:
> On 27 Jul 2022, at 15:32, Andrew Jones <ajones at ventanamicro.com> wrote:
>> On Wed, Jul 27, 2022 at 10:07:39PM +0800, dramforever wrote:
>>> Do not attempt to unnecessarily align va_list before reading long long
>>> arguments.
>> Can you please elaborate on why it was presumed necessary once, but is now
>> deemed not necessary?
> Because of this nonsense that follows:
>
> 	if (sizeof(unsigned long long) ==
> 			sizeof(unsigned long)) {
> 		tmp = va_arg(args, unsigned long long);
> 		acnt += sizeof(unsigned long long);
> 	} else {
> 		((unsigned long *)&tmp)[0] =
> 			va_arg(args, unsigned long);
> 		((unsigned long *)&tmp)[1] =
> 			va_arg(args, unsigned long);
> 		acnt += 2*sizeof(unsigned long);
> 	}
>
> For RV64 the condition of the while is always false as variadic
> arguments are already padded to 8 bytes. For RV32 the implementation of
> va_arg will dynamically align the pointer to 8 bytes at run time
> (mirroring what this loop does) so it would seem unnecessary, except
> for the nonsense that follows. Since unsigned long is only 32-bit, it
> ends up taking the else branch and reading two unsigned longs, which
> will *not* get the same dynamic alignment from the compiler. So long as
> that nonsense also exists the code is needed on RV32. Where this gets
> more problematic, though, is on RV64, since acnt does not stay in sync
> with how much args has been offset; all variadic arguments are padded
> to 8 bytes, but for (unsigned) int the printf implementation here only
> increments acnt by sizeof(int). Thus, on RV64, if you pass an odd
> number of ints and then a long long the implementation will erroneously
> skip an entry, which is presumably what resulted in this being found
> and the patch in question.
Yes, this was indeed how I've found out about it :)
> This should all be changed to just reading an unsigned long long
> because that will do the right thing (if not that is a serious compiler
> bug), not try to buggily reimplement va_arg ABI lowering.

Thanks for the tip. I'll send a v2 with this fix.

dram

> Note this code has existed right from 9e8ff05cb61f ("Initial commit.?).
>
> Jess
>
>>> Signed-off-by: dramforever <dramforever at live.com>
>>> ---
>>> lib/sbi/sbi_console.c | 5 -----
>>> 1 file changed, 5 deletions(-)
>>>
>>> diff --git a/lib/sbi/sbi_console.c b/lib/sbi/sbi_console.c
>>> index 34c843d..7af50e5 100644
>>> --- a/lib/sbi/sbi_console.c
>>> +++ b/lib/sbi/sbi_console.c
>>> @@ -261,11 +261,6 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
>>> 				continue;
>>> 			}
>>> 			if (*format == 'l' && *(format + 1) == 'l') {
>>> -				while (acnt &
>>> -				 (sizeof(unsigned long long) - 1)) {
>>> -					va_arg(args, int);
>>> -					acnt += sizeof(int);
>>> -				}
>> I think this should generate a "set but not used" warning with clang, as
>> this was the only place acnt was getting used. We can just remove all of
>> it.
>>
>> Thanks,
>> drew
>>
>>> 				if (sizeof(unsigned long long) ==
>>> 				 sizeof(unsigned long)) {
>>> 					tmp = va_arg(args, unsigned long long);
>>> -- 
>>> 2.37.0
>>>
>>>
>>> -- 
>>> opensbi mailing list
>>> opensbi at lists.infradead.org
>>> http://lists.infradead.org/mailman/listinfo/opensbi
>> -- 
>> opensbi mailing list
>> opensbi at lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/opensbi


From jrtc27 at jrtc27.com  Wed Jul 27 07:53:37 2022
From: jrtc27 at jrtc27.com (Jessica Clarke)
Date: Wed, 27 Jul 2022 15:53:37 +0100
Subject: [PATCH] lib: sbi: Fix printf handling of long long
In-Reply-To: <AF7FE4B0-2DBE-4827-AC1C-05E6709784C5@jrtc27.com>
References: <TYYP286MB1439FD2B8C624B5346185D7DC6979@TYYP286MB1439.JPNP286.PROD.OUTLOOK.COM>
 <20220727143254.kb7nrdccubbdzsxe@kamzik>
 <AF7FE4B0-2DBE-4827-AC1C-05E6709784C5@jrtc27.com>
Message-ID: <6F9758F7-03BC-4703-BFBE-B70601785B92@jrtc27.com>

On 27 Jul 2022, at 15:48, Jessica Clarke <jrtc27 at jrtc27.com> wrote:
> 
> On 27 Jul 2022, at 15:32, Andrew Jones <ajones at ventanamicro.com> wrote:
>> 
>> On Wed, Jul 27, 2022 at 10:07:39PM +0800, dramforever wrote:
>>> Do not attempt to unnecessarily align va_list before reading long long
>>> arguments.
>> 
>> Can you please elaborate on why it was presumed necessary once, but is now
>> deemed not necessary?
> 
> Because of this nonsense that follows:
> 
> 	if (sizeof(unsigned long long) ==
> 			sizeof(unsigned long)) {
> 		tmp = va_arg(args, unsigned long long);
> 		acnt += sizeof(unsigned long long);
> 	} else {
> 		((unsigned long *)&tmp)[0] =
> 			va_arg(args, unsigned long);
> 		((unsigned long *)&tmp)[1] =
> 			va_arg(args, unsigned long);
> 		acnt += 2*sizeof(unsigned long);
> 	}
> 
> For RV64 the condition of the while is always false as variadic
> arguments are already padded to 8 bytes.

Uh I forgot to snip this sentence after my understanding evolved. This
is false and contradicted by what I say later.

> For RV32 the implementation of
> va_arg will dynamically align the pointer to 8 bytes at run time
> (mirroring what this loop does) so it would seem unnecessary, except
> for the nonsense that follows. Since unsigned long is only 32-bit, it
> ends up taking the else branch and reading two unsigned longs, which
> will *not* get the same dynamic alignment from the compiler. So long as
> that nonsense also exists the code is needed on RV32. Where this gets
> more problematic, though, is on RV64, since acnt does not stay in sync
> with how much args has been offset; all variadic arguments are padded
> to 8 bytes, but for (unsigned) int the printf implementation here only
> increments acnt by sizeof(int). Thus, on RV64, if you pass an odd
> number of ints and then a long long the implementation will erroneously
> skip an entry, which is presumably what resulted in this being found
> and the patch in question.
> 
> This should all be changed to just reading an unsigned long long
> because that will do the right thing (if not that is a serious compiler
> bug), not try to buggily reimplement va_arg ABI lowering.
> 
> Note this code has existed right from 9e8ff05cb61f ("Initial commit.?).
> 
> Jess
> 
>>> Signed-off-by: dramforever <dramforever at live.com>
>>> ---
>>> lib/sbi/sbi_console.c | 5 -----
>>> 1 file changed, 5 deletions(-)
>>> 
>>> diff --git a/lib/sbi/sbi_console.c b/lib/sbi/sbi_console.c
>>> index 34c843d..7af50e5 100644
>>> --- a/lib/sbi/sbi_console.c
>>> +++ b/lib/sbi/sbi_console.c
>>> @@ -261,11 +261,6 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
>>> 				continue;
>>> 			}
>>> 			if (*format == 'l' && *(format + 1) == 'l') {
>>> -				while (acnt &
>>> -				 (sizeof(unsigned long long) - 1)) {
>>> -					va_arg(args, int);
>>> -					acnt += sizeof(int);
>>> -				}
>> 
>> I think this should generate a "set but not used" warning with clang, as
>> this was the only place acnt was getting used. We can just remove all of
>> it.
>> 
>> Thanks,
>> drew
>> 
>>> 				if (sizeof(unsigned long long) ==
>>> 				 sizeof(unsigned long)) {
>>> 					tmp = va_arg(args, unsigned long long);
>>> -- 
>>> 2.37.0
>>> 
>>> 
>>> -- 
>>> opensbi mailing list
>>> opensbi at lists.infradead.org
>>> http://lists.infradead.org/mailman/listinfo/opensbi
>> 
>> -- 
>> opensbi mailing list
>> opensbi at lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/opensbi
> 



From dramforever at live.com  Wed Jul 27 08:06:12 2022
From: dramforever at live.com (dramforever)
Date: Wed, 27 Jul 2022 23:06:12 +0800
Subject: [PATCH v2] lib: sbi: Fix printf handling of long long
Message-ID: <TYYP286MB143938D036AFEF2F33EBF335C6979@TYYP286MB1439.JPNP286.PROD.OUTLOOK.COM>

Read long long arguments directly using va_arg. Remove original hack for
RV32 that read a long long arg as two long args.

This un-breaks the case on RV64 where e.g. the long long is followed by
an odd number of ints:

    sbi_printf("%d %lld", (int) 1, (long long) 2LL);

Signed-off-by: dramforever <dramforever at live.com>
---
 lib/sbi/sbi_console.c | 18 ++----------------
 1 file changed, 2 insertions(+), 16 deletions(-)

diff --git a/lib/sbi/sbi_console.c b/lib/sbi/sbi_console.c
index 34c843d..f18411b 100644
--- a/lib/sbi/sbi_console.c
+++ b/lib/sbi/sbi_console.c
@@ -261,22 +261,8 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
 				continue;
 			}
 			if (*format == 'l' && *(format + 1) == 'l') {
-				while (acnt &
-				       (sizeof(unsigned long long) - 1)) {
-					va_arg(args, int);
-					acnt += sizeof(int);
-				}
-				if (sizeof(unsigned long long) ==
-				    sizeof(unsigned long)) {
-					tmp = va_arg(args, unsigned long long);
-					acnt += sizeof(unsigned long long);
-				} else {
-					((unsigned long *)&tmp)[0] =
-						va_arg(args, unsigned long);
-					((unsigned long *)&tmp)[1] =
-						va_arg(args, unsigned long);
-					acnt += 2 * sizeof(unsigned long);
-				}
+				tmp = va_arg(args, unsigned long long);
+				acnt += sizeof(unsigned long long);
 				if (*(format + 2) == 'u') {
 					format += 2;
 					pc += printi(out, out_len, tmp, 10, 0,
-- 
2.37.0



From ajones at ventanamicro.com  Wed Jul 27 08:50:09 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Wed, 27 Jul 2022 17:50:09 +0200
Subject: [PATCH v2] lib: sbi: Fix printf handling of long long
In-Reply-To: <TYYP286MB143938D036AFEF2F33EBF335C6979@TYYP286MB1439.JPNP286.PROD.OUTLOOK.COM>
References: <TYYP286MB143938D036AFEF2F33EBF335C6979@TYYP286MB1439.JPNP286.PROD.OUTLOOK.COM>
Message-ID: <20220727155009.ix4dv5gb3r4nnpru@kamzik>

On Wed, Jul 27, 2022 at 11:06:12PM +0800, dramforever wrote:
> Read long long arguments directly using va_arg. Remove original hack for
> RV32 that read a long long arg as two long args.
> 
> This un-breaks the case on RV64 where e.g. the long long is followed by
> an odd number of ints:
> 
>     sbi_printf("%d %lld", (int) 1, (long long) 2LL);
> 
> Signed-off-by: dramforever <dramforever at live.com>
> ---
>  lib/sbi/sbi_console.c | 18 ++----------------
>  1 file changed, 2 insertions(+), 16 deletions(-)
> 
> diff --git a/lib/sbi/sbi_console.c b/lib/sbi/sbi_console.c
> index 34c843d..f18411b 100644
> --- a/lib/sbi/sbi_console.c
> +++ b/lib/sbi/sbi_console.c
> @@ -261,22 +261,8 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
>  				continue;
>  			}
>  			if (*format == 'l' && *(format + 1) == 'l') {
> -				while (acnt &
> -				       (sizeof(unsigned long long) - 1)) {
> -					va_arg(args, int);
> -					acnt += sizeof(int);
> -				}
> -				if (sizeof(unsigned long long) ==
> -				    sizeof(unsigned long)) {
> -					tmp = va_arg(args, unsigned long long);
> -					acnt += sizeof(unsigned long long);
> -				} else {
> -					((unsigned long *)&tmp)[0] =
> -						va_arg(args, unsigned long);
> -					((unsigned long *)&tmp)[1] =
> -						va_arg(args, unsigned long);
> -					acnt += 2 * sizeof(unsigned long);
> -				}
> +				tmp = va_arg(args, unsigned long long);
> +				acnt += sizeof(unsigned long long);
>  				if (*(format + 2) == 'u') {
>  					format += 2;
>  					pc += printi(out, out_len, tmp, 10, 0,
> -- 
> 2.37.0
>

We need to remove all acnt lines from print() and the variable itself as
it's unused after the while loop above is removed.

Thanks,
drew 


From dramforever at live.com  Wed Jul 27 09:30:05 2022
From: dramforever at live.com (dramforever)
Date: Thu, 28 Jul 2022 00:30:05 +0800
Subject: [PATCH v3] lib: sbi: Fix printf handling of long long
Message-ID: <TYYP286MB14396B0D3F810E64516CFE9DC6979@TYYP286MB1439.JPNP286.PROD.OUTLOOK.COM>

Read long long arguments directly using va_arg. Remove original hack for
RV32 that read a long long arg as two long args.

This un-breaks the case on RV64 where e.g. the long long is followed by
an odd number of ints:

    sbi_printf("%d %lld", (int) 1, (long long) 2LL);

Also remove the acnt variable, which is now unused.

Signed-off-by: dramforever <dramforever at live.com>
---

Changes since v2:

- Remove the acnt variable

Changes since v1:

- Removed hack that reads a long long as two longs 
- Better commit message

 lib/sbi/sbi_console.c | 30 ++----------------------------
 1 file changed, 2 insertions(+), 28 deletions(-)

diff --git a/lib/sbi/sbi_console.c b/lib/sbi/sbi_console.c
index 34c843d..6629b43 100644
--- a/lib/sbi/sbi_console.c
+++ b/lib/sbi/sbi_console.c
@@ -181,7 +181,7 @@ static int printi(char **out, u32 *out_len, long long i, int b, int sg,
 
 static int print(char **out, u32 *out_len, const char *format, va_list args)
 {
-	int width, flags, acnt = 0;
+	int width, flags;
 	int pc = 0;
 	char scr[2];
 	unsigned long long tmp;
@@ -214,7 +214,6 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
 			}
 			if (*format == 's') {
 				char *s = va_arg(args, char *);
-				acnt += sizeof(char *);
 				pc += prints(out, out_len, s ? s : "(null)",
 					     width, flags);
 				continue;
@@ -222,61 +221,40 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
 			if ((*format == 'd') || (*format == 'i')) {
 				pc += printi(out, out_len, va_arg(args, int),
 					     10, 1, width, flags, '0');
-				acnt += sizeof(int);
 				continue;
 			}
 			if (*format == 'x') {
 				pc += printi(out, out_len,
 					     va_arg(args, unsigned int), 16, 0,
 					     width, flags, 'a');
-				acnt += sizeof(unsigned int);
 				continue;
 			}
 			if (*format == 'X') {
 				pc += printi(out, out_len,
 					     va_arg(args, unsigned int), 16, 0,
 					     width, flags, 'A');
-				acnt += sizeof(unsigned int);
 				continue;
 			}
 			if (*format == 'u') {
 				pc += printi(out, out_len,
 					     va_arg(args, unsigned int), 10, 0,
 					     width, flags, 'a');
-				acnt += sizeof(unsigned int);
 				continue;
 			}
 			if (*format == 'p') {
 				pc += printi(out, out_len,
 					     va_arg(args, unsigned long), 16, 0,
 					     width, flags, 'a');
-				acnt += sizeof(unsigned long);
 				continue;
 			}
 			if (*format == 'P') {
 				pc += printi(out, out_len,
 					     va_arg(args, unsigned long), 16, 0,
 					     width, flags, 'A');
-				acnt += sizeof(unsigned long);
 				continue;
 			}
 			if (*format == 'l' && *(format + 1) == 'l') {
-				while (acnt &
-				       (sizeof(unsigned long long) - 1)) {
-					va_arg(args, int);
-					acnt += sizeof(int);
-				}
-				if (sizeof(unsigned long long) ==
-				    sizeof(unsigned long)) {
-					tmp = va_arg(args, unsigned long long);
-					acnt += sizeof(unsigned long long);
-				} else {
-					((unsigned long *)&tmp)[0] =
-						va_arg(args, unsigned long);
-					((unsigned long *)&tmp)[1] =
-						va_arg(args, unsigned long);
-					acnt += 2 * sizeof(unsigned long);
-				}
+				tmp = va_arg(args, unsigned long long);
 				if (*(format + 2) == 'u') {
 					format += 2;
 					pc += printi(out, out_len, tmp, 10, 0,
@@ -308,19 +286,16 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
 						out, out_len,
 						va_arg(args, unsigned long), 16,
 						0, width, flags, 'a');
-					acnt += sizeof(unsigned long);
 				} else if (*(format + 1) == 'X') {
 					format += 1;
 					pc += printi(
 						out, out_len,
 						va_arg(args, unsigned long), 16,
 						0, width, flags, 'A');
-					acnt += sizeof(unsigned long);
 				} else {
 					pc += printi(out, out_len,
 						     va_arg(args, long), 10, 1,
 						     width, flags, '0');
-					acnt += sizeof(long);
 				}
 			}
 			if (*format == 'c') {
@@ -328,7 +303,6 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
 				scr[0] = va_arg(args, int);
 				scr[1] = '\0';
 				pc += prints(out, out_len, scr, width, flags);
-				acnt += sizeof(int);
 				continue;
 			}
 		} else {
-- 
2.37.0



From ajones at ventanamicro.com  Wed Jul 27 09:44:01 2022
From: ajones at ventanamicro.com (Andrew Jones)
Date: Wed, 27 Jul 2022 18:44:01 +0200
Subject: [PATCH v3] lib: sbi: Fix printf handling of long long
In-Reply-To: <TYYP286MB14396B0D3F810E64516CFE9DC6979@TYYP286MB1439.JPNP286.PROD.OUTLOOK.COM>
References: <TYYP286MB14396B0D3F810E64516CFE9DC6979@TYYP286MB1439.JPNP286.PROD.OUTLOOK.COM>
Message-ID: <20220727164401.tjinf26ysa27j43y@kamzik>

On Thu, Jul 28, 2022 at 12:30:05AM +0800, dramforever wrote:
> Read long long arguments directly using va_arg. Remove original hack for
> RV32 that read a long long arg as two long args.
> 
> This un-breaks the case on RV64 where e.g. the long long is followed by
> an odd number of ints:
> 
>     sbi_printf("%d %lld", (int) 1, (long long) 2LL);
> 
> Also remove the acnt variable, which is now unused.
> 
> Signed-off-by: dramforever <dramforever at live.com>
> ---
> 
> Changes since v2:
> 
> - Remove the acnt variable
> 
> Changes since v1:
> 
> - Removed hack that reads a long long as two longs 
> - Better commit message
> 
>  lib/sbi/sbi_console.c | 30 ++----------------------------
>  1 file changed, 2 insertions(+), 28 deletions(-)
>

Reviewed-by: Andrew Jones <ajones at ventanamicro.com>


From anup at brainfault.org  Fri Jul 29 22:24:48 2022
From: anup at brainfault.org (Anup Patel)
Date: Sat, 30 Jul 2022 10:54:48 +0530
Subject: [PATCH 1/4] lib: utils/fdt: Factor out common uart node code
In-Reply-To: <20220718172028.2006166-2-ajones@ventanamicro.com>
References: <20220718172028.2006166-1-ajones@ventanamicro.com> <20220718172028.2006166-2-ajones@ventanamicro.com>
Message-ID: <CAAhSdy0Crf3puRUJehFaVc5TtSb7gyad26bR_DpQ5wnUr=aT1A@mail.gmail.com>

On Mon, Jul 18, 2022 at 10:50 PM Andrew Jones <ajones at ventanamicro.com> wrote:
>
> Factor out the common code used by the fdt UART node parsers,
> allowing us to drop duplicate code.
>
> Signed-off-by: Andrew Jones <ajones at ventanamicro.com>

Looks good to me.

Reviewed-by: Anup Patel <anup at brainfault.org>

Regards,
Anup

> ---
>  lib/utils/fdt/fdt_helper.c | 131 +++++++++++--------------------------
>  1 file changed, 39 insertions(+), 92 deletions(-)
>
> diff --git a/lib/utils/fdt/fdt_helper.c b/lib/utils/fdt/fdt_helper.c
> index 2bdb1ef23e55..c6abf804014d 100644
> --- a/lib/utils/fdt/fdt_helper.c
> +++ b/lib/utils/fdt/fdt_helper.c
> @@ -314,8 +314,10 @@ int fdt_parse_timebase_frequency(void *fdt, unsigned long *freq)
>         return 0;
>  }
>
> -int fdt_parse_gaisler_uart_node(void *fdt, int nodeoffset,
> -                               struct platform_uart_data *uart)
> +static int fdt_parse_uart_node_common(void *fdt, int nodeoffset,
> +                                     struct platform_uart_data *uart,
> +                                     unsigned long default_freq,
> +                                     unsigned long default_baud)
>  {
>         int len, rc;
>         const fdt32_t *val;
> @@ -338,13 +340,28 @@ int fdt_parse_gaisler_uart_node(void *fdt, int nodeoffset,
>         if (len > 0 && val)
>                 uart->freq = fdt32_to_cpu(*val);
>         else
> -               uart->freq = DEFAULT_UART_FREQ;
> +               uart->freq = default_freq;
>
>         val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "current-speed", &len);
>         if (len > 0 && val)
>                 uart->baud = fdt32_to_cpu(*val);
>         else
> -               uart->baud = DEFAULT_UART_BAUD;
> +               uart->baud = default_baud;
> +
> +       return 0;
> +}
> +
> +int fdt_parse_gaisler_uart_node(void *fdt, int nodeoffset,
> +                               struct platform_uart_data *uart)
> +{
> +       int rc;
> +
> +       rc = fdt_parse_uart_node_common(fdt, nodeoffset, uart,
> +                                       DEFAULT_UART_FREQ,
> +                                       DEFAULT_UART_BAUD);
> +
> +       if (rc)
> +               return rc;
>
>         /* For Gaisler APBUART, the reg-shift and reg-io-width are fixed .*/
>         uart->reg_shift    = DEFAULT_UART_REG_SHIFT;
> @@ -356,69 +373,26 @@ int fdt_parse_gaisler_uart_node(void *fdt, int nodeoffset,
>  int fdt_parse_shakti_uart_node(void *fdt, int nodeoffset,
>                                struct platform_uart_data *uart)
>  {
> -       int len, rc;
> -       const fdt32_t *val;
> -       uint64_t reg_addr, reg_size;
> -
> -       if (nodeoffset < 0 || !uart || !fdt)
> -               return SBI_ENODEV;
> -
> -       rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
> -                                   &reg_addr, &reg_size);
> -       if (rc < 0 || !reg_addr || !reg_size)
> -               return SBI_ENODEV;
> -       uart->addr = reg_addr;
> +       int rc;
>
> -       /**
> -        * UART address is mandaotry. clock-frequency and current-speed
> -        * may not be present. Don't return error.
> -        */
> -       val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "clock-frequency", &len);
> -       if (len > 0 && val)
> -               uart->freq = fdt32_to_cpu(*val);
> -       else
> -               uart->freq = DEFAULT_SHAKTI_UART_FREQ;
> +       rc = fdt_parse_uart_node_common(fdt, nodeoffset, uart,
> +                                       DEFAULT_SHAKTI_UART_FREQ,
> +                                       DEFAULT_SHAKTI_UART_BAUD);
>
> -       val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "current-speed", &len);
> -       if (len > 0 && val)
> -               uart->baud = fdt32_to_cpu(*val);
> -       else
> -               uart->baud = DEFAULT_SHAKTI_UART_BAUD;
> -
> -       return 0;
> +       return rc ? : 0;
>  }
>
>  int fdt_parse_sifive_uart_node(void *fdt, int nodeoffset,
>                                struct platform_uart_data *uart)
>  {
> -       int len, rc;
> -       const fdt32_t *val;
> -       uint64_t reg_addr, reg_size;
> -
> -       if (nodeoffset < 0 || !uart || !fdt)
> -               return SBI_ENODEV;
> -
> -       rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
> -                                   &reg_addr, &reg_size);
> -       if (rc < 0 || !reg_addr || !reg_size)
> -               return SBI_ENODEV;
> -       uart->addr = reg_addr;
> +       int rc;
>
> -       /**
> -        * UART address is mandaotry. clock-frequency and current-speed
> -        * may not be present. Don't return error.
> -        */
> -       val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "clock-frequency", &len);
> -       if (len > 0 && val)
> -               uart->freq = fdt32_to_cpu(*val);
> -       else
> -               uart->freq = DEFAULT_SIFIVE_UART_FREQ;
> +       rc = fdt_parse_uart_node_common(fdt, nodeoffset, uart,
> +                                       DEFAULT_SIFIVE_UART_FREQ,
> +                                       DEFAULT_SIFIVE_UART_BAUD);
>
> -       val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "current-speed", &len);
> -       if (len > 0 && val)
> -               uart->baud = fdt32_to_cpu(*val);
> -       else
> -               uart->baud = DEFAULT_SIFIVE_UART_BAUD;
> +       if (rc)
> +               return rc;
>
>         /* For SiFive UART, the reg-shift and reg-io-width are fixed .*/
>         uart->reg_shift = DEFAULT_SIFIVE_UART_REG_SHIFT;
> @@ -432,32 +406,13 @@ int fdt_parse_uart8250_node(void *fdt, int nodeoffset,
>  {
>         int len, rc;
>         const fdt32_t *val;
> -       uint64_t reg_addr, reg_size;
> -
> -       if (nodeoffset < 0 || !uart || !fdt)
> -               return SBI_ENODEV;
> -
> -       rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
> -                                   &reg_addr, &reg_size);
> -       if (rc < 0 || !reg_addr || !reg_size)
> -               return SBI_ENODEV;
> -       uart->addr = reg_addr;
>
> -       /**
> -        * UART address is mandaotry. clock-frequency and current-speed
> -        * may not be present. Don't return error.
> -        */
> -       val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "clock-frequency", &len);
> -       if (len > 0 && val)
> -               uart->freq = fdt32_to_cpu(*val);
> -       else
> -               uart->freq = DEFAULT_UART_FREQ;
> +       rc = fdt_parse_uart_node_common(fdt, nodeoffset, uart,
> +                                       DEFAULT_UART_FREQ,
> +                                       DEFAULT_UART_BAUD);
>
> -       val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "current-speed", &len);
> -       if (len > 0 && val)
> -               uart->baud = fdt32_to_cpu(*val);
> -       else
> -               uart->baud = DEFAULT_UART_BAUD;
> +       if (rc)
> +               return rc;
>
>         val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "reg-shift", &len);
>         if (len > 0 && val)
> @@ -499,18 +454,10 @@ int fdt_parse_xlnx_uartlite_node(void *fdt, int nodeoffset,
>                                struct platform_uart_data *uart)
>  {
>         int rc;
> -       uint64_t reg_addr, reg_size;
>
> -       if (nodeoffset < 0 || !uart || !fdt)
> -               return SBI_ENODEV;
> +       rc = fdt_parse_uart_node_common(fdt, nodeoffset, uart, 0, 0);
>
> -       rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
> -                                   &reg_addr, &reg_size);
> -       if (rc < 0 || !reg_addr || !reg_size)
> -               return SBI_ENODEV;
> -       uart->addr = reg_addr;
> -
> -       return 0;
> +       return rc ? : 0;
>  }
>
>  int fdt_parse_aplic_node(void *fdt, int nodeoff, struct aplic_data *aplic)
> --
> 2.36.1
>
>
> --
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi


From anup at brainfault.org  Fri Jul 29 22:25:47 2022
From: anup at brainfault.org (Anup Patel)
Date: Sat, 30 Jul 2022 10:55:47 +0530
Subject: [PATCH 2/4] lib: utils/serial: Initialize platform_uart_data to zero
In-Reply-To: <20220718172028.2006166-3-ajones@ventanamicro.com>
References: <20220718172028.2006166-1-ajones@ventanamicro.com> <20220718172028.2006166-3-ajones@ventanamicro.com>
Message-ID: <CAAhSdy1m=ML9knL-mrBMwF5ByMu9pk4Mq6ey-DPj=JFBH7u2Kw@mail.gmail.com>

On Mon, Jul 18, 2022 at 10:50 PM Andrew Jones <ajones at ventanamicro.com> wrote:
>
> While it doesn't look like there are any current cases of using
> uninitialized data, let's zero all the UART data members to be
> safe. Zero may not actually be better than a random number in
> some cases, so all structure members should still be validated
> before use, but at least zero is usually easier to debug than
> some random stack garbage...
>
> Signed-off-by: Andrew Jones <ajones at ventanamicro.com>

Looks good to me.

Reviewed-by: Anup Patel <anup at brainfault.org>

Regards,
Anup

> ---
>  lib/utils/serial/fdt_serial_gaisler.c       | 2 +-
>  lib/utils/serial/fdt_serial_shakti.c        | 2 +-
>  lib/utils/serial/fdt_serial_sifive.c        | 2 +-
>  lib/utils/serial/fdt_serial_uart8250.c      | 2 +-
>  lib/utils/serial/fdt_serial_xlnx_uartlite.c | 2 +-
>  platform/fpga/openpiton/platform.c          | 2 +-
>  6 files changed, 6 insertions(+), 6 deletions(-)
>
> diff --git a/lib/utils/serial/fdt_serial_gaisler.c b/lib/utils/serial/fdt_serial_gaisler.c
> index 9a9f9a8b7962..74988e34bcd8 100644
> --- a/lib/utils/serial/fdt_serial_gaisler.c
> +++ b/lib/utils/serial/fdt_serial_gaisler.c
> @@ -15,7 +15,7 @@ static int serial_gaisler_init(void *fdt, int nodeoff,
>                                const struct fdt_match *match)
>  {
>         int rc;
> -       struct platform_uart_data uart;
> +       struct platform_uart_data uart = { 0 };
>
>         rc = fdt_parse_gaisler_uart_node(fdt, nodeoff, &uart);
>         if (rc)
> diff --git a/lib/utils/serial/fdt_serial_shakti.c b/lib/utils/serial/fdt_serial_shakti.c
> index 4f91419ef53c..0e056303dbb5 100644
> --- a/lib/utils/serial/fdt_serial_shakti.c
> +++ b/lib/utils/serial/fdt_serial_shakti.c
> @@ -13,7 +13,7 @@ static int serial_shakti_init(void *fdt, int nodeoff,
>                                 const struct fdt_match *match)
>  {
>         int rc;
> -       struct platform_uart_data uart;
> +       struct platform_uart_data uart = { 0 };
>
>         rc = fdt_parse_shakti_uart_node(fdt, nodeoff, &uart);
>         if (rc)
> diff --git a/lib/utils/serial/fdt_serial_sifive.c b/lib/utils/serial/fdt_serial_sifive.c
> index f4c833c1573d..3ca10913eee3 100644
> --- a/lib/utils/serial/fdt_serial_sifive.c
> +++ b/lib/utils/serial/fdt_serial_sifive.c
> @@ -15,7 +15,7 @@ static int serial_sifive_init(void *fdt, int nodeoff,
>                                 const struct fdt_match *match)
>  {
>         int rc;
> -       struct platform_uart_data uart;
> +       struct platform_uart_data uart = { 0 };
>
>         rc = fdt_parse_sifive_uart_node(fdt, nodeoff, &uart);
>         if (rc)
> diff --git a/lib/utils/serial/fdt_serial_uart8250.c b/lib/utils/serial/fdt_serial_uart8250.c
> index 544b7416de42..0b95f2d9e44e 100644
> --- a/lib/utils/serial/fdt_serial_uart8250.c
> +++ b/lib/utils/serial/fdt_serial_uart8250.c
> @@ -15,7 +15,7 @@ static int serial_uart8250_init(void *fdt, int nodeoff,
>                                 const struct fdt_match *match)
>  {
>         int rc;
> -       struct platform_uart_data uart;
> +       struct platform_uart_data uart = { 0 };
>
>         rc = fdt_parse_uart8250_node(fdt, nodeoff, &uart);
>         if (rc)
> diff --git a/lib/utils/serial/fdt_serial_xlnx_uartlite.c b/lib/utils/serial/fdt_serial_xlnx_uartlite.c
> index 466e16e82d8c..9f04aea3673b 100644
> --- a/lib/utils/serial/fdt_serial_xlnx_uartlite.c
> +++ b/lib/utils/serial/fdt_serial_xlnx_uartlite.c
> @@ -15,7 +15,7 @@ static int serial_xlnx_uartlite_init(void *fdt, int nodeoff,
>                                 const struct fdt_match *match)
>  {
>         int rc;
> -       struct platform_uart_data uart;
> +       struct platform_uart_data uart = { 0 };
>
>         rc = fdt_parse_xlnx_uartlite_node(fdt, nodeoff, &uart);
>         if (rc)
> diff --git a/platform/fpga/openpiton/platform.c b/platform/fpga/openpiton/platform.c
> index 7ca21236bfef..5ff7d200aba9 100644
> --- a/platform/fpga/openpiton/platform.c
> +++ b/platform/fpga/openpiton/platform.c
> @@ -69,7 +69,7 @@ static struct aclint_mtimer_data mtimer = {
>  static int openpiton_early_init(bool cold_boot)
>  {
>         void *fdt;
> -       struct platform_uart_data uart_data;
> +       struct platform_uart_data uart_data = { 0 };
>         struct plic_data plic_data;
>         unsigned long aclint_freq;
>         uint64_t clint_addr;
> --
> 2.36.1
>
>
> --
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi


From anup at brainfault.org  Fri Jul 29 22:27:03 2022
From: anup at brainfault.org (Anup Patel)
Date: Sat, 30 Jul 2022 10:57:03 +0530
Subject: [PATCH 3/4] lib: serial: Clean up coding style in sifive-uart.c
In-Reply-To: <20220718172028.2006166-4-ajones@ventanamicro.com>
References: <20220718172028.2006166-1-ajones@ventanamicro.com> <20220718172028.2006166-4-ajones@ventanamicro.com>
Message-ID: <CAAhSdy1HfEsFeXxZJK=3JJx1BygeQ4dnYejFJfQ4+kZiauqi=g@mail.gmail.com>

On Mon, Jul 18, 2022 at 10:50 PM Andrew Jones <ajones at ventanamicro.com> wrote:
>
> Signed-off-by: Andrew Jones <ajones at ventanamicro.com>

Looks good to me.

Reviewed-by: Anup Patel <anup at brainfault.org>

Regards,
Anup

> ---
>  lib/utils/serial/sifive-uart.c | 11 ++++++++---
>  1 file changed, 8 insertions(+), 3 deletions(-)
>
> diff --git a/lib/utils/serial/sifive-uart.c b/lib/utils/serial/sifive-uart.c
> index 9478a77f8054..7078611a5274 100644
> --- a/lib/utils/serial/sifive-uart.c
> +++ b/lib/utils/serial/sifive-uart.c
> @@ -48,12 +48,12 @@ static inline unsigned int uart_min_clk_divisor(uint64_t in_freq,
>                                                 uint64_t max_target_hz)
>  {
>         uint64_t quotient = (in_freq + max_target_hz - 1) / (max_target_hz);
> +
>         /* Avoid underflow */
> -       if (quotient == 0) {
> +       if (quotient == 0)
>                 return 0;
> -       } else {
> +       else
>                 return quotient - 1;
> -       }
>  }
>
>  static u32 get_reg(u32 num)
> @@ -77,8 +77,10 @@ static void sifive_uart_putc(char ch)
>  static int sifive_uart_getc(void)
>  {
>         u32 ret = get_reg(UART_REG_RXFIFO);
> +
>         if (!(ret & UART_RXFIFO_EMPTY))
>                 return ret & UART_RXFIFO_DATA;
> +
>         return -1;
>  }
>
> @@ -97,10 +99,13 @@ int sifive_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
>         /* Configure baudrate */
>         if (in_freq)
>                 set_reg(UART_REG_DIV, uart_min_clk_divisor(in_freq, baudrate));
> +
>         /* Disable interrupts */
>         set_reg(UART_REG_IE, 0);
> +
>         /* Enable TX */
>         set_reg(UART_REG_TXCTRL, UART_TXCTRL_TXEN);
> +
>         /* Enable Rx */
>         set_reg(UART_REG_RXCTRL, UART_RXCTRL_RXEN);
>
> --
> 2.36.1
>
>
> --
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi


From anup at brainfault.org  Fri Jul 29 22:31:10 2022
From: anup at brainfault.org (Anup Patel)
Date: Sat, 30 Jul 2022 11:01:10 +0530
Subject: [PATCH 4/4] lib: utils/serial: Ensure baudrate is non-zero before using
In-Reply-To: <20220718172028.2006166-5-ajones@ventanamicro.com>
References: <20220718172028.2006166-1-ajones@ventanamicro.com> <20220718172028.2006166-5-ajones@ventanamicro.com>
Message-ID: <CAAhSdy1=zo7HYCGe-W4TZjF4Lr+35NNXDEd6i+EQsiX6OJ4coQ@mail.gmail.com>

On Mon, Jul 18, 2022 at 10:50 PM Andrew Jones <ajones at ventanamicro.com> wrote:
>
> RISC-V doesn't generate exceptions on divide-by-zero, but the result,
> all bits set, is not likely what people expect either. In all cases
> where we divide by baudrate there's a chance it's zero (when the DT
> it came from is "bad"). To avoid difficult to debug situations, leave
> baudrate dependent registers alone when baudrate is zero, as, also in
> all cases, it appears we can skip initialization of those registers
> and still [hopefully] have a functioning UART.
>
> Signed-off-by: Andrew Jones <ajones at ventanamicro.com>

Looks good to me.

Reviewed-by: Anup Patel <anup at brainfault.org>

Regards,
Anup

> ---
>  lib/utils/serial/gaisler-uart.c | 2 +-
>  lib/utils/serial/shakti-uart.c  | 8 ++++++--
>  lib/utils/serial/sifive-uart.c  | 2 +-
>  lib/utils/serial/uart8250.c     | 7 +++++--
>  4 files changed, 13 insertions(+), 6 deletions(-)
>
> diff --git a/lib/utils/serial/gaisler-uart.c b/lib/utils/serial/gaisler-uart.c
> index 5f30ee43b719..eec75cca0c98 100644
> --- a/lib/utils/serial/gaisler-uart.c
> +++ b/lib/utils/serial/gaisler-uart.c
> @@ -70,7 +70,7 @@ int gaisler_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
>         uart_base = (volatile char *)base;
>
>         /* Configure baudrate */
> -       if (in_freq)
> +       if (in_freq && baudrate)
>                 set_reg(UART_REG_SCALER, in_freq / (baudrate * 8 + 7));
>
>         ctrl = get_reg(UART_REG_CTRL);
> diff --git a/lib/utils/serial/shakti-uart.c b/lib/utils/serial/shakti-uart.c
> index 5f2fe75c9f33..2f09f141b9cf 100644
> --- a/lib/utils/serial/shakti-uart.c
> +++ b/lib/utils/serial/shakti-uart.c
> @@ -47,8 +47,12 @@ static struct sbi_console_device shakti_console = {
>  int shakti_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
>  {
>         uart_base = (volatile char *)base;
> -       u16 baud = (u16)(in_freq/(16 * baudrate));
> -       writew(baud, uart_base + REG_BAUD);
> +       u16 baud;
> +
> +       if (baudrate) {
> +               baud = (u16)(in_freq / (16 * baudrate));
> +               writew(baud, uart_base + REG_BAUD);
> +       }
>
>         sbi_console_set_device(&shakti_console);
>
> diff --git a/lib/utils/serial/sifive-uart.c b/lib/utils/serial/sifive-uart.c
> index 7078611a5274..3581d47a6d4b 100644
> --- a/lib/utils/serial/sifive-uart.c
> +++ b/lib/utils/serial/sifive-uart.c
> @@ -97,7 +97,7 @@ int sifive_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
>         uart_baudrate = baudrate;
>
>         /* Configure baudrate */
> -       if (in_freq)
> +       if (in_freq && baudrate)
>                 set_reg(UART_REG_DIV, uart_min_clk_divisor(in_freq, baudrate));
>
>         /* Disable interrupts */
> diff --git a/lib/utils/serial/uart8250.c b/lib/utils/serial/uart8250.c
> index 38ea11af4d31..99bf1bf733f2 100644
> --- a/lib/utils/serial/uart8250.c
> +++ b/lib/utils/serial/uart8250.c
> @@ -93,7 +93,7 @@ static struct sbi_console_device uart8250_console = {
>  int uart8250_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift,
>                   u32 reg_width, u32 reg_offset)
>  {
> -       u16 bdiv;
> +       u16 bdiv = 0;
>
>         uart8250_base      = (volatile char *)base + reg_offset;
>         uart8250_reg_shift = reg_shift;
> @@ -101,7 +101,10 @@ int uart8250_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift,
>         uart8250_in_freq   = in_freq;
>         uart8250_baudrate  = baudrate;
>
> -       bdiv = (uart8250_in_freq + 8 * uart8250_baudrate) / (16 * uart8250_baudrate);
> +       if (uart8250_baudrate) {
> +               bdiv = (uart8250_in_freq + 8 * uart8250_baudrate) /
> +                      (16 * uart8250_baudrate);
> +       }
>
>         /* Disable all interrupts */
>         set_reg(UART_IER_OFFSET, 0x00);
> --
> 2.36.1
>
>
> --
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi


From anup at brainfault.org  Sat Jul 30 03:19:46 2022
From: anup at brainfault.org (Anup Patel)
Date: Sat, 30 Jul 2022 15:49:46 +0530
Subject: [PATCH 1/4] lib: utils/fdt: Factor out common uart node code
In-Reply-To: <CAAhSdy0Crf3puRUJehFaVc5TtSb7gyad26bR_DpQ5wnUr=aT1A@mail.gmail.com>
References: <20220718172028.2006166-1-ajones@ventanamicro.com>
 <20220718172028.2006166-2-ajones@ventanamicro.com> <CAAhSdy0Crf3puRUJehFaVc5TtSb7gyad26bR_DpQ5wnUr=aT1A@mail.gmail.com>
Message-ID: <CAAhSdy2GzcnFPF-gs44BVrbtNGPHJHVh5D6n7ZNukM8Nm3FuKQ@mail.gmail.com>

On Sat, Jul 30, 2022 at 10:54 AM Anup Patel <anup at brainfault.org> wrote:
>
> On Mon, Jul 18, 2022 at 10:50 PM Andrew Jones <ajones at ventanamicro.com> wrote:
> >
> > Factor out the common code used by the fdt UART node parsers,
> > allowing us to drop duplicate code.
> >
> > Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
>
> Looks good to me.
>
> Reviewed-by: Anup Patel <anup at brainfault.org>

Applied this patch to the riscv/opensbi repo.

Thanks,
Anup

>
> Regards,
> Anup
>
> > ---
> >  lib/utils/fdt/fdt_helper.c | 131 +++++++++++--------------------------
> >  1 file changed, 39 insertions(+), 92 deletions(-)
> >
> > diff --git a/lib/utils/fdt/fdt_helper.c b/lib/utils/fdt/fdt_helper.c
> > index 2bdb1ef23e55..c6abf804014d 100644
> > --- a/lib/utils/fdt/fdt_helper.c
> > +++ b/lib/utils/fdt/fdt_helper.c
> > @@ -314,8 +314,10 @@ int fdt_parse_timebase_frequency(void *fdt, unsigned long *freq)
> >         return 0;
> >  }
> >
> > -int fdt_parse_gaisler_uart_node(void *fdt, int nodeoffset,
> > -                               struct platform_uart_data *uart)
> > +static int fdt_parse_uart_node_common(void *fdt, int nodeoffset,
> > +                                     struct platform_uart_data *uart,
> > +                                     unsigned long default_freq,
> > +                                     unsigned long default_baud)
> >  {
> >         int len, rc;
> >         const fdt32_t *val;
> > @@ -338,13 +340,28 @@ int fdt_parse_gaisler_uart_node(void *fdt, int nodeoffset,
> >         if (len > 0 && val)
> >                 uart->freq = fdt32_to_cpu(*val);
> >         else
> > -               uart->freq = DEFAULT_UART_FREQ;
> > +               uart->freq = default_freq;
> >
> >         val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "current-speed", &len);
> >         if (len > 0 && val)
> >                 uart->baud = fdt32_to_cpu(*val);
> >         else
> > -               uart->baud = DEFAULT_UART_BAUD;
> > +               uart->baud = default_baud;
> > +
> > +       return 0;
> > +}
> > +
> > +int fdt_parse_gaisler_uart_node(void *fdt, int nodeoffset,
> > +                               struct platform_uart_data *uart)
> > +{
> > +       int rc;
> > +
> > +       rc = fdt_parse_uart_node_common(fdt, nodeoffset, uart,
> > +                                       DEFAULT_UART_FREQ,
> > +                                       DEFAULT_UART_BAUD);
> > +
> > +       if (rc)
> > +               return rc;
> >
> >         /* For Gaisler APBUART, the reg-shift and reg-io-width are fixed .*/
> >         uart->reg_shift    = DEFAULT_UART_REG_SHIFT;
> > @@ -356,69 +373,26 @@ int fdt_parse_gaisler_uart_node(void *fdt, int nodeoffset,
> >  int fdt_parse_shakti_uart_node(void *fdt, int nodeoffset,
> >                                struct platform_uart_data *uart)
> >  {
> > -       int len, rc;
> > -       const fdt32_t *val;
> > -       uint64_t reg_addr, reg_size;
> > -
> > -       if (nodeoffset < 0 || !uart || !fdt)
> > -               return SBI_ENODEV;
> > -
> > -       rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
> > -                                   &reg_addr, &reg_size);
> > -       if (rc < 0 || !reg_addr || !reg_size)
> > -               return SBI_ENODEV;
> > -       uart->addr = reg_addr;
> > +       int rc;
> >
> > -       /**
> > -        * UART address is mandaotry. clock-frequency and current-speed
> > -        * may not be present. Don't return error.
> > -        */
> > -       val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "clock-frequency", &len);
> > -       if (len > 0 && val)
> > -               uart->freq = fdt32_to_cpu(*val);
> > -       else
> > -               uart->freq = DEFAULT_SHAKTI_UART_FREQ;
> > +       rc = fdt_parse_uart_node_common(fdt, nodeoffset, uart,
> > +                                       DEFAULT_SHAKTI_UART_FREQ,
> > +                                       DEFAULT_SHAKTI_UART_BAUD);
> >
> > -       val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "current-speed", &len);
> > -       if (len > 0 && val)
> > -               uart->baud = fdt32_to_cpu(*val);
> > -       else
> > -               uart->baud = DEFAULT_SHAKTI_UART_BAUD;
> > -
> > -       return 0;
> > +       return rc ? : 0;
> >  }
> >
> >  int fdt_parse_sifive_uart_node(void *fdt, int nodeoffset,
> >                                struct platform_uart_data *uart)
> >  {
> > -       int len, rc;
> > -       const fdt32_t *val;
> > -       uint64_t reg_addr, reg_size;
> > -
> > -       if (nodeoffset < 0 || !uart || !fdt)
> > -               return SBI_ENODEV;
> > -
> > -       rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
> > -                                   &reg_addr, &reg_size);
> > -       if (rc < 0 || !reg_addr || !reg_size)
> > -               return SBI_ENODEV;
> > -       uart->addr = reg_addr;
> > +       int rc;
> >
> > -       /**
> > -        * UART address is mandaotry. clock-frequency and current-speed
> > -        * may not be present. Don't return error.
> > -        */
> > -       val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "clock-frequency", &len);
> > -       if (len > 0 && val)
> > -               uart->freq = fdt32_to_cpu(*val);
> > -       else
> > -               uart->freq = DEFAULT_SIFIVE_UART_FREQ;
> > +       rc = fdt_parse_uart_node_common(fdt, nodeoffset, uart,
> > +                                       DEFAULT_SIFIVE_UART_FREQ,
> > +                                       DEFAULT_SIFIVE_UART_BAUD);
> >
> > -       val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "current-speed", &len);
> > -       if (len > 0 && val)
> > -               uart->baud = fdt32_to_cpu(*val);
> > -       else
> > -               uart->baud = DEFAULT_SIFIVE_UART_BAUD;
> > +       if (rc)
> > +               return rc;
> >
> >         /* For SiFive UART, the reg-shift and reg-io-width are fixed .*/
> >         uart->reg_shift = DEFAULT_SIFIVE_UART_REG_SHIFT;
> > @@ -432,32 +406,13 @@ int fdt_parse_uart8250_node(void *fdt, int nodeoffset,
> >  {
> >         int len, rc;
> >         const fdt32_t *val;
> > -       uint64_t reg_addr, reg_size;
> > -
> > -       if (nodeoffset < 0 || !uart || !fdt)
> > -               return SBI_ENODEV;
> > -
> > -       rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
> > -                                   &reg_addr, &reg_size);
> > -       if (rc < 0 || !reg_addr || !reg_size)
> > -               return SBI_ENODEV;
> > -       uart->addr = reg_addr;
> >
> > -       /**
> > -        * UART address is mandaotry. clock-frequency and current-speed
> > -        * may not be present. Don't return error.
> > -        */
> > -       val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "clock-frequency", &len);
> > -       if (len > 0 && val)
> > -               uart->freq = fdt32_to_cpu(*val);
> > -       else
> > -               uart->freq = DEFAULT_UART_FREQ;
> > +       rc = fdt_parse_uart_node_common(fdt, nodeoffset, uart,
> > +                                       DEFAULT_UART_FREQ,
> > +                                       DEFAULT_UART_BAUD);
> >
> > -       val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "current-speed", &len);
> > -       if (len > 0 && val)
> > -               uart->baud = fdt32_to_cpu(*val);
> > -       else
> > -               uart->baud = DEFAULT_UART_BAUD;
> > +       if (rc)
> > +               return rc;
> >
> >         val = (fdt32_t *)fdt_getprop(fdt, nodeoffset, "reg-shift", &len);
> >         if (len > 0 && val)
> > @@ -499,18 +454,10 @@ int fdt_parse_xlnx_uartlite_node(void *fdt, int nodeoffset,
> >                                struct platform_uart_data *uart)
> >  {
> >         int rc;
> > -       uint64_t reg_addr, reg_size;
> >
> > -       if (nodeoffset < 0 || !uart || !fdt)
> > -               return SBI_ENODEV;
> > +       rc = fdt_parse_uart_node_common(fdt, nodeoffset, uart, 0, 0);
> >
> > -       rc = fdt_get_node_addr_size(fdt, nodeoffset, 0,
> > -                                   &reg_addr, &reg_size);
> > -       if (rc < 0 || !reg_addr || !reg_size)
> > -               return SBI_ENODEV;
> > -       uart->addr = reg_addr;
> > -
> > -       return 0;
> > +       return rc ? : 0;
> >  }
> >
> >  int fdt_parse_aplic_node(void *fdt, int nodeoff, struct aplic_data *aplic)
> > --
> > 2.36.1
> >
> >
> > --
> > opensbi mailing list
> > opensbi at lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/opensbi


From anup at brainfault.org  Sat Jul 30 03:20:09 2022
From: anup at brainfault.org (Anup Patel)
Date: Sat, 30 Jul 2022 15:50:09 +0530
Subject: [PATCH 2/4] lib: utils/serial: Initialize platform_uart_data to zero
In-Reply-To: <CAAhSdy1m=ML9knL-mrBMwF5ByMu9pk4Mq6ey-DPj=JFBH7u2Kw@mail.gmail.com>
References: <20220718172028.2006166-1-ajones@ventanamicro.com>
 <20220718172028.2006166-3-ajones@ventanamicro.com> <CAAhSdy1m=ML9knL-mrBMwF5ByMu9pk4Mq6ey-DPj=JFBH7u2Kw@mail.gmail.com>
Message-ID: <CAAhSdy0jFVoafmDvbk3YmbN_hKv01WPGp4rvjgSqVRgvpjcYGA@mail.gmail.com>

On Sat, Jul 30, 2022 at 10:55 AM Anup Patel <anup at brainfault.org> wrote:
>
> On Mon, Jul 18, 2022 at 10:50 PM Andrew Jones <ajones at ventanamicro.com> wrote:
> >
> > While it doesn't look like there are any current cases of using
> > uninitialized data, let's zero all the UART data members to be
> > safe. Zero may not actually be better than a random number in
> > some cases, so all structure members should still be validated
> > before use, but at least zero is usually easier to debug than
> > some random stack garbage...
> >
> > Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
>
> Looks good to me.
>
> Reviewed-by: Anup Patel <anup at brainfault.org>

Applied this patch to the riscv/opensbi repo.

Thanks,
Anup

>
> Regards,
> Anup
>
> > ---
> >  lib/utils/serial/fdt_serial_gaisler.c       | 2 +-
> >  lib/utils/serial/fdt_serial_shakti.c        | 2 +-
> >  lib/utils/serial/fdt_serial_sifive.c        | 2 +-
> >  lib/utils/serial/fdt_serial_uart8250.c      | 2 +-
> >  lib/utils/serial/fdt_serial_xlnx_uartlite.c | 2 +-
> >  platform/fpga/openpiton/platform.c          | 2 +-
> >  6 files changed, 6 insertions(+), 6 deletions(-)
> >
> > diff --git a/lib/utils/serial/fdt_serial_gaisler.c b/lib/utils/serial/fdt_serial_gaisler.c
> > index 9a9f9a8b7962..74988e34bcd8 100644
> > --- a/lib/utils/serial/fdt_serial_gaisler.c
> > +++ b/lib/utils/serial/fdt_serial_gaisler.c
> > @@ -15,7 +15,7 @@ static int serial_gaisler_init(void *fdt, int nodeoff,
> >                                const struct fdt_match *match)
> >  {
> >         int rc;
> > -       struct platform_uart_data uart;
> > +       struct platform_uart_data uart = { 0 };
> >
> >         rc = fdt_parse_gaisler_uart_node(fdt, nodeoff, &uart);
> >         if (rc)
> > diff --git a/lib/utils/serial/fdt_serial_shakti.c b/lib/utils/serial/fdt_serial_shakti.c
> > index 4f91419ef53c..0e056303dbb5 100644
> > --- a/lib/utils/serial/fdt_serial_shakti.c
> > +++ b/lib/utils/serial/fdt_serial_shakti.c
> > @@ -13,7 +13,7 @@ static int serial_shakti_init(void *fdt, int nodeoff,
> >                                 const struct fdt_match *match)
> >  {
> >         int rc;
> > -       struct platform_uart_data uart;
> > +       struct platform_uart_data uart = { 0 };
> >
> >         rc = fdt_parse_shakti_uart_node(fdt, nodeoff, &uart);
> >         if (rc)
> > diff --git a/lib/utils/serial/fdt_serial_sifive.c b/lib/utils/serial/fdt_serial_sifive.c
> > index f4c833c1573d..3ca10913eee3 100644
> > --- a/lib/utils/serial/fdt_serial_sifive.c
> > +++ b/lib/utils/serial/fdt_serial_sifive.c
> > @@ -15,7 +15,7 @@ static int serial_sifive_init(void *fdt, int nodeoff,
> >                                 const struct fdt_match *match)
> >  {
> >         int rc;
> > -       struct platform_uart_data uart;
> > +       struct platform_uart_data uart = { 0 };
> >
> >         rc = fdt_parse_sifive_uart_node(fdt, nodeoff, &uart);
> >         if (rc)
> > diff --git a/lib/utils/serial/fdt_serial_uart8250.c b/lib/utils/serial/fdt_serial_uart8250.c
> > index 544b7416de42..0b95f2d9e44e 100644
> > --- a/lib/utils/serial/fdt_serial_uart8250.c
> > +++ b/lib/utils/serial/fdt_serial_uart8250.c
> > @@ -15,7 +15,7 @@ static int serial_uart8250_init(void *fdt, int nodeoff,
> >                                 const struct fdt_match *match)
> >  {
> >         int rc;
> > -       struct platform_uart_data uart;
> > +       struct platform_uart_data uart = { 0 };
> >
> >         rc = fdt_parse_uart8250_node(fdt, nodeoff, &uart);
> >         if (rc)
> > diff --git a/lib/utils/serial/fdt_serial_xlnx_uartlite.c b/lib/utils/serial/fdt_serial_xlnx_uartlite.c
> > index 466e16e82d8c..9f04aea3673b 100644
> > --- a/lib/utils/serial/fdt_serial_xlnx_uartlite.c
> > +++ b/lib/utils/serial/fdt_serial_xlnx_uartlite.c
> > @@ -15,7 +15,7 @@ static int serial_xlnx_uartlite_init(void *fdt, int nodeoff,
> >                                 const struct fdt_match *match)
> >  {
> >         int rc;
> > -       struct platform_uart_data uart;
> > +       struct platform_uart_data uart = { 0 };
> >
> >         rc = fdt_parse_xlnx_uartlite_node(fdt, nodeoff, &uart);
> >         if (rc)
> > diff --git a/platform/fpga/openpiton/platform.c b/platform/fpga/openpiton/platform.c
> > index 7ca21236bfef..5ff7d200aba9 100644
> > --- a/platform/fpga/openpiton/platform.c
> > +++ b/platform/fpga/openpiton/platform.c
> > @@ -69,7 +69,7 @@ static struct aclint_mtimer_data mtimer = {
> >  static int openpiton_early_init(bool cold_boot)
> >  {
> >         void *fdt;
> > -       struct platform_uart_data uart_data;
> > +       struct platform_uart_data uart_data = { 0 };
> >         struct plic_data plic_data;
> >         unsigned long aclint_freq;
> >         uint64_t clint_addr;
> > --
> > 2.36.1
> >
> >
> > --
> > opensbi mailing list
> > opensbi at lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/opensbi


From anup at brainfault.org  Sat Jul 30 03:20:29 2022
From: anup at brainfault.org (Anup Patel)
Date: Sat, 30 Jul 2022 15:50:29 +0530
Subject: [PATCH 3/4] lib: serial: Clean up coding style in sifive-uart.c
In-Reply-To: <CAAhSdy1HfEsFeXxZJK=3JJx1BygeQ4dnYejFJfQ4+kZiauqi=g@mail.gmail.com>
References: <20220718172028.2006166-1-ajones@ventanamicro.com>
 <20220718172028.2006166-4-ajones@ventanamicro.com> <CAAhSdy1HfEsFeXxZJK=3JJx1BygeQ4dnYejFJfQ4+kZiauqi=g@mail.gmail.com>
Message-ID: <CAAhSdy16T8i-GzUYY-uBmvSQiJw7khPBWsfrL5C4i+zMMzo7=g@mail.gmail.com>

On Sat, Jul 30, 2022 at 10:57 AM Anup Patel <anup at brainfault.org> wrote:
>
> On Mon, Jul 18, 2022 at 10:50 PM Andrew Jones <ajones at ventanamicro.com> wrote:
> >
> > Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
>
> Looks good to me.
>
> Reviewed-by: Anup Patel <anup at brainfault.org>

Applied this patch to the riscv/opensbi repo.

Thanks,
Anup

>
> Regards,
> Anup
>
> > ---
> >  lib/utils/serial/sifive-uart.c | 11 ++++++++---
> >  1 file changed, 8 insertions(+), 3 deletions(-)
> >
> > diff --git a/lib/utils/serial/sifive-uart.c b/lib/utils/serial/sifive-uart.c
> > index 9478a77f8054..7078611a5274 100644
> > --- a/lib/utils/serial/sifive-uart.c
> > +++ b/lib/utils/serial/sifive-uart.c
> > @@ -48,12 +48,12 @@ static inline unsigned int uart_min_clk_divisor(uint64_t in_freq,
> >                                                 uint64_t max_target_hz)
> >  {
> >         uint64_t quotient = (in_freq + max_target_hz - 1) / (max_target_hz);
> > +
> >         /* Avoid underflow */
> > -       if (quotient == 0) {
> > +       if (quotient == 0)
> >                 return 0;
> > -       } else {
> > +       else
> >                 return quotient - 1;
> > -       }
> >  }
> >
> >  static u32 get_reg(u32 num)
> > @@ -77,8 +77,10 @@ static void sifive_uart_putc(char ch)
> >  static int sifive_uart_getc(void)
> >  {
> >         u32 ret = get_reg(UART_REG_RXFIFO);
> > +
> >         if (!(ret & UART_RXFIFO_EMPTY))
> >                 return ret & UART_RXFIFO_DATA;
> > +
> >         return -1;
> >  }
> >
> > @@ -97,10 +99,13 @@ int sifive_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
> >         /* Configure baudrate */
> >         if (in_freq)
> >                 set_reg(UART_REG_DIV, uart_min_clk_divisor(in_freq, baudrate));
> > +
> >         /* Disable interrupts */
> >         set_reg(UART_REG_IE, 0);
> > +
> >         /* Enable TX */
> >         set_reg(UART_REG_TXCTRL, UART_TXCTRL_TXEN);
> > +
> >         /* Enable Rx */
> >         set_reg(UART_REG_RXCTRL, UART_RXCTRL_RXEN);
> >
> > --
> > 2.36.1
> >
> >
> > --
> > opensbi mailing list
> > opensbi at lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/opensbi


From anup at brainfault.org  Sat Jul 30 03:21:02 2022
From: anup at brainfault.org (Anup Patel)
Date: Sat, 30 Jul 2022 15:51:02 +0530
Subject: [PATCH 4/4] lib: utils/serial: Ensure baudrate is non-zero before using
In-Reply-To: <CAAhSdy1=zo7HYCGe-W4TZjF4Lr+35NNXDEd6i+EQsiX6OJ4coQ@mail.gmail.com>
References: <20220718172028.2006166-1-ajones@ventanamicro.com>
 <20220718172028.2006166-5-ajones@ventanamicro.com> <CAAhSdy1=zo7HYCGe-W4TZjF4Lr+35NNXDEd6i+EQsiX6OJ4coQ@mail.gmail.com>
Message-ID: <CAAhSdy2nSQhEfpbYtAs3daAPyMPCPzF83cdWGeU+THyW=n6Nug@mail.gmail.com>

On Sat, Jul 30, 2022 at 11:01 AM Anup Patel <anup at brainfault.org> wrote:
>
> On Mon, Jul 18, 2022 at 10:50 PM Andrew Jones <ajones at ventanamicro.com> wrote:
> >
> > RISC-V doesn't generate exceptions on divide-by-zero, but the result,
> > all bits set, is not likely what people expect either. In all cases
> > where we divide by baudrate there's a chance it's zero (when the DT
> > it came from is "bad"). To avoid difficult to debug situations, leave
> > baudrate dependent registers alone when baudrate is zero, as, also in
> > all cases, it appears we can skip initialization of those registers
> > and still [hopefully] have a functioning UART.
> >
> > Signed-off-by: Andrew Jones <ajones at ventanamicro.com>
>
> Looks good to me.
>
> Reviewed-by: Anup Patel <anup at brainfault.org>

Applied this patch to the riscv/opensbi repo.

Thanks,
Anup

>
> Regards,
> Anup
>
> > ---
> >  lib/utils/serial/gaisler-uart.c | 2 +-
> >  lib/utils/serial/shakti-uart.c  | 8 ++++++--
> >  lib/utils/serial/sifive-uart.c  | 2 +-
> >  lib/utils/serial/uart8250.c     | 7 +++++--
> >  4 files changed, 13 insertions(+), 6 deletions(-)
> >
> > diff --git a/lib/utils/serial/gaisler-uart.c b/lib/utils/serial/gaisler-uart.c
> > index 5f30ee43b719..eec75cca0c98 100644
> > --- a/lib/utils/serial/gaisler-uart.c
> > +++ b/lib/utils/serial/gaisler-uart.c
> > @@ -70,7 +70,7 @@ int gaisler_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
> >         uart_base = (volatile char *)base;
> >
> >         /* Configure baudrate */
> > -       if (in_freq)
> > +       if (in_freq && baudrate)
> >                 set_reg(UART_REG_SCALER, in_freq / (baudrate * 8 + 7));
> >
> >         ctrl = get_reg(UART_REG_CTRL);
> > diff --git a/lib/utils/serial/shakti-uart.c b/lib/utils/serial/shakti-uart.c
> > index 5f2fe75c9f33..2f09f141b9cf 100644
> > --- a/lib/utils/serial/shakti-uart.c
> > +++ b/lib/utils/serial/shakti-uart.c
> > @@ -47,8 +47,12 @@ static struct sbi_console_device shakti_console = {
> >  int shakti_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
> >  {
> >         uart_base = (volatile char *)base;
> > -       u16 baud = (u16)(in_freq/(16 * baudrate));
> > -       writew(baud, uart_base + REG_BAUD);
> > +       u16 baud;
> > +
> > +       if (baudrate) {
> > +               baud = (u16)(in_freq / (16 * baudrate));
> > +               writew(baud, uart_base + REG_BAUD);
> > +       }
> >
> >         sbi_console_set_device(&shakti_console);
> >
> > diff --git a/lib/utils/serial/sifive-uart.c b/lib/utils/serial/sifive-uart.c
> > index 7078611a5274..3581d47a6d4b 100644
> > --- a/lib/utils/serial/sifive-uart.c
> > +++ b/lib/utils/serial/sifive-uart.c
> > @@ -97,7 +97,7 @@ int sifive_uart_init(unsigned long base, u32 in_freq, u32 baudrate)
> >         uart_baudrate = baudrate;
> >
> >         /* Configure baudrate */
> > -       if (in_freq)
> > +       if (in_freq && baudrate)
> >                 set_reg(UART_REG_DIV, uart_min_clk_divisor(in_freq, baudrate));
> >
> >         /* Disable interrupts */
> > diff --git a/lib/utils/serial/uart8250.c b/lib/utils/serial/uart8250.c
> > index 38ea11af4d31..99bf1bf733f2 100644
> > --- a/lib/utils/serial/uart8250.c
> > +++ b/lib/utils/serial/uart8250.c
> > @@ -93,7 +93,7 @@ static struct sbi_console_device uart8250_console = {
> >  int uart8250_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift,
> >                   u32 reg_width, u32 reg_offset)
> >  {
> > -       u16 bdiv;
> > +       u16 bdiv = 0;
> >
> >         uart8250_base      = (volatile char *)base + reg_offset;
> >         uart8250_reg_shift = reg_shift;
> > @@ -101,7 +101,10 @@ int uart8250_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift,
> >         uart8250_in_freq   = in_freq;
> >         uart8250_baudrate  = baudrate;
> >
> > -       bdiv = (uart8250_in_freq + 8 * uart8250_baudrate) / (16 * uart8250_baudrate);
> > +       if (uart8250_baudrate) {
> > +               bdiv = (uart8250_in_freq + 8 * uart8250_baudrate) /
> > +                      (16 * uart8250_baudrate);
> > +       }
> >
> >         /* Disable all interrupts */
> >         set_reg(UART_IER_OFFSET, 0x00);
> > --
> > 2.36.1
> >
> >
> > --
> > opensbi mailing list
> > opensbi at lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/opensbi


From anup at brainfault.org  Sat Jul 30 03:23:02 2022
From: anup at brainfault.org (Anup Patel)
Date: Sat, 30 Jul 2022 15:53:02 +0530
Subject: [PATCH v3] lib: sbi: Fix sbi_snprintf
In-Reply-To: <20220727141747.4793-1-ajones@ventanamicro.com>
References: <20220727141747.4793-1-ajones@ventanamicro.com>
Message-ID: <CAAhSdy1=wcB4EDQxoU19XUV8f0QNBPuY7B_2aO-2b7ADAyt1sg@mail.gmail.com>

On Wed, Jul 27, 2022 at 7:47 PM Andrew Jones <ajones at ventanamicro.com> wrote:
>
> printc would happily write to 'out' even when 'out_len' was zero,
> potentially overflowing buffers. Rework printc to not do that and
> also ensure the null byte is written at the last position when
> necessary, as stated in the snprintf man page. Also, panic if
> sprintf or snprintf are called with NULL output strings (except
> the special case of snprintf having a NULL output string and
> a zero output size, allowing it to be used to get the number of
> characters that would have been written). Finally, rename a
> goto label which clashed with 'out'.
>
> Fixes: 9e8ff05cb61f ("Initial commit.")
> Signed-off-by: Andrew Jones <ajones at ventanamicro.com>

Looks good to me.

Reviewed-by: Anup Patel <anup at brainfault.org>

Applied this patch to the riscv/opensbi repo.

Thanks,
Anup

> ---
>
> v3:
>   - Add comment in printc stating our expectations of *out
>
> v2:
>   - Simply forbid *out == NULL by panicing when it's detected.
>     (The error message for snprintf has been split over two
>      lines to avoid going over 80 chars. I'd prefer error messages
>      not be split, but that seems like the general practice for
>      opensbi.)
>   - Drop some branches, particularly the extra 'if (out)' in print(),
>     by always writing a '\0' on each printc [Xiang W]
>
>
>  lib/sbi/sbi_console.c | 39 +++++++++++++++++++++++----------------
>  1 file changed, 23 insertions(+), 16 deletions(-)
>
> diff --git a/lib/sbi/sbi_console.c b/lib/sbi/sbi_console.c
> index 34c843d3f9e3..7b9be4a4667f 100644
> --- a/lib/sbi/sbi_console.c
> +++ b/lib/sbi/sbi_console.c
> @@ -76,20 +76,22 @@ typedef __builtin_va_list va_list;
>
>  static void printc(char **out, u32 *out_len, char ch)
>  {
> -       if (out) {
> -               if (*out) {
> -                       if (out_len && (0 < *out_len)) {
> -                               **out = ch;
> -                               ++(*out);
> -                               (*out_len)--;
> -                       } else {
> -                               **out = ch;
> -                               ++(*out);
> -                       }
> -               }
> -       } else {
> +       if (!out) {
>                 sbi_putc(ch);
> +               return;
>         }
> +
> +       /*
> +        * The *printf entry point functions have enforced that (*out) can
> +        * only be null when out_len is non-null and its value is zero.
> +        */
> +       if (!out_len || *out_len > 1) {
> +               *(*out)++ = ch;
> +               **out = '\0';
> +       }
> +
> +       if (out_len && *out_len > 0)
> +               --(*out_len);
>  }
>
>  static int prints(char **out, u32 *out_len, const char *string, int width,
> @@ -193,7 +195,7 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
>                         if (*format == '\0')
>                                 break;
>                         if (*format == '%')
> -                               goto out;
> +                               goto literal;
>                         /* Get flags */
>                         if (*format == '-') {
>                                 ++format;
> @@ -332,13 +334,11 @@ static int print(char **out, u32 *out_len, const char *format, va_list args)
>                                 continue;
>                         }
>                 } else {
> -               out:
> +literal:
>                         printc(out, out_len, *format);
>                         ++pc;
>                 }
>         }
> -       if (out)
> -               **out = '\0';
>
>         return pc;
>  }
> @@ -348,6 +348,9 @@ int sbi_sprintf(char *out, const char *format, ...)
>         va_list args;
>         int retval;
>
> +       if (unlikely(!out))
> +               sbi_panic("sbi_sprintf called with NULL output string\n");
> +
>         va_start(args, format);
>         retval = print(&out, NULL, format, args);
>         va_end(args);
> @@ -360,6 +363,10 @@ int sbi_snprintf(char *out, u32 out_sz, const char *format, ...)
>         va_list args;
>         int retval;
>
> +       if (unlikely(!out && out_sz != 0))
> +               sbi_panic("sbi_snprintf called with NULL output string and "
> +                         "output size is not zero\n");
> +
>         va_start(args, format);
>         retval = print(&out, &out_sz, format, args);
>         va_end(args);
> --
> 2.36.1
>
>
> --
> opensbi mailing list
> opensbi at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/opensbi


From anup at brainfault.org  Sat Jul 30 03:23:59 2022
From: anup at brainfault.org (Anup Patel)
Date: Sat, 30 Jul 2022 15:53:59 +0530
Subject: [PATCH v3 1/4] lib: sbi: Fix counter index sanity check
In-Reply-To: <20220720215035.3453553-2-atishp@rivosinc.com>
References: <20220720215035.3453553-1-atishp@rivosinc.com> <20220720215035.3453553-2-atishp@rivosinc.com>
Message-ID: <CAAhSdy0nmtmEDBY7QHn0WdZay-zZWdMTpy1_34N=wCXMwqR6fw@mail.gmail.com>

On Thu, Jul 21, 2022 at 3:20 AM Atish Patra <atishp at rivosinc.com> wrote:
>
> The current implementation computes the possible counter range
> by doing a left shift of counter base. However, this may overflow depending
> on the counter base value. In case of overflow, the highest counter id
> may be computed incorrectly. As per the SBI specification, the respective
> function should return an error if any of the counter is not valid.
>
> Fix the counter index check by avoiding left shifting while doing the
> sanity checks. Without the shift, the implementation just iterates
> over the counter mask and computes the correct counter index by adding
> the base to it.
>
> Reviewed-by: Andrew Jones <ajones at ventanamicro.com>
> Signed-off-by: Atish Patra <atishp at rivosinc.com>

Looks good to me.

Reviewed-by: Anup Patel <anup at brainfault.org>

Applied this patch to the riscv/opensbi repo.

Thanks,
Anup

> ---
>  lib/sbi/sbi_pmu.c | 33 +++++++++++++++++----------------
>  1 file changed, 17 insertions(+), 16 deletions(-)
>
> diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
> index 3f5fd1031b25..31631a2fab80 100644
> --- a/lib/sbi/sbi_pmu.c
> +++ b/lib/sbi/sbi_pmu.c
> @@ -343,25 +343,26 @@ int sbi_pmu_ctr_start(unsigned long cbase, unsigned long cmask,
>  {
>         int event_idx_type;
>         uint32_t event_code;
> -       unsigned long ctr_mask = cmask << cbase;
>         int ret = SBI_EINVAL;
>         bool bUpdate = FALSE;
> +       int i, cidx;
>
> -       if (sbi_fls(ctr_mask) >= total_ctrs)
> +       if ((cbase + sbi_fls(cmask)) >= total_ctrs)
>                 return ret;
>
>         if (flags & SBI_PMU_START_FLAG_SET_INIT_VALUE)
>                 bUpdate = TRUE;
>
> -       for_each_set_bit_from(cbase, &ctr_mask, total_ctrs) {
> -               event_idx_type = pmu_ctr_validate(cbase, &event_code);
> +       for_each_set_bit(i, &cmask, total_ctrs) {
> +               cidx = i + cbase;
> +               event_idx_type = pmu_ctr_validate(cidx, &event_code);
>                 if (event_idx_type < 0)
>                         /* Continue the start operation for other counters */
>                         continue;
>                 else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW)
> -                       ret = pmu_ctr_start_fw(cbase, event_code, ival, bUpdate);
> +                       ret = pmu_ctr_start_fw(cidx, event_code, ival, bUpdate);
>                 else
> -                       ret = pmu_ctr_start_hw(cbase, ival, bUpdate);
> +                       ret = pmu_ctr_start_hw(cidx, ival, bUpdate);
>         }
>
>         return ret;
> @@ -421,25 +422,26 @@ int sbi_pmu_ctr_stop(unsigned long cbase, unsigned long cmask,
>         int ret = SBI_EINVAL;
>         int event_idx_type;
>         uint32_t event_code;
> -       unsigned long ctr_mask = cmask << cbase;
> +       int i, cidx;
>
> -       if (sbi_fls(ctr_mask) >= total_ctrs)
> +       if ((cbase + sbi_fls(cmask)) >= total_ctrs)
>                 return SBI_EINVAL;
>
> -       for_each_set_bit_from(cbase, &ctr_mask, total_ctrs) {
> -               event_idx_type = pmu_ctr_validate(cbase, &event_code);
> +       for_each_set_bit(i, &cmask, total_ctrs) {
> +               cidx = i + cbase;
> +               event_idx_type = pmu_ctr_validate(cidx, &event_code);
>                 if (event_idx_type < 0)
>                         /* Continue the stop operation for other counters */
>                         continue;
>
>                 else if (event_idx_type == SBI_PMU_EVENT_TYPE_FW)
> -                       ret = pmu_ctr_stop_fw(cbase, event_code);
> +                       ret = pmu_ctr_stop_fw(cidx, event_code);
>                 else
> -                       ret = pmu_ctr_stop_hw(cbase);
> +                       ret = pmu_ctr_stop_hw(cidx);
>
>                 if (flag & SBI_PMU_STOP_FLAG_RESET) {
> -                       active_events[hartid][cbase] = SBI_PMU_EVENT_IDX_INVALID;
> -                       pmu_reset_hw_mhpmevent(cbase);
> +                       active_events[hartid][cidx] = SBI_PMU_EVENT_IDX_INVALID;
> +                       pmu_reset_hw_mhpmevent(cidx);
>                 }
>         }
>
> @@ -615,10 +617,9 @@ int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask,
>         int event_type = get_cidx_type(event_idx);
>         struct sbi_pmu_fw_event *fevent;
>         uint32_t fw_evt_code;
> -       unsigned long tmp = cidx_mask << cidx_base;
>
>         /* Do a basic sanity check of counter base & mask */
> -       if (sbi_fls(tmp) >= total_ctrs || event_type >= SBI_PMU_EVENT_TYPE_MAX)
> +       if ((cidx_base + sbi_fls(cidx_mask)) >= total_ctrs || event_type >= SBI_PMU_EVENT_TYPE_MAX)
>                 return SBI_EINVAL;
>
>         if (flags & SBI_PMU_CFG_FLAG_SKIP_MATCH) {
> --
> 2.25.1
>


From anup at brainfault.org  Sat Jul 30 03:24:58 2022
From: anup at brainfault.org (Anup Patel)
Date: Sat, 30 Jul 2022 15:54:58 +0530
Subject: [PATCH v3 2/4] lib: sbi: Add the bound check for events during
 config match
In-Reply-To: <20220721054629.lurwim3jm7ggr26h@kamzik>
References: <20220720215035.3453553-1-atishp@rivosinc.com> <20220720215035.3453553-3-atishp@rivosinc.com>
 <20220721054629.lurwim3jm7ggr26h@kamzik>
Message-ID: <CAAhSdy1Ka_1Xa_UBOao=Kyqiz2_womMHzQdTGzp1VTC-DnAWyQ@mail.gmail.com>

On Thu, Jul 21, 2022 at 11:16 AM Andrew Jones <ajones at ventanamicro.com> wrote:
>
> On Wed, Jul 20, 2022 at 02:50:33PM -0700, Atish Patra wrote:
> > Currently, there is no sanity check for firmware event code. We don't see
> > any issue as Linux kernel driver does a bound check on firmware events
> > already. However, OpenSBI can't assume sane supervisor mode software
> > always. Thus, an invalid event idx can cause a buffer overflow error.
> > For hardware events, the match will fail for invalid event code anyways.
> > However, a search is unecessary if event code is invalid.
> >
> > Add a event ID validation function to solve the issue.
> >
> > Signed-off-by: Atish Patra <atishp at rivosinc.com>
> > ---
> >  lib/sbi/sbi_pmu.c | 46 ++++++++++++++++++++++++++++++++++++++++++++--
> >  1 file changed, 44 insertions(+), 2 deletions(-)
> >
> > diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
> > index 31631a2fab80..bd1ef8665a70 100644
> > --- a/lib/sbi/sbi_pmu.c
> > +++ b/lib/sbi/sbi_pmu.c
> > @@ -107,6 +107,44 @@ static bool pmu_event_select_overlap(struct sbi_pmu_hw_event *evt,
> >       return FALSE;
> >  }
> >
> > +static int pmu_event_validate(unsigned long event_idx)
> > +{
> > +     uint32_t event_idx_type = get_cidx_type(event_idx);
> > +     uint32_t event_idx_code = get_cidx_code(event_idx);
> > +     uint32_t event_idx_code_max = -1;
> > +     uint32_t cache_ops_result, cache_ops_id, cache_id;
> > +
> > +     switch(event_idx_type) {
> > +     case SBI_PMU_EVENT_TYPE_HW:
> > +             event_idx_code_max = SBI_PMU_HW_GENERAL_MAX;
> > +             break;
> > +     case SBI_PMU_EVENT_TYPE_FW:
> > +             event_idx_code_max = SBI_PMU_FW_MAX;
> > +             break;
> > +     case SBI_PMU_EVENT_TYPE_HW_CACHE:
> > +             cache_ops_result = event_idx_code & 0x1;
> > +             cache_ops_id = (event_idx_code & 0x06) >> 1;
> > +             cache_id = (event_idx_code & 0xfff8) >> 3;
>
> It'd be nice to introduce helper macros for these.

I have added appropriate defines in include/sbi/sbi_ecall_interface.h and
used it here at time of merging this patch.

>
> > +             if ((cache_ops_result < SBI_PMU_HW_CACHE_RESULT_MAX) &&
> > +                 (cache_ops_id < SBI_PMU_HW_CACHE_OP_MAX) &&
> > +                 (cache_id < SBI_PMU_HW_CACHE_MAX))
> > +                     return event_idx_type;
> > +             else
> > +                     return SBI_EINVAL;
> > +             break;
> > +     case SBI_PMU_EVENT_TYPE_HW_RAW:
> > +             event_idx_code_max = 1; // event_idx.code should be zero
> > +             break;
> > +     default:
> > +             return SBI_EINVAL;
> > +     }
> > +
> > +     if (event_idx_code < event_idx_code_max)
> > +             return event_idx_type;
> > +
> > +     return SBI_EINVAL;
> > +}
> > +
> >  static int pmu_ctr_validate(uint32_t cidx, uint32_t *event_idx_code)
> >  {
> >       uint32_t event_idx_val;
> > @@ -614,12 +652,16 @@ int sbi_pmu_ctr_cfg_match(unsigned long cidx_base, unsigned long cidx_mask,
> >  {
> >       int ctr_idx = SBI_ENOTSUPP;
> >       u32 hartid = current_hartid();
> > -     int event_type = get_cidx_type(event_idx);
> > +     int event_type;
> >       struct sbi_pmu_fw_event *fevent;
> >       uint32_t fw_evt_code;
> >
> >       /* Do a basic sanity check of counter base & mask */
> > -     if ((cidx_base + sbi_fls(cidx_mask)) >= total_ctrs || event_type >= SBI_PMU_EVENT_TYPE_MAX)
> > +     if ((cidx_base + sbi_fls(cidx_mask)) >= total_ctrs)
> > +             return SBI_EINVAL;
> > +
> > +     event_type = pmu_event_validate(event_idx);
> > +     if (event_type < 0)
> >               return SBI_EINVAL;
> >
> >       if (flags & SBI_PMU_CFG_FLAG_SKIP_MATCH) {
> > --
> > 2.25.1
> >
>
> Otherwise,
>
> Reviewed-by: Andrew Jones <ajones at ventanamicro.com>

Looks good to me.

Reviewed-by: Anup Patel <anup at brainfault.org>

Applied this patch to the riscv/opensbi repo.

Thanks,
Anup


From anup at brainfault.org  Sat Jul 30 03:25:26 2022
From: anup at brainfault.org (Anup Patel)
Date: Sat, 30 Jul 2022 15:55:26 +0530
Subject: [PATCH v3 3/4] lib: sbi: Fix possible buffer overrun in counter validation
In-Reply-To: <20220720215035.3453553-4-atishp@rivosinc.com>
References: <20220720215035.3453553-1-atishp@rivosinc.com> <20220720215035.3453553-4-atishp@rivosinc.com>
Message-ID: <CAAhSdy31KhX2bsMELPLf+xagkySUqZoLB6fX=Xj713ixL8ommg@mail.gmail.com>

On Thu, Jul 21, 2022 at 3:20 AM Atish Patra <atishp at rivosinc.com> wrote:
>
> The active_events array is accessed with counter ID passed from the supervisor
> software before the counter ID bound check. This may cause a buffer overrun
> if a supervisor passes an invalid counter ID.
>
> Fix this by moving the access part after the bound check.
>
> Reported-by: Andrew Jones <ajones at ventanamicro.com>
> Reviewed-by: Andrew Jones <ajones at ventanamicro.com>
> Signed-off-by: Atish Patra <atishp at rivosinc.com>

Looks good to me.

Reviewed-by: Anup Patel <anup at brainfault.org>

Applied this patch to the riscv/opensbi repo.

Thanks,
Anup

> ---
>  lib/sbi/sbi_pmu.c | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
> index bd1ef8665a70..ae3c00374058 100644
> --- a/lib/sbi/sbi_pmu.c
> +++ b/lib/sbi/sbi_pmu.c
> @@ -151,13 +151,13 @@ static int pmu_ctr_validate(uint32_t cidx, uint32_t *event_idx_code)
>         uint32_t event_idx_type;
>         u32 hartid = current_hartid();
>
> -       event_idx_val = active_events[hartid][cidx];
> -
> -       if (cidx >= total_ctrs || (event_idx_val == SBI_PMU_EVENT_IDX_INVALID))
> +       if (cidx >= total_ctrs)
>                 return SBI_EINVAL;
>
> +       event_idx_val = active_events[hartid][cidx];
>         event_idx_type = get_cidx_type(event_idx_val);
> -       if (event_idx_type >= SBI_PMU_EVENT_TYPE_MAX)
> +       if (event_idx_val == SBI_PMU_EVENT_IDX_INVALID ||
> +           event_idx_type >= SBI_PMU_EVENT_TYPE_MAX)
>                 return SBI_EINVAL;
>
>         *event_idx_code = get_cidx_code(event_idx_val);
> --
> 2.25.1
>


From anup at brainfault.org  Sat Jul 30 03:25:48 2022
From: anup at brainfault.org (Anup Patel)
Date: Sat, 30 Jul 2022 15:55:48 +0530
Subject: [PATCH v3 4/4] lib: sbi: Fix fw_event_map initialization
In-Reply-To: <20220720215035.3453553-5-atishp@rivosinc.com>
References: <20220720215035.3453553-1-atishp@rivosinc.com> <20220720215035.3453553-5-atishp@rivosinc.com>
Message-ID: <CAAhSdy1ZpRd3q6D50Gv1b5FwD_kqbGtpC4EsCV=BZxOMTnyaYA@mail.gmail.com>

On Thu, Jul 21, 2022 at 3:20 AM Atish Patra <atishp at rivosinc.com> wrote:
>
> fw_event_map represents array of firmware events. It should initialized
> for maximum number of firmware events not counters.
>
> Reviewed-by: Andrew Jones <ajones at ventanamicro.com>
> Signed-off-by: Atish Patra <atishp at rivosinc.com>

Looks good to me.

Reviewed-by: Anup Patel <anup at brainfault.org>

Applied this patch to the riscv/opensbi repo.

Thanks,
Anup

> ---
>  lib/sbi/sbi_pmu.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
> index ae3c00374058..b4342971bcf1 100644
> --- a/lib/sbi/sbi_pmu.c
> +++ b/lib/sbi/sbi_pmu.c
> @@ -764,7 +764,7 @@ static void pmu_reset_event_map(u32 hartid)
>         /* Initialize the counter to event mapping table */
>         for (j = 3; j < total_ctrs; j++)
>                 active_events[hartid][j] = SBI_PMU_EVENT_IDX_INVALID;
> -       for (j = 0; j < SBI_PMU_FW_CTR_MAX; j++)
> +       for (j = 0; j < SBI_PMU_FW_EVENT_MAX; j++)
>                 sbi_memset(&fw_event_map[hartid][j], 0,
>                            sizeof(struct sbi_pmu_fw_event));
>  }
> --
> 2.25.1
>


From anup at brainfault.org  Sat Jul 30 03:27:34 2022
From: anup at brainfault.org (Anup Patel)
Date: Sat, 30 Jul 2022 15:57:34 +0530
Subject: [PATCH v3] lib: sbi: Fix printf handling of long long
In-Reply-To: <20220727164401.tjinf26ysa27j43y@kamzik>
References: <TYYP286MB14396B0D3F810E64516CFE9DC6979@TYYP286MB1439.JPNP286.PROD.OUTLOOK.COM>
 <20220727164401.tjinf26ysa27j43y@kamzik>
Message-ID: <CAAhSdy1NMe0yrA4ELi8Vkz2mSWXYjrnx5aevEq7zF2mFo=pgdg@mail.gmail.com>

On Wed, Jul 27, 2022 at 10:14 PM Andrew Jones <ajones at ventanamicro.com> wrote:
>
> On Thu, Jul 28, 2022 at 12:30:05AM +0800, dramforever wrote:
> > Read long long arguments directly using va_arg. Remove original hack for
> > RV32 that read a long long arg as two long args.
> >
> > This un-breaks the case on RV64 where e.g. the long long is followed by
> > an odd number of ints:
> >
> >     sbi_printf("%d %lld", (int) 1, (long long) 2LL);
> >
> > Also remove the acnt variable, which is now unused.
> >
> > Signed-off-by: dramforever <dramforever at live.com>
> > ---
> >
> > Changes since v2:
> >
> > - Remove the acnt variable
> >
> > Changes since v1:
> >
> > - Removed hack that reads a long long as two longs
> > - Better commit message
> >
> >  lib/sbi/sbi_console.c | 30 ++----------------------------
> >  1 file changed, 2 insertions(+), 28 deletions(-)
> >
>
> Reviewed-by: Andrew Jones <ajones at ventanamicro.com>

Looks good to me.

Reviewed-by: Anup Patel <anup at brainfault.org>

Applied this patch to the riscv/opensbi repo.

Thanks,
Anup

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


From apatel at ventanamicro.com  Sat Jul 30 21:56:38 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Sun, 31 Jul 2022 10:26:38 +0530
Subject: [PATCH v4 00/15] OpenSBI Kconfig Support
Message-ID: <20220731045653.165058-1-apatel@ventanamicro.com>

In future, we will see more drivers in OpenSBI for system reset, serial port,
power management, etc platform specific devices. We need platform specific
compile time configuration to allow users control the size of OpenSBI generic
firmware binaries by only enabling desired drivers. In other words, users
should be allowed to restrict the set of RISC-V platforms on which OpenSBI
generic firmwares built by them can run.

To address above, we add Kconfig support for OpenSBI platforms using the
Kconfiglib project which is a standalone kconfig parser.

To try OpenSBI Kconfig support with generic platform, here are few
example commands:
make PLATFORM=generic             <-- builds OpenSBI using "defconfig"
make PLATFORM=generic menuconfig  <-- launches graphical interface for changes

These patches can also be found in the kconfig_v4 branch at:
https://github.com/avpatel/opensbi.git

Changes since v3:
 - Strip down the Kconfiglib sources added by PATCH1
 - Removed extra "." in the top-level Makefile updated by PATCH2

Changes since v2:
 - Use $(FOO) instead of $FOO to access FOO environment variable in
   Kconfig files.
 - Added a new PATCH1 to include Kconfiglib v14.1.0 sources under
   the scripts directory of OpenSBI.

Changes since v1:
 - Fix SPDX license specifier in new Kconfig files

Anup Patel (15):
  scripts: Add Kconfiglib v14.1.0 under scripts directory
  Makefile: Add initial kconfig support for each platform
  Makefile: Compile lib/utils sources separately for each platform
  lib: utils/serial: Use kconfig for enabling/disabling drivers
  lib: utils/reset: Use kconfig for enabling/disabling drivers
  lib: utils/sys: Use kconfig for enabling/disabling drivers
  lib: utils/timer: Use kconfig for enabling/disabling drivers
  lib: utils/ipi: Use kconfig for enabling/disabling drivers
  lib: utils/irqchip: Use kconfig for enabling/disabling drivers
  lib: utils/i2c: Use kconfig for enabling/disabling drivers
  lib: utils/gpio: Use kconfig for enabling/disabling drivers
  lib: utils/fdt: Use kconfig for enabling/disabling
  platform: generic: Use kconfig for enabling/disabling overrides
  platform: Remove redundant config.mk from all platforms
  docs: Update documentation for kconfig support

 Kconfig                                   |   23 +
 Makefile                                  |  104 +-
 README.md                                 |   25 +-
 docs/firmware/fw_dynamic.md               |    2 +-
 docs/firmware/fw_jump.md                  |    4 +-
 docs/firmware/fw_payload.md               |    4 +-
 docs/library_usage.md                     |    6 +-
 docs/platform/platform.md                 |    4 +-
 docs/platform_guide.md                    |    9 +-
 firmware/Kconfig                          |    1 +
 include/sbi_utils/fdt/fdt_domain.h        |    9 +
 include/sbi_utils/fdt/fdt_pmu.h           |   10 +
 include/sbi_utils/ipi/fdt_ipi.h           |    9 +
 include/sbi_utils/irqchip/fdt_irqchip.h   |   10 +
 include/sbi_utils/irqchip/imsic.h         |   10 +
 include/sbi_utils/reset/fdt_reset.h       |   12 +
 include/sbi_utils/serial/fdt_serial.h     |    8 +
 include/sbi_utils/timer/fdt_timer.h       |    9 +
 lib/utils/Kconfig                         |   25 +
 lib/utils/fdt/Kconfig                     |   18 +
 lib/utils/fdt/objects.mk                  |    8 +-
 lib/utils/gpio/Kconfig                    |   23 +
 lib/utils/gpio/objects.mk                 |   10 +-
 lib/utils/i2c/Kconfig                     |   23 +
 lib/utils/i2c/objects.mk                  |   10 +-
 lib/utils/ipi/Kconfig                     |   23 +
 lib/utils/ipi/objects.mk                  |   10 +-
 lib/utils/irqchip/Kconfig                 |   41 +
 lib/utils/irqchip/objects.mk              |   22 +-
 lib/utils/libfdt/Kconfig                  |    5 +
 lib/utils/libfdt/objects.mk               |    4 +-
 lib/utils/reset/Kconfig                   |   37 +
 lib/utils/reset/objects.mk                |   28 +-
 lib/utils/serial/Kconfig                  |   73 +
 lib/utils/serial/objects.mk               |   44 +-
 lib/utils/sys/Kconfig                     |   13 +
 lib/utils/sys/objects.mk                  |    4 +-
 lib/utils/timer/Kconfig                   |   23 +
 lib/utils/timer/objects.mk                |   10 +-
 platform/andes/ae350/Kconfig              |    8 +
 platform/andes/ae350/config.mk            |   36 -
 platform/andes/ae350/configs/defconfig    |    0
 platform/andes/ae350/objects.mk           |   29 +
 platform/fpga/ariane/Kconfig              |   10 +
 platform/fpga/ariane/config.mk            |   36 -
 platform/fpga/ariane/configs/defconfig    |    0
 platform/fpga/ariane/objects.mk           |   35 +
 platform/fpga/openpiton/Kconfig           |   10 +
 platform/fpga/openpiton/config.mk         |   35 -
 platform/fpga/openpiton/configs/defconfig |    0
 platform/fpga/openpiton/objects.mk        |   35 +
 platform/generic/Kconfig                  |   26 +
 platform/generic/allwinner/objects.mk     |    4 +-
 platform/generic/config.mk                |   40 -
 platform/generic/configs/defconfig        |   29 +
 platform/generic/objects.mk               |   33 +
 platform/generic/sifive/objects.mk        |    8 +-
 platform/kendryte/k210/Kconfig            |   10 +
 platform/kendryte/k210/config.mk          |   19 -
 platform/kendryte/k210/configs/defconfig  |    0
 platform/kendryte/k210/objects.mk         |   12 +
 platform/nuclei/ux600/Kconfig             |   10 +
 platform/nuclei/ux600/config.mk           |   30 -
 platform/nuclei/ux600/configs/defconfig   |    0
 platform/nuclei/ux600/objects.mk          |   22 +
 platform/nuclei/ux600/platform.c          |    1 -
 platform/template/Kconfig                 |    9 +
 platform/template/config.mk               |   77 -
 platform/template/configs/defconfig       |    0
 platform/template/objects.mk              |   72 +
 platform/template/platform.c              |    2 +-
 scripts/Kconfiglib/LICENSE.txt            |    5 +
 scripts/Kconfiglib/allnoconfig.py         |   45 +
 scripts/Kconfiglib/allyesconfig.py        |   56 +
 scripts/Kconfiglib/defconfig.py           |   43 +
 scripts/Kconfiglib/genconfig.py           |  154 +
 scripts/Kconfiglib/kconfiglib.py          | 7160 +++++++++++++++++++++
 scripts/Kconfiglib/menuconfig.py          | 3278 ++++++++++
 scripts/Kconfiglib/oldconfig.py           |  246 +
 scripts/Kconfiglib/olddefconfig.py        |   28 +
 scripts/Kconfiglib/savedefconfig.py       |   49 +
 scripts/Kconfiglib/setconfig.py           |   92 +
 scripts/create-binary-archive.sh          |    2 +-
 83 files changed, 12106 insertions(+), 403 deletions(-)
 create mode 100644 Kconfig
 create mode 100644 firmware/Kconfig
 create mode 100644 lib/utils/Kconfig
 create mode 100644 lib/utils/fdt/Kconfig
 create mode 100644 lib/utils/gpio/Kconfig
 create mode 100644 lib/utils/i2c/Kconfig
 create mode 100644 lib/utils/ipi/Kconfig
 create mode 100644 lib/utils/irqchip/Kconfig
 create mode 100644 lib/utils/libfdt/Kconfig
 create mode 100644 lib/utils/reset/Kconfig
 create mode 100644 lib/utils/serial/Kconfig
 create mode 100644 lib/utils/sys/Kconfig
 create mode 100644 lib/utils/timer/Kconfig
 create mode 100644 platform/andes/ae350/Kconfig
 delete mode 100644 platform/andes/ae350/config.mk
 create mode 100644 platform/andes/ae350/configs/defconfig
 create mode 100644 platform/fpga/ariane/Kconfig
 delete mode 100644 platform/fpga/ariane/config.mk
 create mode 100644 platform/fpga/ariane/configs/defconfig
 create mode 100644 platform/fpga/openpiton/Kconfig
 delete mode 100644 platform/fpga/openpiton/config.mk
 create mode 100644 platform/fpga/openpiton/configs/defconfig
 create mode 100644 platform/generic/Kconfig
 delete mode 100644 platform/generic/config.mk
 create mode 100644 platform/generic/configs/defconfig
 create mode 100644 platform/kendryte/k210/Kconfig
 delete mode 100644 platform/kendryte/k210/config.mk
 create mode 100644 platform/kendryte/k210/configs/defconfig
 create mode 100644 platform/nuclei/ux600/Kconfig
 delete mode 100644 platform/nuclei/ux600/config.mk
 create mode 100644 platform/nuclei/ux600/configs/defconfig
 create mode 100644 platform/template/Kconfig
 delete mode 100644 platform/template/config.mk
 create mode 100644 platform/template/configs/defconfig
 create mode 100644 scripts/Kconfiglib/LICENSE.txt
 create mode 100755 scripts/Kconfiglib/allnoconfig.py
 create mode 100755 scripts/Kconfiglib/allyesconfig.py
 create mode 100755 scripts/Kconfiglib/defconfig.py
 create mode 100755 scripts/Kconfiglib/genconfig.py
 create mode 100644 scripts/Kconfiglib/kconfiglib.py
 create mode 100755 scripts/Kconfiglib/menuconfig.py
 create mode 100755 scripts/Kconfiglib/oldconfig.py
 create mode 100755 scripts/Kconfiglib/olddefconfig.py
 create mode 100755 scripts/Kconfiglib/savedefconfig.py
 create mode 100755 scripts/Kconfiglib/setconfig.py

-- 
2.34.1



From apatel at ventanamicro.com  Sat Jul 30 21:56:40 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Sun, 31 Jul 2022 10:26:40 +0530
Subject: [PATCH v4 02/15] Makefile: Add initial kconfig support for each platform
In-Reply-To: <20220731045653.165058-1-apatel@ventanamicro.com>
References: <20220731045653.165058-1-apatel@ventanamicro.com>
Message-ID: <20220731045653.165058-3-apatel@ventanamicro.com>

We extend the top-level makefile to allow kconfig based configuration
for each platform where each platform has it's own set of configs with
"defconfig" being the default config.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 Kconfig                                   | 23 ++++++++++
 Makefile                                  | 56 +++++++++++++++++++++--
 firmware/Kconfig                          |  1 +
 lib/utils/Kconfig                         |  1 +
 platform/andes/ae350/Kconfig              |  5 ++
 platform/andes/ae350/configs/defconfig    |  0
 platform/fpga/ariane/Kconfig              |  5 ++
 platform/fpga/ariane/configs/defconfig    |  0
 platform/fpga/openpiton/Kconfig           |  5 ++
 platform/fpga/openpiton/configs/defconfig |  0
 platform/generic/Kconfig                  |  5 ++
 platform/generic/configs/defconfig        |  0
 platform/kendryte/k210/Kconfig            |  5 ++
 platform/kendryte/k210/configs/defconfig  |  0
 platform/nuclei/ux600/Kconfig             |  5 ++
 platform/nuclei/ux600/configs/defconfig   |  0
 platform/template/Kconfig                 |  5 ++
 platform/template/configs/defconfig       |  0
 18 files changed, 113 insertions(+), 3 deletions(-)
 create mode 100644 Kconfig
 create mode 100644 firmware/Kconfig
 create mode 100644 lib/utils/Kconfig
 create mode 100644 platform/andes/ae350/Kconfig
 create mode 100644 platform/andes/ae350/configs/defconfig
 create mode 100644 platform/fpga/ariane/Kconfig
 create mode 100644 platform/fpga/ariane/configs/defconfig
 create mode 100644 platform/fpga/openpiton/Kconfig
 create mode 100644 platform/fpga/openpiton/configs/defconfig
 create mode 100644 platform/generic/Kconfig
 create mode 100644 platform/generic/configs/defconfig
 create mode 100644 platform/kendryte/k210/Kconfig
 create mode 100644 platform/kendryte/k210/configs/defconfig
 create mode 100644 platform/nuclei/ux600/Kconfig
 create mode 100644 platform/nuclei/ux600/configs/defconfig
 create mode 100644 platform/template/Kconfig
 create mode 100644 platform/template/configs/defconfig

diff --git a/Kconfig b/Kconfig
new file mode 100644
index 0000000..b3213a9
--- /dev/null
+++ b/Kconfig
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+mainmenu "OpenSBI $(OPENSBI_PLATFORM) Configuration"
+
+config OPENSBI_SRC_DIR
+	string
+	option env="OPENSBI_SRC_DIR"
+
+config OPENSBI_PLATFORM
+	string
+	option env="OPENSBI_PLATFORM"
+
+config OPENSBI_PLATFORM_SRC_DIR
+	string
+	option env="OPENSBI_PLATFORM_SRC_DIR"
+
+menu "Platform Options"
+source "$(OPENSBI_PLATFORM_SRC_DIR)/Kconfig"
+endmenu
+
+source "$(OPENSBI_SRC_DIR)/lib/utils/Kconfig"
+
+source "$(OPENSBI_SRC_DIR)/firmware/Kconfig"
diff --git a/Makefile b/Makefile
index f619ef7..b10cc19 100644
--- a/Makefile
+++ b/Makefile
@@ -52,6 +52,9 @@ ifdef PLATFORM_DIR
 else
  platform_parent_dir=$(src_dir)/platform
 endif
+ifndef PLATFORM_DEFCONFIG
+PLATFORM_DEFCONFIG=defconfig
+endif
 
 # Check if verbosity is ON for build process
 CMD_PREFIX_DEFAULT := @
@@ -70,6 +73,20 @@ export libsbi_dir=$(CURDIR)/lib/sbi
 export libsbiutils_dir=$(CURDIR)/lib/utils
 export firmware_dir=$(CURDIR)/firmware
 
+# Setup variables for kconfig
+ifdef PLATFORM
+export PYTHONDONTWRITEBYTECODE=1
+export KCONFIG_DIR=$(platform_build_dir)/kconfig
+export KCONFIG_AUTOLIST=$(KCONFIG_DIR)/auto.list
+export KCONFIG_AUTOHEADER=$(KCONFIG_DIR)/autoconf.h
+export KCONFIG_AUTOCMD=$(KCONFIG_DIR)/auto.conf.cmd
+export KCONFIG_CONFIG=$(KCONFIG_DIR)/.config
+# Additional exports for include paths in Kconfig files
+export OPENSBI_SRC_DIR=$(src_dir)
+export OPENSBI_PLATFORM=$(PLATFORM)
+export OPENSBI_PLATFORM_SRC_DIR=$(platform_src_dir)
+endif
+
 # Find library version
 OPENSBI_VERSION_MAJOR=`grep "define OPENSBI_VERSION_MAJOR" $(include_dir)/sbi/sbi_version.h | sed 's/.*MAJOR.*\([0-9][0-9]*\)/\1/'`
 OPENSBI_VERSION_MINOR=`grep "define OPENSBI_VERSION_MINOR" $(include_dir)/sbi/sbi_version.h | sed 's/.*MINOR.*\([0-9][0-9]*\)/\1/'`
@@ -183,8 +200,35 @@ libsbi-object-mks=$(shell if [ -d $(libsbi_dir) ]; then find $(libsbi_dir) -inam
 libsbiutils-object-mks=$(shell if [ -d $(libsbiutils_dir) ]; then find $(libsbiutils_dir) -iname "objects.mk" | sort -r; fi)
 firmware-object-mks=$(shell if [ -d $(firmware_dir) ]; then find $(firmware_dir) -iname "objects.mk" | sort -r; fi)
 
-# Include platform specifig config.mk
+# The "make all" rule should always be first rule
+.PHONY: all
+all:
+
+# Include platform specific .config and config.mk
 ifdef PLATFORM
+.PHONY: menuconfig
+menuconfig: $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
+	$(CMD_PREFIX)mkdir -p $(KCONFIG_DIR)
+	$(CMD_PREFIX)$(src_dir)/scripts/Kconfiglib/menuconfig.py $(src_dir)/Kconfig
+
+.PHONY: savedefconfig
+savedefconfig: $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
+	$(CMD_PREFIX)mkdir -p $(KCONFIG_DIR)
+	$(CMD_PREFIX)$(src_dir)/scripts/Kconfiglib/savedefconfig.py --kconfig $(src_dir)/Kconfig --out $(KCONFIG_DIR)/defconfig
+
+$(KCONFIG_CONFIG): $(platform_src_dir)/configs/$(PLATFORM_DEFCONFIG) $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
+	$(CMD_PREFIX)mkdir -p $(KCONFIG_DIR)
+	$(CMD_PREFIX)$(src_dir)/scripts/Kconfiglib/defconfig.py --kconfig $(src_dir)/Kconfig $(platform_src_dir)/configs/$(PLATFORM_DEFCONFIG)
+	$(CMD_PREFIX)$(src_dir)/scripts/Kconfiglib/genconfig.py --header-path $(KCONFIG_AUTOHEADER) --sync-deps $(KCONFIG_DIR) --file-list $(KCONFIG_AUTOLIST) $(src_dir)/Kconfig
+
+$(KCONFIG_AUTOCMD): $(KCONFIG_CONFIG)
+	$(CMD_PREFIX)mkdir -p $(KCONFIG_DIR)
+	$(CMD_PREFIX)echo -n "$(KCONFIG_CONFIG): " > $(KCONFIG_AUTOCMD)
+	$(CMD_PREFIX)cat $(KCONFIG_AUTOLIST) | tr '\n' ' ' >> $(KCONFIG_AUTOCMD)
+
+include $(KCONFIG_CONFIG)
+include $(KCONFIG_AUTOCMD)
+
 include $(platform_src_dir)/config.mk
 endif
 
@@ -280,6 +324,9 @@ ifeq ($(BUILD_INFO),y)
 GENFLAGS	+=	-DOPENSBI_BUILD_TIME_STAMP="\"$(OPENSBI_BUILD_TIME_STAMP)\""
 GENFLAGS	+=	-DOPENSBI_BUILD_COMPILER_VERSION="\"$(OPENSBI_BUILD_COMPILER_VERSION)\""
 endif
+ifdef PLATFORM
+GENFLAGS	+=	-include $(KCONFIG_AUTOHEADER)
+endif
 GENFLAGS	+=	$(libsbiutils-genflags-y)
 GENFLAGS	+=	$(platform-genflags-y)
 GENFLAGS	+=	$(firmware-genflags-y)
@@ -419,7 +466,7 @@ targets-y += $(platform_build_dir)/lib/libplatsbi.a
 endif
 targets-y += $(firmware-bins-path-y)
 
-# Default rule "make" should always be first rule
+# The default "make all" rule
 .PHONY: all
 all: $(targets-y)
 
@@ -456,7 +503,7 @@ $(build_dir)/%.o: $(src_dir)/%.S
 	$(call compile_as,$@,$<)
 
 $(build_dir)/%.dep: $(src_dir)/%.carray
-	$(call compile_gen_dep,$@,.c,$<)
+	$(call compile_gen_dep,$@,.c,$< $(KCONFIG_CONFIG))
 	$(call compile_gen_dep,$@,.o,$(@:.dep=.c))
 
 $(build_dir)/%.c: $(src_dir)/%.carray
@@ -523,6 +570,9 @@ docs: $(build_dir)/docs/latex/refman.pdf
 # They should not be included for any "xxxconfig" or "xxxclean" rule
 all-deps-1 = $(if $(findstring config,$(MAKECMDGOALS)),,$(deps-y))
 all-deps-2 = $(if $(findstring clean,$(MAKECMDGOALS)),,$(all-deps-1))
+ifdef PLATFORM
+$(all-deps-2): | $(KCONFIG_CONFIG)
+endif
 -include $(all-deps-2)
 
 # Include external dependency of firmwares after default Makefile rules
diff --git a/firmware/Kconfig b/firmware/Kconfig
new file mode 100644
index 0000000..d6e0506
--- /dev/null
+++ b/firmware/Kconfig
@@ -0,0 +1 @@
+# SPDX-License-Identifier: BSD-2-Clause
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
new file mode 100644
index 0000000..d6e0506
--- /dev/null
+++ b/lib/utils/Kconfig
@@ -0,0 +1 @@
+# SPDX-License-Identifier: BSD-2-Clause
diff --git a/platform/andes/ae350/Kconfig b/platform/andes/ae350/Kconfig
new file mode 100644
index 0000000..4b73c76
--- /dev/null
+++ b/platform/andes/ae350/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+config PLATFORM_ANDES_AE350
+	bool
+	default y
diff --git a/platform/andes/ae350/configs/defconfig b/platform/andes/ae350/configs/defconfig
new file mode 100644
index 0000000..e69de29
diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
new file mode 100644
index 0000000..70d2004
--- /dev/null
+++ b/platform/fpga/ariane/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+config PLATFORM_ARIANE_FPGA
+	bool
+	default y
diff --git a/platform/fpga/ariane/configs/defconfig b/platform/fpga/ariane/configs/defconfig
new file mode 100644
index 0000000..e69de29
diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
new file mode 100644
index 0000000..81e4b76
--- /dev/null
+++ b/platform/fpga/openpiton/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+config PLATFORM_OPENPITON_FPGA
+	bool
+	default y
diff --git a/platform/fpga/openpiton/configs/defconfig b/platform/fpga/openpiton/configs/defconfig
new file mode 100644
index 0000000..e69de29
diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
new file mode 100644
index 0000000..3eab282
--- /dev/null
+++ b/platform/generic/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+config PLATFORM_GENERIC
+	bool
+	default y
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
new file mode 100644
index 0000000..e69de29
diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
new file mode 100644
index 0000000..889f16b
--- /dev/null
+++ b/platform/kendryte/k210/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+config PLATFORM_KENDRYTE_K210
+	bool
+	default y
diff --git a/platform/kendryte/k210/configs/defconfig b/platform/kendryte/k210/configs/defconfig
new file mode 100644
index 0000000..e69de29
diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
new file mode 100644
index 0000000..c368ef4
--- /dev/null
+++ b/platform/nuclei/ux600/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+config PLATFORM_NUCLEI_UX600
+	bool
+	default y
diff --git a/platform/nuclei/ux600/configs/defconfig b/platform/nuclei/ux600/configs/defconfig
new file mode 100644
index 0000000..e69de29
diff --git a/platform/template/Kconfig b/platform/template/Kconfig
new file mode 100644
index 0000000..b9c4350
--- /dev/null
+++ b/platform/template/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+config PLATFORM_TEMPLATE
+	bool
+	default y
diff --git a/platform/template/configs/defconfig b/platform/template/configs/defconfig
new file mode 100644
index 0000000..e69de29
-- 
2.34.1



From apatel at ventanamicro.com  Sat Jul 30 21:56:41 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Sun, 31 Jul 2022 10:26:41 +0530
Subject: [PATCH v4 03/15] Makefile: Compile lib/utils sources separately for each platform
In-Reply-To: <20220731045653.165058-1-apatel@ventanamicro.com>
References: <20220731045653.165058-1-apatel@ventanamicro.com>
Message-ID: <20220731045653.165058-4-apatel@ventanamicro.com>

Currently, if same build directory is used to compile two different
platforms then lib/utils objects are shared for these platforms.

We will be having platform specific configs to enable/disable drivers
in lib/utils and select compile time options for lib/utils sources.
This means lib/utils sources will now be compiled in a platform
specific way.

To tackle above, we update top-level Makefile as follows:
1) Don't create libsbiutils.a anymore because this can't be shared
   between platforms.
2) Compile lib/utils sources separately for each platform.
3) Add comments showing which make rules are for lib/sbi, lib/utils,
   firmware, and platform sources.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 Makefile                         | 48 ++++++++++++++++----------------
 docs/library_usage.md            |  6 +---
 scripts/create-binary-archive.sh |  2 +-
 3 files changed, 26 insertions(+), 30 deletions(-)

diff --git a/Makefile b/Makefile
index b10cc19..820a137 100644
--- a/Makefile
+++ b/Makefile
@@ -242,8 +242,8 @@ include $(firmware-object-mks)
 
 # Setup list of objects
 libsbi-objs-path-y=$(foreach obj,$(libsbi-objs-y),$(build_dir)/lib/sbi/$(obj))
-libsbiutils-objs-path-y=$(foreach obj,$(libsbiutils-objs-y),$(build_dir)/lib/utils/$(obj))
 ifdef PLATFORM
+libsbiutils-objs-path-y=$(foreach obj,$(libsbiutils-objs-y),$(platform_build_dir)/lib/utils/$(obj))
 platform-objs-path-y=$(foreach obj,$(platform-objs-y),$(platform_build_dir)/$(obj))
 firmware-bins-path-y=$(foreach bin,$(firmware-bins-y),$(platform_build_dir)/firmware/$(bin))
 endif
@@ -460,7 +460,6 @@ compile_gen_dep = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
 	     echo "$(1:.dep=$(2)): $(3)" >> $(1)
 
 targets-y  = $(build_dir)/lib/libsbi.a
-targets-y  += $(build_dir)/lib/libsbiutils.a
 ifdef PLATFORM
 targets-y += $(platform_build_dir)/lib/libplatsbi.a
 endif
@@ -473,12 +472,10 @@ all: $(targets-y)
 # Preserve all intermediate files
 .SECONDARY:
 
+# Rules for lib/sbi sources
 $(build_dir)/lib/libsbi.a: $(libsbi-objs-path-y)
 	$(call compile_ar,$@,$^)
 
-$(build_dir)/lib/libsbiutils.a: $(libsbi-objs-path-y) $(libsbiutils-objs-path-y)
-	$(call compile_ar,$@,$^)
-
 $(platform_build_dir)/lib/libplatsbi.a: $(libsbi-objs-path-y) $(libsbiutils-objs-path-y) $(platform-objs-path-y)
 	$(call compile_ar,$@,$^)
 
@@ -502,22 +499,14 @@ $(build_dir)/%.dep: $(src_dir)/%.S
 $(build_dir)/%.o: $(src_dir)/%.S
 	$(call compile_as,$@,$<)
 
-$(build_dir)/%.dep: $(src_dir)/%.carray
+# Rules for platform sources
+$(platform_build_dir)/%.dep: $(platform_src_dir)/%.carray
 	$(call compile_gen_dep,$@,.c,$< $(KCONFIG_CONFIG))
 	$(call compile_gen_dep,$@,.o,$(@:.dep=.c))
 
-$(build_dir)/%.c: $(src_dir)/%.carray
+$(platform_build_dir)/%.c: $(platform_src_dir)/%.carray
 	$(call compile_carray,$@,$<)
 
-$(platform_build_dir)/%.bin: $(platform_build_dir)/%.elf
-	$(call compile_objcopy,$@,$<)
-
-$(platform_build_dir)/%.elf: $(platform_build_dir)/%.o $(platform_build_dir)/%.elf.ld $(platform_build_dir)/lib/libplatsbi.a
-	$(call compile_elf,$@,$@.ld,$< $(platform_build_dir)/lib/libplatsbi.a)
-
-$(platform_build_dir)/%.ld: $(src_dir)/%.ldS
-	$(call compile_cpp,$@,$<)
-
 $(platform_build_dir)/%.dep: $(platform_src_dir)/%.c
 	$(call compile_cc_dep,$@,$<)
 
@@ -541,6 +530,23 @@ $(platform_build_dir)/%.c: $(platform_build_dir)/%.dtb
 $(platform_build_dir)/%.dtb: $(platform_src_dir)/%.dts
 	$(call compile_dts,$@,$<)
 
+# Rules for lib/utils and firmware sources
+$(platform_build_dir)/%.bin: $(platform_build_dir)/%.elf
+	$(call compile_objcopy,$@,$<)
+
+$(platform_build_dir)/%.elf: $(platform_build_dir)/%.o $(platform_build_dir)/%.elf.ld $(platform_build_dir)/lib/libplatsbi.a
+	$(call compile_elf,$@,$@.ld,$< $(platform_build_dir)/lib/libplatsbi.a)
+
+$(platform_build_dir)/%.ld: $(src_dir)/%.ldS
+	$(call compile_cpp,$@,$<)
+
+$(platform_build_dir)/%.dep: $(src_dir)/%.carray
+	$(call compile_gen_dep,$@,.c,$< $(KCONFIG_CONFIG))
+	$(call compile_gen_dep,$@,.o,$(@:.dep=.c))
+
+$(platform_build_dir)/%.c: $(src_dir)/%.carray
+	$(call compile_carray,$@,$<)
+
 $(platform_build_dir)/%.dep: $(src_dir)/%.c
 	$(call compile_cc_dep,$@,$<)
 
@@ -594,7 +600,6 @@ endif
 endif
 
 install_targets-y  = install_libsbi
-install_targets-y  += install_libsbiutils
 ifdef PLATFORM
 install_targets-y += install_libplatsbi
 install_targets-y += install_firmwares
@@ -609,17 +614,12 @@ install_libsbi: $(build_dir)/lib/libsbi.a
 	$(call inst_header_dir,$(install_root_dir)/$(install_include_path),$(include_dir)/sbi)
 	$(call inst_file,$(install_root_dir)/$(install_lib_path)/libsbi.a,$(build_dir)/lib/libsbi.a)
 
-.PHONY: install_libsbiutils
-install_libsbiutils: $(build_dir)/lib/libsbiutils.a
-	$(call inst_header_dir,$(install_root_dir)/$(install_include_path),$(include_dir)/sbi_utils)
-	$(call inst_file,$(install_root_dir)/$(install_lib_path)/libsbiutils.a,$(build_dir)/lib/libsbiutils.a)
-
 .PHONY: install_libplatsbi
-install_libplatsbi: $(platform_build_dir)/lib/libplatsbi.a $(build_dir)/lib/libsbi.a $(build_dir)/lib/libsbiutils.a
+install_libplatsbi: $(platform_build_dir)/lib/libplatsbi.a $(build_dir)/lib/libsbi.a
 	$(call inst_file,$(install_root_dir)/$(install_lib_path)/opensbi/$(platform_subdir)/lib/libplatsbi.a,$(platform_build_dir)/lib/libplatsbi.a)
 
 .PHONY: install_firmwares
-install_firmwares: $(platform_build_dir)/lib/libplatsbi.a $(build_dir)/lib/libsbi.a $(build_dir)/lib/libsbiutils.a $(firmware-bins-path-y)
+install_firmwares: $(platform_build_dir)/lib/libplatsbi.a $(build_dir)/lib/libsbi.a $(firmware-bins-path-y)
 	$(call inst_file_list,$(install_root_dir),$(build_dir),$(install_firmware_path)/$(platform_subdir)/firmware,$(firmware-elfs-path-y))
 	$(call inst_file_list,$(install_root_dir),$(build_dir),$(install_firmware_path)/$(platform_subdir)/firmware,$(firmware-bins-path-y))
 
diff --git a/docs/library_usage.md b/docs/library_usage.md
index ff99801..d5d2ba9 100644
--- a/docs/library_usage.md
+++ b/docs/library_usage.md
@@ -8,11 +8,7 @@ OpenSBI provides two types of static libraries:
    hooks for the execution of this interface must be provided by the firmware or
    bootloader linking with this library. This library is installed as
    *<install_directory>/lib/libsbi.a*
-2. *libsbiutils.a* - A static library that will contain all common code required
-   by any platform supported in OpenSBI. It will be built by default and included
-   in libplatsbi.a. This library is installed as
-   *<install_directory>/lib/libsbiutils.a*.
-3. *libplatsbi.a* - An example platform-specific static library integrating
+2. *libplatsbi.a* - An example platform-specific static library integrating
    *libsbi.a* with platform-specific hooks. This library is available only for
    the platforms supported by OpenSBI. This library is installed as
    *<install_directory>/platform/<platform_subdir>/lib/libplatsbi.a*
diff --git a/scripts/create-binary-archive.sh b/scripts/create-binary-archive.sh
index 43c5452..261a45a 100755
--- a/scripts/create-binary-archive.sh
+++ b/scripts/create-binary-archive.sh
@@ -118,7 +118,7 @@ build_opensbi() {
 	# Build and install generic library
 	echo "Build and install generic library XLEN=${BUILD_RISCV_XLEN}"
 	echo ""
-	make -C "${BUILD_OPENSBI_SOURCE_PATH}" O="${BUILD_OUTPUT_PATH}/${BUILD_NAME}" I="${BUILD_OUTPUT_PATH}/${BUILD_ARCHIVE_NAME}" PLATFORM_RISCV_XLEN="${BUILD_RISCV_XLEN}" install_libsbi install_libsbiutils -j "${BUILD_NUM_THREADS}"
+	make -C "${BUILD_OPENSBI_SOURCE_PATH}" O="${BUILD_OUTPUT_PATH}/${BUILD_NAME}" I="${BUILD_OUTPUT_PATH}/${BUILD_ARCHIVE_NAME}" PLATFORM_RISCV_XLEN="${BUILD_RISCV_XLEN}" install_libsbi -j "${BUILD_NUM_THREADS}"
 	echo ""
 
 	# Build and install relevant platforms
-- 
2.34.1



From apatel at ventanamicro.com  Sat Jul 30 21:56:42 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Sun, 31 Jul 2022 10:26:42 +0530
Subject: [PATCH v4 04/15] lib: utils/serial: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220731045653.165058-1-apatel@ventanamicro.com>
References: <20220731045653.165058-1-apatel@ventanamicro.com>
Message-ID: <20220731045653.165058-5-apatel@ventanamicro.com>

We update serial drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate serial
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 include/sbi_utils/serial/fdt_serial.h |  8 +++
 lib/utils/Kconfig                     |  6 +++
 lib/utils/serial/Kconfig              | 71 +++++++++++++++++++++++++++
 lib/utils/serial/objects.mk           | 44 ++++++++---------
 platform/andes/ae350/Kconfig          |  1 +
 platform/fpga/ariane/Kconfig          |  1 +
 platform/fpga/openpiton/Kconfig       |  1 +
 platform/generic/configs/defconfig    |  8 +++
 platform/kendryte/k210/Kconfig        |  1 +
 platform/nuclei/ux600/Kconfig         |  1 +
 platform/template/Kconfig             |  1 +
 11 files changed, 121 insertions(+), 22 deletions(-)
 create mode 100644 lib/utils/serial/Kconfig

diff --git a/include/sbi_utils/serial/fdt_serial.h b/include/sbi_utils/serial/fdt_serial.h
index 6451c23..daa2e4f 100644
--- a/include/sbi_utils/serial/fdt_serial.h
+++ b/include/sbi_utils/serial/fdt_serial.h
@@ -12,6 +12,8 @@
 
 #include <sbi/sbi_types.h>
 
+#ifdef CONFIG_FDT_SERIAL
+
 struct fdt_serial {
 	const struct fdt_match *match_table;
 	int (*init)(void *fdt, int nodeoff, const struct fdt_match *match);
@@ -19,4 +21,10 @@ struct fdt_serial {
 
 int fdt_serial_init(void);
 
+#else
+
+static inline int fdt_serial_init(void) { return 0; }
+
+#endif
+
 #endif
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index d6e0506..4524a3f 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -1 +1,7 @@
 # SPDX-License-Identifier: BSD-2-Clause
+
+menu "Utils and Drivers Support"
+
+source "$(OPENSBI_SRC_DIR)/lib/utils/serial/Kconfig"
+
+endmenu
diff --git a/lib/utils/serial/Kconfig b/lib/utils/serial/Kconfig
new file mode 100644
index 0000000..e114f26
--- /dev/null
+++ b/lib/utils/serial/Kconfig
@@ -0,0 +1,71 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "Serial Device Support"
+
+config FDT_SERIAL
+	bool "FDT based serial drivers"
+	default n
+
+if FDT_SERIAL
+
+config FDT_SERIAL_GAISLER
+	bool "Gaisler UART FDT driver"
+	select SERIAL_GAISLER
+	default n
+
+config FDT_SERIAL_HTIF
+	bool "Host transfer interface (HTIF) UART FDT driver"
+	default n
+
+config FDT_SERIAL_SHAKTI
+	bool "Shakti UART FDT driver"
+	select SERIAL_SHAKTI
+	default n
+
+config FDT_SERIAL_SIFIVE
+	bool "SiFive UART FDT driver"
+	select SERIAL_SIFIVE
+	default n
+
+config FDT_SERIAL_LITEX
+	bool "LiteX UART FDT driver"
+	select SERIAL_LITEX
+	default n
+
+config FDT_SERIAL_UART8250
+	bool "8250 UART FDT driver"
+	select SERIAL_UART8250
+	default n
+
+config FDT_SERIAL_XILINX_UARTLITE
+	bool "Xilinx UART Lite FDT driver"
+	select SERIAL_XILINX_UARTLITE
+	default n
+
+endif
+
+config SERIAL_GAISLER
+	bool "Gaisler UART support"
+	default n
+
+config SERIAL_SHAKTI
+	bool "Shakti UART support"
+	default n
+
+config SERIAL_SIFIVE
+	bool "SiFive UART support"
+	default n
+
+config SERIAL_LITEX
+	bool "LiteX UART support"
+	default n
+
+config SERIAL_UART8250
+	bool "8250 UART support"
+	default n
+
+config SERIAL_XILINX_UARTLITE
+	bool "Xilinx UART Lite support"
+	default n
+
+endmenu
diff --git a/lib/utils/serial/objects.mk b/lib/utils/serial/objects.mk
index d26a74e..fa9f5a3 100644
--- a/lib/utils/serial/objects.mk
+++ b/lib/utils/serial/objects.mk
@@ -7,33 +7,33 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += serial/fdt_serial.o
-libsbiutils-objs-y += serial/fdt_serial_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_SERIAL) += serial/fdt_serial.o
+libsbiutils-objs-$(CONFIG_FDT_SERIAL) += serial/fdt_serial_drivers.o
 
-carray-fdt_serial_drivers-y += fdt_serial_gaisler
-libsbiutils-objs-y += serial/fdt_serial_gaisler.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_GAISLER) += fdt_serial_gaisler
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_GAISLER) += serial/fdt_serial_gaisler.o
 
-carray-fdt_serial_drivers-y += fdt_serial_htif
-libsbiutils-objs-y += serial/fdt_serial_htif.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_HTIF) += fdt_serial_htif
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_HTIF) += serial/fdt_serial_htif.o
 
-carray-fdt_serial_drivers-y += fdt_serial_shakti
-libsbiutils-objs-y += serial/fdt_serial_shakti.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_SHAKTI) += fdt_serial_shakti
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_SHAKTI) += serial/fdt_serial_shakti.o
 
-carray-fdt_serial_drivers-y += fdt_serial_sifive
-libsbiutils-objs-y += serial/fdt_serial_sifive.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_SIFIVE) += fdt_serial_sifive
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_SIFIVE) += serial/fdt_serial_sifive.o
 
-carray-fdt_serial_drivers-y += fdt_serial_litex
-libsbiutils-objs-y += serial/fdt_serial_litex.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_LITEX) += fdt_serial_litex
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_LITEX) += serial/fdt_serial_litex.o
 
-carray-fdt_serial_drivers-y += fdt_serial_uart8250
-libsbiutils-objs-y += serial/fdt_serial_uart8250.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_UART8250) += fdt_serial_uart8250
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_UART8250) += serial/fdt_serial_uart8250.o
 
-carray-fdt_serial_drivers-y += fdt_serial_xlnx_uartlite
-libsbiutils-objs-y += serial/fdt_serial_xlnx_uartlite.o
+carray-fdt_serial_drivers-$(CONFIG_FDT_SERIAL_XILINX_UARTLITE) += fdt_serial_xlnx_uartlite
+libsbiutils-objs-$(CONFIG_FDT_SERIAL_XILINX_UARTLITE) += serial/fdt_serial_xlnx_uartlite.o
 
-libsbiutils-objs-y += serial/gaisler-uart.o
-libsbiutils-objs-y += serial/shakti-uart.o
-libsbiutils-objs-y += serial/sifive-uart.o
-libsbiutils-objs-y += serial/litex-uart.o
-libsbiutils-objs-y += serial/uart8250.o
-libsbiutils-objs-y += serial/xlnx-uartlite.o
+libsbiutils-objs-$(CONFIG_SERIAL_GAISLER) += serial/gaisler-uart.o
+libsbiutils-objs-$(CONFIG_SERIAL_SHAKTI) += serial/shakti-uart.o
+libsbiutils-objs-$(CONFIG_SERIAL_SIFIVE) += serial/sifive-uart.o
+libsbiutils-objs-$(CONFIG_SERIAL_LITEX) += serial/litex-uart.o
+libsbiutils-objs-$(CONFIG_SERIAL_UART8250) += serial/uart8250.o
+libsbiutils-objs-$(CONFIG_SERIAL_XILINX_UARTLITE) += serial/xlnx-uartlite.o
diff --git a/platform/andes/ae350/Kconfig b/platform/andes/ae350/Kconfig
index 4b73c76..162d19c 100644
--- a/platform/andes/ae350/Kconfig
+++ b/platform/andes/ae350/Kconfig
@@ -2,4 +2,5 @@
 
 config PLATFORM_ANDES_AE350
 	bool
+	select SERIAL_UART8250
 	default y
diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
index 70d2004..3ce0ec1 100644
--- a/platform/fpga/ariane/Kconfig
+++ b/platform/fpga/ariane/Kconfig
@@ -2,4 +2,5 @@
 
 config PLATFORM_ARIANE_FPGA
 	bool
+	select SERIAL_UART8250
 	default y
diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
index 81e4b76..4dd752d 100644
--- a/platform/fpga/openpiton/Kconfig
+++ b/platform/fpga/openpiton/Kconfig
@@ -2,4 +2,5 @@
 
 config PLATFORM_OPENPITON_FPGA
 	bool
+	select SERIAL_UART8250
 	default y
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index e69de29..ce70c59 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -0,0 +1,8 @@
+CONFIG_FDT_SERIAL=y
+CONFIG_FDT_SERIAL_GAISLER=y
+CONFIG_FDT_SERIAL_HTIF=y
+CONFIG_FDT_SERIAL_SHAKTI=y
+CONFIG_FDT_SERIAL_SIFIVE=y
+CONFIG_FDT_SERIAL_LITEX=y
+CONFIG_FDT_SERIAL_UART8250=y
+CONFIG_FDT_SERIAL_XILINX_UARTLITE=y
diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
index 889f16b..adc7613 100644
--- a/platform/kendryte/k210/Kconfig
+++ b/platform/kendryte/k210/Kconfig
@@ -2,4 +2,5 @@
 
 config PLATFORM_KENDRYTE_K210
 	bool
+	select SERIAL_SIFIVE
 	default y
diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
index c368ef4..69fe8f8 100644
--- a/platform/nuclei/ux600/Kconfig
+++ b/platform/nuclei/ux600/Kconfig
@@ -2,4 +2,5 @@
 
 config PLATFORM_NUCLEI_UX600
 	bool
+	select SERIAL_SIFIVE
 	default y
diff --git a/platform/template/Kconfig b/platform/template/Kconfig
index b9c4350..5a5472b 100644
--- a/platform/template/Kconfig
+++ b/platform/template/Kconfig
@@ -2,4 +2,5 @@
 
 config PLATFORM_TEMPLATE
 	bool
+	select SERIAL_UART8250
 	default y
-- 
2.34.1



From apatel at ventanamicro.com  Sat Jul 30 21:56:43 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Sun, 31 Jul 2022 10:26:43 +0530
Subject: [PATCH v4 05/15] lib: utils/reset: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220731045653.165058-1-apatel@ventanamicro.com>
References: <20220731045653.165058-1-apatel@ventanamicro.com>
Message-ID: <20220731045653.165058-6-apatel@ventanamicro.com>

We update reset drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate reset
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 include/sbi_utils/reset/fdt_reset.h | 12 +++++++++++
 lib/utils/Kconfig                   |  2 ++
 lib/utils/reset/Kconfig             | 33 +++++++++++++++++++++++++++++
 lib/utils/reset/objects.mk          | 28 ++++++++++++------------
 platform/generic/configs/defconfig  |  6 ++++++
 5 files changed, 67 insertions(+), 14 deletions(-)
 create mode 100644 lib/utils/reset/Kconfig

diff --git a/include/sbi_utils/reset/fdt_reset.h b/include/sbi_utils/reset/fdt_reset.h
index e7f7350..ea8063b 100644
--- a/include/sbi_utils/reset/fdt_reset.h
+++ b/include/sbi_utils/reset/fdt_reset.h
@@ -17,6 +17,8 @@ struct fdt_reset {
 	int (*init)(void *fdt, int nodeoff, const struct fdt_match *match);
 };
 
+#ifdef CONFIG_FDT_RESET
+
 /**
  * fdt_reset_driver_init() - initialize reset driver based on the device-tree
  */
@@ -29,4 +31,14 @@ int fdt_reset_driver_init(void *fdt, struct fdt_reset *drv);
  */
 void fdt_reset_init(void);
 
+#else
+
+static inline int fdt_reset_driver_init(void *fdt, struct fdt_reset *drv)
+{
+	return 0;
+}
+static inline void fdt_reset_init(void) { }
+
+#endif
+
 #endif
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 4524a3f..f061b7a 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -2,6 +2,8 @@
 
 menu "Utils and Drivers Support"
 
+source "$(OPENSBI_SRC_DIR)/lib/utils/reset/Kconfig"
+
 source "$(OPENSBI_SRC_DIR)/lib/utils/serial/Kconfig"
 
 endmenu
diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig
new file mode 100644
index 0000000..845a1ae
--- /dev/null
+++ b/lib/utils/reset/Kconfig
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "System Reset Support"
+
+config FDT_RESET
+	bool "FDT based reset drivers"
+	default n
+
+if FDT_RESET
+
+config FDT_RESET_GPIO
+	bool "GPIO FDT reset driver"
+	default n
+
+config FDT_RESET_HTIF
+	bool "Host transfer interface (HTIF) FDT reset driver"
+	default n
+
+config FDT_RESET_SIFIVE_TEST
+	bool "SiFive Test FDT reset driver"
+	default n
+
+config FDT_RESET_SUNXI_WDT
+	bool "Sunxi WDT FDT reset driver"
+	default n
+
+config FDT_RESET_THEAD
+	bool "T-HEAD FDT reset driver"
+	default n
+
+endif
+
+endmenu
diff --git a/lib/utils/reset/objects.mk b/lib/utils/reset/objects.mk
index 8cddcdf..8a50dd0 100644
--- a/lib/utils/reset/objects.mk
+++ b/lib/utils/reset/objects.mk
@@ -7,22 +7,22 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += reset/fdt_reset.o
-libsbiutils-objs-y += reset/fdt_reset_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_RESET) += reset/fdt_reset.o
+libsbiutils-objs-$(CONFIG_FDT_RESET) += reset/fdt_reset_drivers.o
 
-carray-fdt_reset_drivers-y += fdt_poweroff_gpio
-carray-fdt_reset_drivers-y += fdt_reset_gpio
-libsbiutils-objs-y += reset/fdt_reset_gpio.o
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_GPIO) += fdt_poweroff_gpio
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_GPIO) += fdt_reset_gpio
+libsbiutils-objs-$(CONFIG_FDT_RESET_GPIO) += reset/fdt_reset_gpio.o
 
-carray-fdt_reset_drivers-y += fdt_reset_htif
-libsbiutils-objs-y += reset/fdt_reset_htif.o
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_HTIF) += fdt_reset_htif
+libsbiutils-objs-$(CONFIG_FDT_RESET_HTIF) += reset/fdt_reset_htif.o
 
-carray-fdt_reset_drivers-y += fdt_reset_sifive_test
-libsbiutils-objs-y += reset/fdt_reset_sifive_test.o
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_SIFIVE_TEST) += fdt_reset_sifive_test
+libsbiutils-objs-$(CONFIG_FDT_RESET_SIFIVE_TEST) += reset/fdt_reset_sifive_test.o
 
-carray-fdt_reset_drivers-y += fdt_reset_sunxi_wdt
-libsbiutils-objs-y += reset/fdt_reset_sunxi_wdt.o
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_SUNXI_WDT) += fdt_reset_sunxi_wdt
+libsbiutils-objs-$(CONFIG_FDT_RESET_SUNXI_WDT) += reset/fdt_reset_sunxi_wdt.o
 
-carray-fdt_reset_drivers-y += fdt_reset_thead
-libsbiutils-objs-y += reset/fdt_reset_thead.o
-libsbiutils-objs-y += reset/fdt_reset_thead_asm.o
+carray-fdt_reset_drivers-$(CONFIG_FDT_RESET_THEAD) += fdt_reset_thead
+libsbiutils-objs-$(CONFIG_FDT_RESET_THEAD) += reset/fdt_reset_thead.o
+libsbiutils-objs-$(CONFIG_FDT_RESET_THEAD) += reset/fdt_reset_thead_asm.o
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index ce70c59..9778665 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -1,3 +1,9 @@
+CONFIG_FDT_RESET=y
+CONFIG_FDT_RESET_GPIO=y
+CONFIG_FDT_RESET_HTIF=y
+CONFIG_FDT_RESET_SIFIVE_TEST=y
+CONFIG_FDT_RESET_SUNXI_WDT=y
+CONFIG_FDT_RESET_THEAD=y
 CONFIG_FDT_SERIAL=y
 CONFIG_FDT_SERIAL_GAISLER=y
 CONFIG_FDT_SERIAL_HTIF=y
-- 
2.34.1



From apatel at ventanamicro.com  Sat Jul 30 21:56:44 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Sun, 31 Jul 2022 10:26:44 +0530
Subject: [PATCH v4 06/15] lib: utils/sys: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220731045653.165058-1-apatel@ventanamicro.com>
References: <20220731045653.165058-1-apatel@ventanamicro.com>
Message-ID: <20220731045653.165058-7-apatel@ventanamicro.com>

We update system drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate system
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 lib/utils/Kconfig        |  2 ++
 lib/utils/reset/Kconfig  |  2 ++
 lib/utils/serial/Kconfig |  1 +
 lib/utils/sys/Kconfig    | 13 +++++++++++++
 lib/utils/sys/objects.mk |  4 ++--
 5 files changed, 20 insertions(+), 2 deletions(-)
 create mode 100644 lib/utils/sys/Kconfig

diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index f061b7a..492f4e9 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -6,4 +6,6 @@ source "$(OPENSBI_SRC_DIR)/lib/utils/reset/Kconfig"
 
 source "$(OPENSBI_SRC_DIR)/lib/utils/serial/Kconfig"
 
+source "$(OPENSBI_SRC_DIR)/lib/utils/sys/Kconfig"
+
 endmenu
diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig
index 845a1ae..e7a71e3 100644
--- a/lib/utils/reset/Kconfig
+++ b/lib/utils/reset/Kconfig
@@ -14,10 +14,12 @@ config FDT_RESET_GPIO
 
 config FDT_RESET_HTIF
 	bool "Host transfer interface (HTIF) FDT reset driver"
+	select SYS_HTIF
 	default n
 
 config FDT_RESET_SIFIVE_TEST
 	bool "SiFive Test FDT reset driver"
+	select SYS_SIFIVE_TEST
 	default n
 
 config FDT_RESET_SUNXI_WDT
diff --git a/lib/utils/serial/Kconfig b/lib/utils/serial/Kconfig
index e114f26..bf37e34 100644
--- a/lib/utils/serial/Kconfig
+++ b/lib/utils/serial/Kconfig
@@ -15,6 +15,7 @@ config FDT_SERIAL_GAISLER
 
 config FDT_SERIAL_HTIF
 	bool "Host transfer interface (HTIF) UART FDT driver"
+	select SYS_HTIF
 	default n
 
 config FDT_SERIAL_SHAKTI
diff --git a/lib/utils/sys/Kconfig b/lib/utils/sys/Kconfig
new file mode 100644
index 0000000..ee85b1a
--- /dev/null
+++ b/lib/utils/sys/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "System Device Support"
+
+config SYS_HTIF
+	bool "Host transfere interface (HTIF) support"
+	default n
+
+config SYS_SIFIVE_TEST
+	bool "SiFive test support"
+	default n
+
+endmenu
diff --git a/lib/utils/sys/objects.mk b/lib/utils/sys/objects.mk
index 06be322..9f67aee 100644
--- a/lib/utils/sys/objects.mk
+++ b/lib/utils/sys/objects.mk
@@ -7,5 +7,5 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += sys/htif.o
-libsbiutils-objs-y += sys/sifive_test.o
+libsbiutils-objs-$(CONFIG_SYS_HTIF) += sys/htif.o
+libsbiutils-objs-$(CONFIG_SYS_SIFIVE_TEST) += sys/sifive_test.o
-- 
2.34.1



From apatel at ventanamicro.com  Sat Jul 30 21:56:45 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Sun, 31 Jul 2022 10:26:45 +0530
Subject: [PATCH v4 07/15] lib: utils/timer: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220731045653.165058-1-apatel@ventanamicro.com>
References: <20220731045653.165058-1-apatel@ventanamicro.com>
Message-ID: <20220731045653.165058-8-apatel@ventanamicro.com>

We update timer drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate timer
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 include/sbi_utils/timer/fdt_timer.h |  9 +++++++++
 lib/utils/Kconfig                   |  2 ++
 lib/utils/timer/Kconfig             | 22 ++++++++++++++++++++++
 lib/utils/timer/objects.mk          | 10 +++++-----
 platform/fpga/ariane/Kconfig        |  1 +
 platform/fpga/openpiton/Kconfig     |  1 +
 platform/generic/configs/defconfig  |  2 ++
 platform/kendryte/k210/Kconfig      |  1 +
 platform/nuclei/ux600/Kconfig       |  1 +
 platform/template/Kconfig           |  1 +
 10 files changed, 45 insertions(+), 5 deletions(-)
 create mode 100644 lib/utils/timer/Kconfig

diff --git a/include/sbi_utils/timer/fdt_timer.h b/include/sbi_utils/timer/fdt_timer.h
index 36202a4..cf105fe 100644
--- a/include/sbi_utils/timer/fdt_timer.h
+++ b/include/sbi_utils/timer/fdt_timer.h
@@ -12,6 +12,8 @@
 
 #include <sbi/sbi_types.h>
 
+#ifdef CONFIG_FDT_TIMER
+
 struct fdt_timer {
 	const struct fdt_match *match_table;
 	int (*cold_init)(void *fdt, int nodeoff, const struct fdt_match *match);
@@ -23,4 +25,11 @@ void fdt_timer_exit(void);
 
 int fdt_timer_init(bool cold_boot);
 
+#else
+
+static inline void fdt_timer_exit(void) { }
+static inline int fdt_timer_init(bool cold_boot) { return 0; }
+
+#endif
+
 #endif
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 492f4e9..6561d0b 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -8,4 +8,6 @@ source "$(OPENSBI_SRC_DIR)/lib/utils/serial/Kconfig"
 
 source "$(OPENSBI_SRC_DIR)/lib/utils/sys/Kconfig"
 
+source "$(OPENSBI_SRC_DIR)/lib/utils/timer/Kconfig"
+
 endmenu
diff --git a/lib/utils/timer/Kconfig b/lib/utils/timer/Kconfig
new file mode 100644
index 0000000..0a1c727
--- /dev/null
+++ b/lib/utils/timer/Kconfig
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "Timer Device Support"
+
+config FDT_TIMER
+	bool "FDT based timer drivers"
+	default n
+
+if FDT_TIMER
+
+config FDT_TIMER_MTIMER
+	bool "ACLINT MTIMER FDT driver"
+	select TIMER_MTIMER
+	default n
+
+endif
+
+config TIMER_MTIMER
+	bool "ACLINT MTIMER support"
+	default n
+
+endmenu
diff --git a/lib/utils/timer/objects.mk b/lib/utils/timer/objects.mk
index bc4073d..7f5f3ce 100644
--- a/lib/utils/timer/objects.mk
+++ b/lib/utils/timer/objects.mk
@@ -7,10 +7,10 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += timer/aclint_mtimer.o
+libsbiutils-objs-$(CONFIG_TIMER_MTIMER) += timer/aclint_mtimer.o
 
-libsbiutils-objs-y += timer/fdt_timer.o
-libsbiutils-objs-y += timer/fdt_timer_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_TIMER) += timer/fdt_timer.o
+libsbiutils-objs-$(CONFIG_FDT_TIMER) += timer/fdt_timer_drivers.o
 
-carray-fdt_timer_drivers-y += fdt_timer_mtimer
-libsbiutils-objs-y += timer/fdt_timer_mtimer.o
+carray-fdt_timer_drivers-$(CONFIG_FDT_TIMER_MTIMER) += fdt_timer_mtimer
+libsbiutils-objs-$(CONFIG_FDT_TIMER_MTIMER) += timer/fdt_timer_mtimer.o
diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
index 3ce0ec1..a7fd758 100644
--- a/platform/fpga/ariane/Kconfig
+++ b/platform/fpga/ariane/Kconfig
@@ -3,4 +3,5 @@
 config PLATFORM_ARIANE_FPGA
 	bool
 	select SERIAL_UART8250
+	select TIMER_MTIMER
 	default y
diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
index 4dd752d..a7f09e4 100644
--- a/platform/fpga/openpiton/Kconfig
+++ b/platform/fpga/openpiton/Kconfig
@@ -3,4 +3,5 @@
 config PLATFORM_OPENPITON_FPGA
 	bool
 	select SERIAL_UART8250
+	select TIMER_MTIMER
 	default y
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index 9778665..9b96a4f 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -12,3 +12,5 @@ CONFIG_FDT_SERIAL_SIFIVE=y
 CONFIG_FDT_SERIAL_LITEX=y
 CONFIG_FDT_SERIAL_UART8250=y
 CONFIG_FDT_SERIAL_XILINX_UARTLITE=y
+CONFIG_FDT_TIMER=y
+CONFIG_FDT_TIMER_MTIMER=y
diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
index adc7613..90db649 100644
--- a/platform/kendryte/k210/Kconfig
+++ b/platform/kendryte/k210/Kconfig
@@ -3,4 +3,5 @@
 config PLATFORM_KENDRYTE_K210
 	bool
 	select SERIAL_SIFIVE
+	select TIMER_MTIMER
 	default y
diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
index 69fe8f8..dec2654 100644
--- a/platform/nuclei/ux600/Kconfig
+++ b/platform/nuclei/ux600/Kconfig
@@ -3,4 +3,5 @@
 config PLATFORM_NUCLEI_UX600
 	bool
 	select SERIAL_SIFIVE
+	select TIMER_MTIMER
 	default y
diff --git a/platform/template/Kconfig b/platform/template/Kconfig
index 5a5472b..ce463ea 100644
--- a/platform/template/Kconfig
+++ b/platform/template/Kconfig
@@ -3,4 +3,5 @@
 config PLATFORM_TEMPLATE
 	bool
 	select SERIAL_UART8250
+	select TIMER_MTIMER
 	default y
-- 
2.34.1



From apatel at ventanamicro.com  Sat Jul 30 21:56:46 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Sun, 31 Jul 2022 10:26:46 +0530
Subject: [PATCH v4 08/15] lib: utils/ipi: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220731045653.165058-1-apatel@ventanamicro.com>
References: <20220731045653.165058-1-apatel@ventanamicro.com>
Message-ID: <20220731045653.165058-9-apatel@ventanamicro.com>

We update ipi drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate ipi
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 include/sbi_utils/ipi/fdt_ipi.h    |  9 +++++++++
 lib/utils/Kconfig                  |  2 ++
 lib/utils/ipi/Kconfig              | 22 ++++++++++++++++++++++
 lib/utils/ipi/objects.mk           | 10 +++++-----
 platform/fpga/ariane/Kconfig       |  1 +
 platform/fpga/openpiton/Kconfig    |  1 +
 platform/generic/configs/defconfig |  2 ++
 platform/kendryte/k210/Kconfig     |  1 +
 platform/nuclei/ux600/Kconfig      |  1 +
 platform/template/Kconfig          |  1 +
 10 files changed, 45 insertions(+), 5 deletions(-)
 create mode 100644 lib/utils/ipi/Kconfig

diff --git a/include/sbi_utils/ipi/fdt_ipi.h b/include/sbi_utils/ipi/fdt_ipi.h
index 9337353..1dd9062 100644
--- a/include/sbi_utils/ipi/fdt_ipi.h
+++ b/include/sbi_utils/ipi/fdt_ipi.h
@@ -12,6 +12,8 @@
 
 #include <sbi/sbi_types.h>
 
+#ifdef CONFIG_FDT_IPI
+
 struct fdt_ipi {
 	const struct fdt_match *match_table;
 	int (*cold_init)(void *fdt, int nodeoff, const struct fdt_match *match);
@@ -23,4 +25,11 @@ void fdt_ipi_exit(void);
 
 int fdt_ipi_init(bool cold_boot);
 
+#else
+
+static inline void fdt_ipi_exit(void) { }
+static inline int fdt_ipi_init(bool cold_boot) { return 0; }
+
+#endif
+
 #endif
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 6561d0b..50d9770 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -2,6 +2,8 @@
 
 menu "Utils and Drivers Support"
 
+source "$(OPENSBI_SRC_DIR)/lib/utils/ipi/Kconfig"
+
 source "$(OPENSBI_SRC_DIR)/lib/utils/reset/Kconfig"
 
 source "$(OPENSBI_SRC_DIR)/lib/utils/serial/Kconfig"
diff --git a/lib/utils/ipi/Kconfig b/lib/utils/ipi/Kconfig
new file mode 100644
index 0000000..c0634d3
--- /dev/null
+++ b/lib/utils/ipi/Kconfig
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "IPI Device Support"
+
+config FDT_IPI
+	bool "FDT based ipi drivers"
+	default n
+
+if FDT_IPI
+
+config FDT_IPI_MSWI
+	bool "ACLINT MSWI FDT driver"
+	select IPI_MSWI
+	default n
+
+endif
+
+config IPI_MSWI
+	bool "ACLINT MSWI support"
+	default n
+
+endmenu
diff --git a/lib/utils/ipi/objects.mk b/lib/utils/ipi/objects.mk
index 0b0bc2d..0600cac 100644
--- a/lib/utils/ipi/objects.mk
+++ b/lib/utils/ipi/objects.mk
@@ -7,10 +7,10 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += ipi/aclint_mswi.o
+libsbiutils-objs-$(CONFIG_IPI_MSWI) += ipi/aclint_mswi.o
 
-libsbiutils-objs-y += ipi/fdt_ipi.o
-libsbiutils-objs-y += ipi/fdt_ipi_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_IPI) += ipi/fdt_ipi.o
+libsbiutils-objs-$(CONFIG_FDT_IPI) += ipi/fdt_ipi_drivers.o
 
-carray-fdt_ipi_drivers-y += fdt_ipi_mswi
-libsbiutils-objs-y += ipi/fdt_ipi_mswi.o
+carray-fdt_ipi_drivers-$(CONFIG_FDT_IPI_MSWI) += fdt_ipi_mswi
+libsbiutils-objs-$(CONFIG_FDT_IPI_MSWI) += ipi/fdt_ipi_mswi.o
diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
index a7fd758..0bc1597 100644
--- a/platform/fpga/ariane/Kconfig
+++ b/platform/fpga/ariane/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_ARIANE_FPGA
 	bool
+	select IPI_MSWI
 	select SERIAL_UART8250
 	select TIMER_MTIMER
 	default y
diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
index a7f09e4..0e4e3ba 100644
--- a/platform/fpga/openpiton/Kconfig
+++ b/platform/fpga/openpiton/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_OPENPITON_FPGA
 	bool
+	select IPI_MSWI
 	select SERIAL_UART8250
 	select TIMER_MTIMER
 	default y
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index 9b96a4f..e218c32 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -1,3 +1,5 @@
+CONFIG_FDT_IPI=y
+CONFIG_FDT_IPI_MSWI=y
 CONFIG_FDT_RESET=y
 CONFIG_FDT_RESET_GPIO=y
 CONFIG_FDT_RESET_HTIF=y
diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
index 90db649..685a5cd 100644
--- a/platform/kendryte/k210/Kconfig
+++ b/platform/kendryte/k210/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_KENDRYTE_K210
 	bool
+	select IPI_MSWI
 	select SERIAL_SIFIVE
 	select TIMER_MTIMER
 	default y
diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
index dec2654..cd995a3 100644
--- a/platform/nuclei/ux600/Kconfig
+++ b/platform/nuclei/ux600/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_NUCLEI_UX600
 	bool
+	select IPI_MSWI
 	select SERIAL_SIFIVE
 	select TIMER_MTIMER
 	default y
diff --git a/platform/template/Kconfig b/platform/template/Kconfig
index ce463ea..672b6e7 100644
--- a/platform/template/Kconfig
+++ b/platform/template/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_TEMPLATE
 	bool
+	select IPI_MSWI
 	select SERIAL_UART8250
 	select TIMER_MTIMER
 	default y
-- 
2.34.1



From apatel at ventanamicro.com  Sat Jul 30 21:56:47 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Sun, 31 Jul 2022 10:26:47 +0530
Subject: [PATCH v4 09/15] lib: utils/irqchip: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220731045653.165058-1-apatel@ventanamicro.com>
References: <20220731045653.165058-1-apatel@ventanamicro.com>
Message-ID: <20220731045653.165058-10-apatel@ventanamicro.com>

We update irqchip drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate irqchip
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 include/sbi_utils/irqchip/fdt_irqchip.h | 10 +++++++
 include/sbi_utils/irqchip/imsic.h       | 10 +++++++
 lib/utils/Kconfig                       |  2 ++
 lib/utils/irqchip/Kconfig               | 40 +++++++++++++++++++++++++
 lib/utils/irqchip/objects.mk            | 22 +++++++-------
 platform/andes/ae350/Kconfig            |  1 +
 platform/fpga/ariane/Kconfig            |  1 +
 platform/fpga/openpiton/Kconfig         |  1 +
 platform/generic/configs/defconfig      |  4 +++
 platform/kendryte/k210/Kconfig          |  1 +
 platform/nuclei/ux600/Kconfig           |  1 +
 platform/template/Kconfig               |  1 +
 12 files changed, 83 insertions(+), 11 deletions(-)
 create mode 100644 lib/utils/irqchip/Kconfig

diff --git a/include/sbi_utils/irqchip/fdt_irqchip.h b/include/sbi_utils/irqchip/fdt_irqchip.h
index 13ef6f7..63e9fd9 100644
--- a/include/sbi_utils/irqchip/fdt_irqchip.h
+++ b/include/sbi_utils/irqchip/fdt_irqchip.h
@@ -12,6 +12,8 @@
 
 #include <sbi/sbi_types.h>
 
+#ifdef CONFIG_FDT_IRQCHIP
+
 struct fdt_irqchip {
 	const struct fdt_match *match_table;
 	int (*cold_init)(void *fdt, int nodeoff, const struct fdt_match *match);
@@ -23,4 +25,12 @@ void fdt_irqchip_exit(void);
 
 int fdt_irqchip_init(bool cold_boot);
 
+#else
+
+static inline void fdt_irqchip_exit(void) { }
+
+static inline int fdt_irqchip_init(bool cold_boot) { return 0; }
+
+#endif
+
 #endif
diff --git a/include/sbi_utils/irqchip/imsic.h b/include/sbi_utils/irqchip/imsic.h
index cffcb5a..e295771 100644
--- a/include/sbi_utils/irqchip/imsic.h
+++ b/include/sbi_utils/irqchip/imsic.h
@@ -33,6 +33,8 @@ struct imsic_data {
 	struct imsic_regs regs[IMSIC_MAX_REGS];
 };
 
+#ifdef CONFIG_IRQCHIP_IMSIC
+
 int imsic_map_hartid_to_data(u32 hartid, struct imsic_data *imsic, int file);
 
 struct imsic_data *imsic_get_data(u32 hartid);
@@ -47,4 +49,12 @@ int imsic_data_check(struct imsic_data *imsic);
 
 int imsic_cold_irqchip_init(struct imsic_data *imsic);
 
+#else
+
+static inline void imsic_local_irqchip_init(void) { }
+
+static inline int imsic_data_check(struct imsic_data *imsic) { return 0; }
+
+#endif
+
 #endif
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 50d9770..3f315fd 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -4,6 +4,8 @@ menu "Utils and Drivers Support"
 
 source "$(OPENSBI_SRC_DIR)/lib/utils/ipi/Kconfig"
 
+source "$(OPENSBI_SRC_DIR)/lib/utils/irqchip/Kconfig"
+
 source "$(OPENSBI_SRC_DIR)/lib/utils/reset/Kconfig"
 
 source "$(OPENSBI_SRC_DIR)/lib/utils/serial/Kconfig"
diff --git a/lib/utils/irqchip/Kconfig b/lib/utils/irqchip/Kconfig
new file mode 100644
index 0000000..6a7e297
--- /dev/null
+++ b/lib/utils/irqchip/Kconfig
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "Interrupt Controller Support"
+
+config FDT_IRQCHIP
+	bool "FDT based interrupt controller drivers"
+	default n
+
+if FDT_IRQCHIP
+
+config FDT_IRQCHIP_APLIC
+	bool "Advanced Platform Level Interrupt Controller (APLIC) FDT driver"
+	select IRQCHIP_APLIC
+	default n
+
+config FDT_IRQCHIP_IMSIC
+	bool "Incoming Message Signalled Interrupt Controller (IMSIC) FDT driver"
+	select IRQCHIP_IMSIC
+	default n
+
+config FDT_IRQCHIP_PLIC
+	bool "Platform Level Interrupt Controller (PLIC) FDT driver"
+	select IRQCHIP_PLIC
+	default n
+
+endif
+
+config IRQCHIP_APLIC
+	bool "Advanced Platform Level Interrupt Controller (APLIC) support"
+	default n
+
+config IRQCHIP_IMSIC
+	bool "Incoming Message Signalled Interrupt Controller (IMSIC) support"
+	default n
+
+config IRQCHIP_PLIC
+	bool "Platform Level Interrupt Controller (PLIC) support"
+	default n
+
+endmenu
diff --git a/lib/utils/irqchip/objects.mk b/lib/utils/irqchip/objects.mk
index 7775bc4..e458891 100644
--- a/lib/utils/irqchip/objects.mk
+++ b/lib/utils/irqchip/objects.mk
@@ -7,18 +7,18 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += irqchip/fdt_irqchip.o
-libsbiutils-objs-y += irqchip/fdt_irqchip_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_IRQCHIP) += irqchip/fdt_irqchip.o
+libsbiutils-objs-$(CONFIG_FDT_IRQCHIP) += irqchip/fdt_irqchip_drivers.o
 
-carray-fdt_irqchip_drivers-y += fdt_irqchip_aplic
-libsbiutils-objs-y += irqchip/fdt_irqchip_aplic.o
+carray-fdt_irqchip_drivers-$(CONFIG_FDT_IRQCHIP_APLIC) += fdt_irqchip_aplic
+libsbiutils-objs-$(CONFIG_FDT_IRQCHIP_APLIC) += irqchip/fdt_irqchip_aplic.o
 
-carray-fdt_irqchip_drivers-y += fdt_irqchip_imsic
-libsbiutils-objs-y += irqchip/fdt_irqchip_imsic.o
+carray-fdt_irqchip_drivers-$(CONFIG_FDT_IRQCHIP_IMSIC) += fdt_irqchip_imsic
+libsbiutils-objs-$(CONFIG_FDT_IRQCHIP_IMSIC) += irqchip/fdt_irqchip_imsic.o
 
-carray-fdt_irqchip_drivers-y += fdt_irqchip_plic
-libsbiutils-objs-y += irqchip/fdt_irqchip_plic.o
+carray-fdt_irqchip_drivers-$(CONFIG_FDT_IRQCHIP_PLIC) += fdt_irqchip_plic
+libsbiutils-objs-$(CONFIG_FDT_IRQCHIP_PLIC) += irqchip/fdt_irqchip_plic.o
 
-libsbiutils-objs-y += irqchip/aplic.o
-libsbiutils-objs-y += irqchip/imsic.o
-libsbiutils-objs-y += irqchip/plic.o
+libsbiutils-objs-$(CONFIG_IRQCHIP_APLIC) += irqchip/aplic.o
+libsbiutils-objs-$(CONFIG_IRQCHIP_IMSIC) += irqchip/imsic.o
+libsbiutils-objs-$(CONFIG_IRQCHIP_PLIC) += irqchip/plic.o
diff --git a/platform/andes/ae350/Kconfig b/platform/andes/ae350/Kconfig
index 162d19c..87deab0 100644
--- a/platform/andes/ae350/Kconfig
+++ b/platform/andes/ae350/Kconfig
@@ -2,5 +2,6 @@
 
 config PLATFORM_ANDES_AE350
 	bool
+	select IRQCHIP_PLIC
 	select SERIAL_UART8250
 	default y
diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
index 0bc1597..5a720d9 100644
--- a/platform/fpga/ariane/Kconfig
+++ b/platform/fpga/ariane/Kconfig
@@ -3,6 +3,7 @@
 config PLATFORM_ARIANE_FPGA
 	bool
 	select IPI_MSWI
+	select IRQCHIP_PLIC
 	select SERIAL_UART8250
 	select TIMER_MTIMER
 	default y
diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
index 0e4e3ba..973906e 100644
--- a/platform/fpga/openpiton/Kconfig
+++ b/platform/fpga/openpiton/Kconfig
@@ -3,6 +3,7 @@
 config PLATFORM_OPENPITON_FPGA
 	bool
 	select IPI_MSWI
+	select IRQCHIP_PLIC
 	select SERIAL_UART8250
 	select TIMER_MTIMER
 	default y
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index e218c32..ad66a63 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -1,5 +1,9 @@
 CONFIG_FDT_IPI=y
 CONFIG_FDT_IPI_MSWI=y
+CONFIG_FDT_IRQCHIP=y
+CONFIG_FDT_IRQCHIP_APLIC=y
+CONFIG_FDT_IRQCHIP_IMSIC=y
+CONFIG_FDT_IRQCHIP_PLIC=y
 CONFIG_FDT_RESET=y
 CONFIG_FDT_RESET_GPIO=y
 CONFIG_FDT_RESET_HTIF=y
diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
index 685a5cd..2a1daaa 100644
--- a/platform/kendryte/k210/Kconfig
+++ b/platform/kendryte/k210/Kconfig
@@ -3,6 +3,7 @@
 config PLATFORM_KENDRYTE_K210
 	bool
 	select IPI_MSWI
+	select IRQCHIP_PLIC
 	select SERIAL_SIFIVE
 	select TIMER_MTIMER
 	default y
diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
index cd995a3..464802e 100644
--- a/platform/nuclei/ux600/Kconfig
+++ b/platform/nuclei/ux600/Kconfig
@@ -3,6 +3,7 @@
 config PLATFORM_NUCLEI_UX600
 	bool
 	select IPI_MSWI
+	select IRQCHIP_PLIC
 	select SERIAL_SIFIVE
 	select TIMER_MTIMER
 	default y
diff --git a/platform/template/Kconfig b/platform/template/Kconfig
index 672b6e7..8338425 100644
--- a/platform/template/Kconfig
+++ b/platform/template/Kconfig
@@ -3,6 +3,7 @@
 config PLATFORM_TEMPLATE
 	bool
 	select IPI_MSWI
+	select IRQCHIP_PLIC
 	select SERIAL_UART8250
 	select TIMER_MTIMER
 	default y
-- 
2.34.1



From apatel at ventanamicro.com  Sat Jul 30 21:56:48 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Sun, 31 Jul 2022 10:26:48 +0530
Subject: [PATCH v4 10/15] lib: utils/i2c: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220731045653.165058-1-apatel@ventanamicro.com>
References: <20220731045653.165058-1-apatel@ventanamicro.com>
Message-ID: <20220731045653.165058-11-apatel@ventanamicro.com>

We update i2c drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate i2c
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 lib/utils/Kconfig                  |  2 ++
 lib/utils/i2c/Kconfig              | 22 ++++++++++++++++++++++
 lib/utils/i2c/objects.mk           | 10 +++++-----
 platform/generic/configs/defconfig |  2 ++
 4 files changed, 31 insertions(+), 5 deletions(-)
 create mode 100644 lib/utils/i2c/Kconfig

diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 3f315fd..39452d5 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -2,6 +2,8 @@
 
 menu "Utils and Drivers Support"
 
+source "$(OPENSBI_SRC_DIR)/lib/utils/i2c/Kconfig"
+
 source "$(OPENSBI_SRC_DIR)/lib/utils/ipi/Kconfig"
 
 source "$(OPENSBI_SRC_DIR)/lib/utils/irqchip/Kconfig"
diff --git a/lib/utils/i2c/Kconfig b/lib/utils/i2c/Kconfig
new file mode 100644
index 0000000..16c4453
--- /dev/null
+++ b/lib/utils/i2c/Kconfig
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "I2C Support"
+
+config FDT_I2C
+	bool "FDT based I2C drivers"
+	select I2C
+	default n
+
+if FDT_I2C
+
+config FDT_I2C_SIFIVE
+	bool "SiFive I2C FDT driver"
+	default n
+
+endif
+
+config I2C
+	bool "I2C support"
+	default n
+
+endmenu
diff --git a/lib/utils/i2c/objects.mk b/lib/utils/i2c/objects.mk
index bb0d6d5..a0fbbb5 100644
--- a/lib/utils/i2c/objects.mk
+++ b/lib/utils/i2c/objects.mk
@@ -7,10 +7,10 @@
 #   Nikita Shubin <n.shubin at yadro.com>
 #
 
-libsbiutils-objs-y += i2c/i2c.o
+libsbiutils-objs-$(CONFIG_I2C) += i2c/i2c.o
 
-libsbiutils-objs-y += i2c/fdt_i2c.o
-libsbiutils-objs-y += i2c/fdt_i2c_adapter_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_I2C) += i2c/fdt_i2c.o
+libsbiutils-objs-$(CONFIG_FDT_I2C) += i2c/fdt_i2c_adapter_drivers.o
 
-carray-fdt_i2c_adapter_drivers-y += fdt_i2c_adapter_sifive
-libsbiutils-objs-y += i2c/fdt_i2c_sifive.o
+carray-fdt_i2c_adapter_drivers-$(CONFIG_FDT_I2C_SIFIVE) += fdt_i2c_adapter_sifive
+libsbiutils-objs-$(CONFIG_FDT_I2C_SIFIVE) += i2c/fdt_i2c_sifive.o
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index ad66a63..c9c221e 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -1,3 +1,5 @@
+CONFIG_FDT_I2C=y
+CONFIG_FDT_I2C_SIFIVE=y
 CONFIG_FDT_IPI=y
 CONFIG_FDT_IPI_MSWI=y
 CONFIG_FDT_IRQCHIP=y
-- 
2.34.1



From apatel at ventanamicro.com  Sat Jul 30 21:56:49 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Sun, 31 Jul 2022 10:26:49 +0530
Subject: [PATCH v4 11/15] lib: utils/gpio: Use kconfig for enabling/disabling drivers
In-Reply-To: <20220731045653.165058-1-apatel@ventanamicro.com>
References: <20220731045653.165058-1-apatel@ventanamicro.com>
Message-ID: <20220731045653.165058-12-apatel@ventanamicro.com>

We update gpio drivers makefile to use kconfig for enabling/disabling
drivers. To avoid compile errors, we also enable appropriate gpio
drivers for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 lib/utils/Kconfig                  |  2 ++
 lib/utils/gpio/Kconfig             | 22 ++++++++++++++++++++++
 lib/utils/gpio/objects.mk          | 10 +++++-----
 lib/utils/reset/Kconfig            |  1 +
 platform/generic/configs/defconfig |  2 ++
 5 files changed, 32 insertions(+), 5 deletions(-)
 create mode 100644 lib/utils/gpio/Kconfig

diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 39452d5..673b02b 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -2,6 +2,8 @@
 
 menu "Utils and Drivers Support"
 
+source "$(OPENSBI_SRC_DIR)/lib/utils/gpio/Kconfig"
+
 source "$(OPENSBI_SRC_DIR)/lib/utils/i2c/Kconfig"
 
 source "$(OPENSBI_SRC_DIR)/lib/utils/ipi/Kconfig"
diff --git a/lib/utils/gpio/Kconfig b/lib/utils/gpio/Kconfig
new file mode 100644
index 0000000..daf7f91
--- /dev/null
+++ b/lib/utils/gpio/Kconfig
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "GPIO Support"
+
+config FDT_GPIO
+	bool "FDT based GPIO drivers"
+	select GPIO
+	default n
+
+if FDT_GPIO
+
+config FDT_GPIO_SIFIVE
+	bool "SiFive GPIO FDT driver"
+	default n
+
+endif
+
+config GPIO
+	bool "GPIO support"
+	default n
+
+endmenu
diff --git a/lib/utils/gpio/objects.mk b/lib/utils/gpio/objects.mk
index a5e131b..eedd699 100644
--- a/lib/utils/gpio/objects.mk
+++ b/lib/utils/gpio/objects.mk
@@ -7,10 +7,10 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
-libsbiutils-objs-y += gpio/fdt_gpio.o
-libsbiutils-objs-y += gpio/fdt_gpio_drivers.o
+libsbiutils-objs-$(CONFIG_FDT_GPIO) += gpio/fdt_gpio.o
+libsbiutils-objs-$(CONFIG_FDT_GPIO) += gpio/fdt_gpio_drivers.o
 
-carray-fdt_gpio_drivers-y += fdt_gpio_sifive
-libsbiutils-objs-y += gpio/fdt_gpio_sifive.o
+carray-fdt_gpio_drivers-$(CONFIG_FDT_GPIO_SIFIVE) += fdt_gpio_sifive
+libsbiutils-objs-$(CONFIG_FDT_GPIO_SIFIVE) += gpio/fdt_gpio_sifive.o
 
-libsbiutils-objs-y += gpio/gpio.o
+libsbiutils-objs-$(CONFIG_GPIO) += gpio/gpio.o
diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig
index e7a71e3..71996cb 100644
--- a/lib/utils/reset/Kconfig
+++ b/lib/utils/reset/Kconfig
@@ -10,6 +10,7 @@ if FDT_RESET
 
 config FDT_RESET_GPIO
 	bool "GPIO FDT reset driver"
+	depends on FDT_GPIO
 	default n
 
 config FDT_RESET_HTIF
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index c9c221e..d56cef2 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -1,3 +1,5 @@
+CONFIG_FDT_GPIO=y
+CONFIG_FDT_GPIO_SIFIVE=y
 CONFIG_FDT_I2C=y
 CONFIG_FDT_I2C_SIFIVE=y
 CONFIG_FDT_IPI=y
-- 
2.34.1



From apatel at ventanamicro.com  Sat Jul 30 21:56:50 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Sun, 31 Jul 2022 10:26:50 +0530
Subject: [PATCH v4 12/15] lib: utils/fdt: Use kconfig for enabling/disabling
In-Reply-To: <20220731045653.165058-1-apatel@ventanamicro.com>
References: <20220731045653.165058-1-apatel@ventanamicro.com>
Message-ID: <20220731045653.165058-13-apatel@ventanamicro.com>

We update FDT support makefile to use kconfig for enabling/disabling.
To avoid compilation errors, we also enable FDT for each platform.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 include/sbi_utils/fdt/fdt_domain.h             |  9 +++++++++
 include/sbi_utils/fdt/fdt_pmu.h                | 10 ++++++++++
 lib/utils/Kconfig                              |  4 ++++
 lib/utils/fdt/Kconfig                          | 18 ++++++++++++++++++
 lib/utils/fdt/objects.mk                       |  8 ++++----
 lib/utils/gpio/Kconfig                         |  1 +
 lib/utils/i2c/Kconfig                          |  1 +
 lib/utils/ipi/Kconfig                          |  1 +
 lib/utils/irqchip/Kconfig                      |  1 +
 {platform/generic => lib/utils/libfdt}/Kconfig |  4 ++--
 lib/utils/libfdt/objects.mk                    |  4 ++--
 lib/utils/reset/Kconfig                        |  1 +
 lib/utils/serial/Kconfig                       |  1 +
 lib/utils/timer/Kconfig                        |  1 +
 platform/andes/ae350/Kconfig                   |  1 +
 platform/fpga/ariane/Kconfig                   |  1 +
 platform/fpga/openpiton/Kconfig                |  1 +
 platform/generic/Kconfig                       |  3 +++
 platform/kendryte/k210/Kconfig                 |  1 +
 platform/nuclei/ux600/Kconfig                  |  1 +
 platform/nuclei/ux600/platform.c               |  1 -
 21 files changed, 64 insertions(+), 9 deletions(-)
 create mode 100644 lib/utils/fdt/Kconfig
 copy {platform/generic => lib/utils/libfdt}/Kconfig (57%)

diff --git a/include/sbi_utils/fdt/fdt_domain.h b/include/sbi_utils/fdt/fdt_domain.h
index 5448eb4..ee09d1b 100644
--- a/include/sbi_utils/fdt/fdt_domain.h
+++ b/include/sbi_utils/fdt/fdt_domain.h
@@ -13,6 +13,8 @@
 
 #include <sbi/sbi_types.h>
 
+#ifdef CONFIG_FDT_DOMAIN
+
 struct sbi_domain;
 
 /**
@@ -70,4 +72,11 @@ void fdt_domain_fixup(void *fdt);
  */
 int fdt_domains_populate(void *fdt);
 
+#else
+
+static inline void fdt_domain_fixup(void *fdt) { }
+static inline int fdt_domains_populate(void *fdt) { return 0; }
+
+#endif
+
 #endif /* __FDT_DOMAIN_H__ */
diff --git a/include/sbi_utils/fdt/fdt_pmu.h b/include/sbi_utils/fdt/fdt_pmu.h
index 2fa01ed..c65cad7 100644
--- a/include/sbi_utils/fdt/fdt_pmu.h
+++ b/include/sbi_utils/fdt/fdt_pmu.h
@@ -13,6 +13,8 @@
 
 #include <sbi/sbi_types.h>
 
+#ifdef CONFIG_FDT_PMU
+
 /**
  * Fix up the PMU node in the device tree
  *
@@ -43,4 +45,12 @@ int fdt_pmu_setup(void *fdt);
  */
 uint64_t fdt_pmu_get_select_value(uint32_t event_idx);
 
+#else
+
+static inline void fdt_pmu_fixup(void *fdt) { }
+static inline int fdt_pmu_setup(void *fdt) { return 0; }
+static inline uint64_t fdt_pmu_get_select_value(uint32_t event_idx) { return 0; }
+
+#endif
+
 #endif
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 673b02b..5a71e75 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -2,6 +2,8 @@
 
 menu "Utils and Drivers Support"
 
+source "$(OPENSBI_SRC_DIR)/lib/utils/fdt/Kconfig"
+
 source "$(OPENSBI_SRC_DIR)/lib/utils/gpio/Kconfig"
 
 source "$(OPENSBI_SRC_DIR)/lib/utils/i2c/Kconfig"
@@ -10,6 +12,8 @@ source "$(OPENSBI_SRC_DIR)/lib/utils/ipi/Kconfig"
 
 source "$(OPENSBI_SRC_DIR)/lib/utils/irqchip/Kconfig"
 
+source "$(OPENSBI_SRC_DIR)/lib/utils/libfdt/Kconfig"
+
 source "$(OPENSBI_SRC_DIR)/lib/utils/reset/Kconfig"
 
 source "$(OPENSBI_SRC_DIR)/lib/utils/serial/Kconfig"
diff --git a/lib/utils/fdt/Kconfig b/lib/utils/fdt/Kconfig
new file mode 100644
index 0000000..23b003b
--- /dev/null
+++ b/lib/utils/fdt/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menuconfig FDT
+	bool "Flattened Device Tree (FDT) support"
+	select LIBFDT
+	default n
+
+if FDT
+
+config FDT_DOMAIN
+	bool "FDT domain support"
+	default n
+
+config FDT_PMU
+	bool "FDT performance monitoring unit (PMU) support"
+	default n
+
+endif
diff --git a/lib/utils/fdt/objects.mk b/lib/utils/fdt/objects.mk
index 03800f9..5cede81 100644
--- a/lib/utils/fdt/objects.mk
+++ b/lib/utils/fdt/objects.mk
@@ -4,7 +4,7 @@
 # Copyright (C) 2020 Bin Meng <bmeng.cn at gmail.com>
 #
 
-libsbiutils-objs-y += fdt/fdt_domain.o
-libsbiutils-objs-y += fdt/fdt_pmu.o
-libsbiutils-objs-y += fdt/fdt_helper.o
-libsbiutils-objs-y += fdt/fdt_fixup.o
+libsbiutils-objs-$(CONFIG_FDT_DOMAIN) += fdt/fdt_domain.o
+libsbiutils-objs-$(CONFIG_FDT_PMU) += fdt/fdt_pmu.o
+libsbiutils-objs-$(CONFIG_FDT) += fdt/fdt_helper.o
+libsbiutils-objs-$(CONFIG_FDT) += fdt/fdt_fixup.o
diff --git a/lib/utils/gpio/Kconfig b/lib/utils/gpio/Kconfig
index daf7f91..38a9d75 100644
--- a/lib/utils/gpio/Kconfig
+++ b/lib/utils/gpio/Kconfig
@@ -4,6 +4,7 @@ menu "GPIO Support"
 
 config FDT_GPIO
 	bool "FDT based GPIO drivers"
+	depends on FDT
 	select GPIO
 	default n
 
diff --git a/lib/utils/i2c/Kconfig b/lib/utils/i2c/Kconfig
index 16c4453..46a3454 100644
--- a/lib/utils/i2c/Kconfig
+++ b/lib/utils/i2c/Kconfig
@@ -4,6 +4,7 @@ menu "I2C Support"
 
 config FDT_I2C
 	bool "FDT based I2C drivers"
+	depends on FDT
 	select I2C
 	default n
 
diff --git a/lib/utils/ipi/Kconfig b/lib/utils/ipi/Kconfig
index c0634d3..efc8960 100644
--- a/lib/utils/ipi/Kconfig
+++ b/lib/utils/ipi/Kconfig
@@ -4,6 +4,7 @@ menu "IPI Device Support"
 
 config FDT_IPI
 	bool "FDT based ipi drivers"
+	depends on FDT
 	default n
 
 if FDT_IPI
diff --git a/lib/utils/irqchip/Kconfig b/lib/utils/irqchip/Kconfig
index 6a7e297..7a71388 100644
--- a/lib/utils/irqchip/Kconfig
+++ b/lib/utils/irqchip/Kconfig
@@ -4,6 +4,7 @@ menu "Interrupt Controller Support"
 
 config FDT_IRQCHIP
 	bool "FDT based interrupt controller drivers"
+	depends on FDT
 	default n
 
 if FDT_IRQCHIP
diff --git a/platform/generic/Kconfig b/lib/utils/libfdt/Kconfig
similarity index 57%
copy from platform/generic/Kconfig
copy to lib/utils/libfdt/Kconfig
index 3eab282..d1cecf8 100644
--- a/platform/generic/Kconfig
+++ b/lib/utils/libfdt/Kconfig
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: BSD-2-Clause
 
-config PLATFORM_GENERIC
+config LIBFDT
 	bool
-	default y
+	default n
diff --git a/lib/utils/libfdt/objects.mk b/lib/utils/libfdt/objects.mk
index 8c060df..a5b4001 100644
--- a/lib/utils/libfdt/objects.mk
+++ b/lib/utils/libfdt/objects.mk
@@ -12,5 +12,5 @@ libfdt_files = fdt.o fdt_addresses.o fdt_check.o fdt_empty_tree.o fdt_ro.o fdt_r
 $(foreach file, $(libfdt_files), \
         $(eval CFLAGS_$(file) = -I$(src)/../../utils/libfdt))
 
-libsbiutils-objs-y += $(addprefix libfdt/,$(libfdt_files))
-libsbiutils-genflags-y  += -I$(libsbiutils_dir)/libfdt/
+libsbiutils-objs-$(CONFIG_LIBFDT) += $(addprefix libfdt/,$(libfdt_files))
+libsbiutils-genflags-$(CONFIG_LIBFDT)  += -I$(libsbiutils_dir)/libfdt/
diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig
index 71996cb..2e83ff6 100644
--- a/lib/utils/reset/Kconfig
+++ b/lib/utils/reset/Kconfig
@@ -4,6 +4,7 @@ menu "System Reset Support"
 
 config FDT_RESET
 	bool "FDT based reset drivers"
+	depends on FDT
 	default n
 
 if FDT_RESET
diff --git a/lib/utils/serial/Kconfig b/lib/utils/serial/Kconfig
index bf37e34..152060d 100644
--- a/lib/utils/serial/Kconfig
+++ b/lib/utils/serial/Kconfig
@@ -4,6 +4,7 @@ menu "Serial Device Support"
 
 config FDT_SERIAL
 	bool "FDT based serial drivers"
+	depends on FDT
 	default n
 
 if FDT_SERIAL
diff --git a/lib/utils/timer/Kconfig b/lib/utils/timer/Kconfig
index 0a1c727..23c48c5 100644
--- a/lib/utils/timer/Kconfig
+++ b/lib/utils/timer/Kconfig
@@ -4,6 +4,7 @@ menu "Timer Device Support"
 
 config FDT_TIMER
 	bool "FDT based timer drivers"
+	depends on FDT
 	default n
 
 if FDT_TIMER
diff --git a/platform/andes/ae350/Kconfig b/platform/andes/ae350/Kconfig
index 87deab0..d57f19f 100644
--- a/platform/andes/ae350/Kconfig
+++ b/platform/andes/ae350/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_ANDES_AE350
 	bool
+	select FDT
 	select IRQCHIP_PLIC
 	select SERIAL_UART8250
 	default y
diff --git a/platform/fpga/ariane/Kconfig b/platform/fpga/ariane/Kconfig
index 5a720d9..e1f118b 100644
--- a/platform/fpga/ariane/Kconfig
+++ b/platform/fpga/ariane/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_ARIANE_FPGA
 	bool
+	select FDT
 	select IPI_MSWI
 	select IRQCHIP_PLIC
 	select SERIAL_UART8250
diff --git a/platform/fpga/openpiton/Kconfig b/platform/fpga/openpiton/Kconfig
index 973906e..bc9c86e 100644
--- a/platform/fpga/openpiton/Kconfig
+++ b/platform/fpga/openpiton/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_OPENPITON_FPGA
 	bool
+	select FDT
 	select IPI_MSWI
 	select IRQCHIP_PLIC
 	select SERIAL_UART8250
diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
index 3eab282..c1a77b3 100644
--- a/platform/generic/Kconfig
+++ b/platform/generic/Kconfig
@@ -2,4 +2,7 @@
 
 config PLATFORM_GENERIC
 	bool
+	select FDT
+	select FDT_DOMAIN
+	select FDT_PMU
 	default y
diff --git a/platform/kendryte/k210/Kconfig b/platform/kendryte/k210/Kconfig
index 2a1daaa..5bf5973 100644
--- a/platform/kendryte/k210/Kconfig
+++ b/platform/kendryte/k210/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_KENDRYTE_K210
 	bool
+	select FDT
 	select IPI_MSWI
 	select IRQCHIP_PLIC
 	select SERIAL_SIFIVE
diff --git a/platform/nuclei/ux600/Kconfig b/platform/nuclei/ux600/Kconfig
index 464802e..84d74e8 100644
--- a/platform/nuclei/ux600/Kconfig
+++ b/platform/nuclei/ux600/Kconfig
@@ -2,6 +2,7 @@
 
 config PLATFORM_NUCLEI_UX600
 	bool
+	select FDT
 	select IPI_MSWI
 	select IRQCHIP_PLIC
 	select SERIAL_SIFIVE
diff --git a/platform/nuclei/ux600/platform.c b/platform/nuclei/ux600/platform.c
index 5498a1f..2b02734 100644
--- a/platform/nuclei/ux600/platform.c
+++ b/platform/nuclei/ux600/platform.c
@@ -8,7 +8,6 @@
  *   hqfang <578567190 at qq.com>
  */
 
-#include <libfdt.h>
 #include <sbi/riscv_asm.h>
 #include <sbi/riscv_io.h>
 #include <sbi/riscv_encoding.h>
-- 
2.34.1



From apatel at ventanamicro.com  Sat Jul 30 21:56:51 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Sun, 31 Jul 2022 10:26:51 +0530
Subject: [PATCH v4 13/15] platform: generic: Use kconfig for enabling/disabling overrides
In-Reply-To: <20220731045653.165058-1-apatel@ventanamicro.com>
References: <20220731045653.165058-1-apatel@ventanamicro.com>
Message-ID: <20220731045653.165058-14-apatel@ventanamicro.com>

We update generic platform to use kconfig for enabling/disabling
platform overrides. We also enable all platform overrides in generic
platform defconfig.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 platform/generic/Kconfig              | 18 ++++++++++++++++++
 platform/generic/allwinner/objects.mk |  4 ++--
 platform/generic/configs/defconfig    |  3 +++
 platform/generic/sifive/objects.mk    |  8 ++++----
 4 files changed, 27 insertions(+), 6 deletions(-)

diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
index c1a77b3..c76bd12 100644
--- a/platform/generic/Kconfig
+++ b/platform/generic/Kconfig
@@ -6,3 +6,21 @@ config PLATFORM_GENERIC
 	select FDT_DOMAIN
 	select FDT_PMU
 	default y
+
+if PLATFORM_GENERIC
+
+config PLATFORM_ALLWINNER_D1
+	bool "Allwinner D1 support"
+	depends on FDT_IRQCHIP_PLIC
+	default n
+
+config PLATFORM_SIFIVE_FU540
+	bool "SiFive FU540 support"
+	default n
+
+config PLATFORM_SIFIVE_FU740
+	bool "SiFive FU740 support"
+	depends on FDT_RESET && FDT_I2C
+	default n
+
+endif
diff --git a/platform/generic/allwinner/objects.mk b/platform/generic/allwinner/objects.mk
index 9e36ab6..447cdf1 100644
--- a/platform/generic/allwinner/objects.mk
+++ b/platform/generic/allwinner/objects.mk
@@ -2,5 +2,5 @@
 # SPDX-License-Identifier: BSD-2-Clause
 #
 
-carray-platform_override_modules-y += sun20i_d1
-platform-objs-y += allwinner/sun20i-d1.o
+carray-platform_override_modules-$(CONFIG_PLATFORM_ALLWINNER_D1) += sun20i_d1
+platform-objs-$(CONFIG_PLATFORM_ALLWINNER_D1) += allwinner/sun20i-d1.o
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index d56cef2..2a75394 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -1,3 +1,6 @@
+CONFIG_PLATFORM_ALLWINNER_D1=y
+CONFIG_PLATFORM_SIFIVE_FU540=y
+CONFIG_PLATFORM_SIFIVE_FU740=y
 CONFIG_FDT_GPIO=y
 CONFIG_FDT_GPIO_SIFIVE=y
 CONFIG_FDT_I2C=y
diff --git a/platform/generic/sifive/objects.mk b/platform/generic/sifive/objects.mk
index c17e2df..d75e444 100644
--- a/platform/generic/sifive/objects.mk
+++ b/platform/generic/sifive/objects.mk
@@ -2,8 +2,8 @@
 # SPDX-License-Identifier: BSD-2-Clause
 #
 
-carray-platform_override_modules-y += sifive_fu540
-platform-objs-y += sifive/fu540.o
+carray-platform_override_modules-$(CONFIG_PLATFORM_SIFIVE_FU540) += sifive_fu540
+platform-objs-$(CONFIG_PLATFORM_SIFIVE_FU540) += sifive/fu540.o
 
-carray-platform_override_modules-y += sifive_fu740
-platform-objs-y += sifive/fu740.o
+carray-platform_override_modules-$(CONFIG_PLATFORM_SIFIVE_FU740) += sifive_fu740
+platform-objs-$(CONFIG_PLATFORM_SIFIVE_FU740) += sifive/fu740.o
-- 
2.34.1



From apatel at ventanamicro.com  Sat Jul 30 21:56:39 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Sun, 31 Jul 2022 10:26:39 +0530
Subject: [PATCH v4 01/15] scripts: Add Kconfiglib v14.1.0 under scripts directory
In-Reply-To: <20220731045653.165058-1-apatel@ventanamicro.com>
References: <20220731045653.165058-1-apatel@ventanamicro.com>
Message-ID: <20220731045653.165058-2-apatel@ventanamicro.com>

We adopt Kconfiglib v14.1.0 sources under scripts directory so that
top-level OpenSBI makefile can directly use Kconfiglib scripts without
expecting users to install a particular version of Kconfiglib on their
build system.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 scripts/Kconfiglib/LICENSE.txt      |    5 +
 scripts/Kconfiglib/allnoconfig.py   |   45 +
 scripts/Kconfiglib/allyesconfig.py  |   56 +
 scripts/Kconfiglib/defconfig.py     |   43 +
 scripts/Kconfiglib/genconfig.py     |  154 +
 scripts/Kconfiglib/kconfiglib.py    | 7160 +++++++++++++++++++++++++++
 scripts/Kconfiglib/menuconfig.py    | 3278 ++++++++++++
 scripts/Kconfiglib/oldconfig.py     |  246 +
 scripts/Kconfiglib/olddefconfig.py  |   28 +
 scripts/Kconfiglib/savedefconfig.py |   49 +
 scripts/Kconfiglib/setconfig.py     |   92 +
 11 files changed, 11156 insertions(+)
 create mode 100644 scripts/Kconfiglib/LICENSE.txt
 create mode 100755 scripts/Kconfiglib/allnoconfig.py
 create mode 100755 scripts/Kconfiglib/allyesconfig.py
 create mode 100755 scripts/Kconfiglib/defconfig.py
 create mode 100755 scripts/Kconfiglib/genconfig.py
 create mode 100644 scripts/Kconfiglib/kconfiglib.py
 create mode 100755 scripts/Kconfiglib/menuconfig.py
 create mode 100755 scripts/Kconfiglib/oldconfig.py
 create mode 100755 scripts/Kconfiglib/olddefconfig.py
 create mode 100755 scripts/Kconfiglib/savedefconfig.py
 create mode 100755 scripts/Kconfiglib/setconfig.py

diff --git a/scripts/Kconfiglib/LICENSE.txt b/scripts/Kconfiglib/LICENSE.txt
new file mode 100644
index 0000000..8b31efc
--- /dev/null
+++ b/scripts/Kconfiglib/LICENSE.txt
@@ -0,0 +1,5 @@
+Copyright (c) 2011-2019, Ulf Magnusson <ulfalizer at gmail.com>
+
+Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/scripts/Kconfiglib/allnoconfig.py b/scripts/Kconfiglib/allnoconfig.py
new file mode 100755
index 0000000..de90d8b
--- /dev/null
+++ b/scripts/Kconfiglib/allnoconfig.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Writes a configuration file where as many symbols as possible are set to 'n'.
+
+The default output filename is '.config'. A different filename can be passed
+in the KCONFIG_CONFIG environment variable.
+
+Usage for the Linux kernel:
+
+  $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/allnoconfig.py
+"""
+
+# See examples/allnoconfig_walk.py for another way to implement this script
+
+import kconfiglib
+
+
+def main():
+    kconf = kconfiglib.standard_kconfig(__doc__)
+
+    # Avoid warnings that would otherwise get printed by Kconfiglib for the
+    # following:
+    #
+    # 1. Assigning a value to a symbol without a prompt, which never has any
+    #    effect
+    #
+    # 2. Assigning values invalid for the type (only bool/tristate symbols
+    #    accept 0/1/2, for n/m/y). The assignments will be ignored for other
+    #    symbol types, which is what we want.
+    kconf.warn = False
+    for sym in kconf.unique_defined_syms:
+        sym.set_value(2 if sym.is_allnoconfig_y else 0)
+    kconf.warn = True
+
+    kconf.load_allconfig("allno.config")
+
+    print(kconf.write_config())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/Kconfiglib/allyesconfig.py b/scripts/Kconfiglib/allyesconfig.py
new file mode 100755
index 0000000..90eb9b8
--- /dev/null
+++ b/scripts/Kconfiglib/allyesconfig.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Writes a configuration file where as many symbols as possible are set to 'y'.
+
+The default output filename is '.config'. A different filename can be passed
+in the KCONFIG_CONFIG environment variable.
+
+Usage for the Linux kernel:
+
+  $ make [ARCH=<arch>] scriptconfig SCRIPT=Kconfiglib/allyesconfig.py
+"""
+import kconfiglib
+
+
+def main():
+    kconf = kconfiglib.standard_kconfig(__doc__)
+
+    # See allnoconfig.py
+    kconf.warn = False
+
+    # Try to set all symbols to 'y'. Dependencies might truncate the value down
+    # later, but this will at least give the highest possible value.
+    #
+    # Assigning 0/1/2 to non-bool/tristate symbols has no effect (int/hex
+    # symbols still take a string, because they preserve formatting).
+    for sym in kconf.unique_defined_syms:
+        # Set choice symbols to 'm'. This value will be ignored for choices in
+        # 'y' mode (the "normal" mode), which will instead just get their
+        # default selection, but will set all symbols in m-mode choices to 'm',
+        # which is as high as they can go.
+        #
+        # Here's a convoluted example of how you might get an m-mode choice
+        # even during allyesconfig:
+        #
+        #   choice
+        #           tristate "weird choice"
+        #           depends on m
+        sym.set_value(1 if sym.choice else 2)
+
+    # Set all choices to the highest possible mode
+    for choice in kconf.unique_choices:
+        choice.set_value(2)
+
+    kconf.warn = True
+
+    kconf.load_allconfig("allyes.config")
+
+    print(kconf.write_config())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/Kconfiglib/defconfig.py b/scripts/Kconfiglib/defconfig.py
new file mode 100755
index 0000000..b179273
--- /dev/null
+++ b/scripts/Kconfiglib/defconfig.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Reads a specified configuration file, then writes a new configuration file.
+This can be used to initialize the configuration from e.g. an arch-specific
+configuration file. This input configuration file would usually be a minimal
+configuration file, as generated by e.g. savedefconfig.
+
+The default output filename is '.config'. A different filename can be passed in
+the KCONFIG_CONFIG environment variable.
+"""
+import argparse
+
+import kconfiglib
+
+
+def main():
+    parser = argparse.ArgumentParser(
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        description=__doc__)
+
+    parser.add_argument(
+        "--kconfig",
+        default="Kconfig",
+        help="Top-level Kconfig file (default: Kconfig)")
+
+    parser.add_argument(
+        "config",
+        metavar="CONFIGURATION",
+        help="Input configuration file")
+
+    args = parser.parse_args()
+
+    kconf = kconfiglib.Kconfig(args.kconfig, suppress_traceback=True)
+    print(kconf.load_config(args.config))
+    print(kconf.write_config())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/Kconfiglib/genconfig.py b/scripts/Kconfiglib/genconfig.py
new file mode 100755
index 0000000..62f065b
--- /dev/null
+++ b/scripts/Kconfiglib/genconfig.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Generates a header file with #defines from the configuration, matching the
+format of include/generated/autoconf.h in the Linux kernel.
+
+Optionally, also writes the configuration output as a .config file. See
+--config-out.
+
+The --sync-deps, --file-list, and --env-list options generate information that
+can be used to avoid needless rebuilds/reconfigurations.
+
+Before writing a header or configuration file, Kconfiglib compares the old
+contents of the file against the new contents. If there's no change, the write
+is skipped. This avoids updating file metadata like the modification time, and
+might save work depending on your build setup.
+
+By default, the configuration is generated from '.config'. A different
+configuration file can be passed in the KCONFIG_CONFIG environment variable.
+
+A custom header string can be inserted at the beginning of generated
+configuration and header files by setting the KCONFIG_CONFIG_HEADER and
+KCONFIG_AUTOHEADER_HEADER environment variables, respectively (this also works
+for other scripts). The string is not automatically made a comment (this is by
+design, to allow anything to be added), and no trailing newline is added, so
+add '/* */', '#', and newlines as appropriate.
+
+See https://www.gnu.org/software/make/manual/make.html#Multi_002dLine for a
+handy way to define multi-line variables in makefiles, for use with custom
+headers. Remember to export the variable to the environment.
+"""
+import argparse
+import os
+import sys
+
+import kconfiglib
+
+
+DEFAULT_SYNC_DEPS_PATH = "deps/"
+
+
+def main():
+    parser = argparse.ArgumentParser(
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        description=__doc__)
+
+    parser.add_argument(
+        "--header-path",
+        metavar="HEADER_FILE",
+        help="""
+Path to write the generated header file to. If not specified, the path in the
+environment variable KCONFIG_AUTOHEADER is used if it is set, and 'config.h'
+otherwise.
+""")
+
+    parser.add_argument(
+        "--config-out",
+        metavar="CONFIG_FILE",
+        help="""
+Write the configuration to CONFIG_FILE. This is useful if you include .config
+files in Makefiles, as the generated configuration file will be a full .config
+file even if .config is outdated. The generated configuration matches what
+olddefconfig would produce. If you use sync-deps, you can include
+deps/auto.conf instead. --config-out is meant for cases where incremental build
+information isn't needed.
+""")
+
+    parser.add_argument(
+        "--sync-deps",
+        metavar="OUTPUT_DIR",
+        nargs="?",
+        const=DEFAULT_SYNC_DEPS_PATH,
+        help="""
+Enable generation of symbol dependency information for incremental builds,
+optionally specifying the output directory (default: {}). See the docstring of
+Kconfig.sync_deps() in Kconfiglib for more information.
+""".format(DEFAULT_SYNC_DEPS_PATH))
+
+    parser.add_argument(
+        "--file-list",
+        metavar="OUTPUT_FILE",
+        help="""
+Write a list of all Kconfig files to OUTPUT_FILE, with one file per line. The
+paths are relative to $srctree (or to the current directory if $srctree is
+unset). Files appear in the order they're 'source'd.
+""")
+
+    parser.add_argument(
+        "--env-list",
+        metavar="OUTPUT_FILE",
+        help="""
+Write a list of all environment variables referenced in Kconfig files to
+OUTPUT_FILE, with one variable per line. Each line has the format NAME=VALUE.
+Only environment variables referenced with the preprocessor $(VAR) syntax are
+included, and not variables referenced with the older $VAR syntax (which is
+only supported for backwards compatibility).
+""")
+
+    parser.add_argument(
+        "kconfig",
+        metavar="KCONFIG",
+        nargs="?",
+        default="Kconfig",
+        help="Top-level Kconfig file (default: Kconfig)")
+
+    args = parser.parse_args()
+
+
+    kconf = kconfiglib.Kconfig(args.kconfig, suppress_traceback=True)
+    kconf.load_config()
+
+    if args.header_path is None:
+        if "KCONFIG_AUTOHEADER" in os.environ:
+            kconf.write_autoconf()
+        else:
+            # Kconfiglib defaults to include/generated/autoconf.h to be
+            # compatible with the C tools. 'config.h' is used here instead for
+            # backwards compatibility. It's probably a saner default for tools
+            # as well.
+            kconf.write_autoconf("config.h")
+    else:
+        kconf.write_autoconf(args.header_path)
+
+    if args.config_out is not None:
+        kconf.write_config(args.config_out, save_old=False)
+
+    if args.sync_deps is not None:
+        kconf.sync_deps(args.sync_deps)
+
+    if args.file_list is not None:
+        with _open_write(args.file_list) as f:
+            for path in kconf.kconfig_filenames:
+                f.write(path + "\n")
+
+    if args.env_list is not None:
+        with _open_write(args.env_list) as f:
+            for env_var in kconf.env_vars:
+                f.write("{}={}\n".format(env_var, os.environ[env_var]))
+
+
+def _open_write(path):
+    # Python 2/3 compatibility. io.open() is available on both, but makes
+    # write() expect 'unicode' strings on Python 2.
+
+    if sys.version_info[0] < 3:
+        return open(path, "w")
+    return open(path, "w", encoding="utf-8")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/Kconfiglib/kconfiglib.py b/scripts/Kconfiglib/kconfiglib.py
new file mode 100644
index 0000000..c67895c
--- /dev/null
+++ b/scripts/Kconfiglib/kconfiglib.py
@@ -0,0 +1,7160 @@
+# Copyright (c) 2011-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Overview
+========
+
+Kconfiglib is a Python 2/3 library for scripting and extracting information
+from Kconfig (https://www.kernel.org/doc/Documentation/kbuild/kconfig-language.txt)
+configuration systems.
+
+See the homepage at https://github.com/ulfalizer/Kconfiglib for a longer
+overview.
+
+Since Kconfiglib 12.0.0, the library version is available in
+kconfiglib.VERSION, which is a (<major>, <minor>, <patch>) tuple, e.g.
+(12, 0, 0).
+
+
+Using Kconfiglib on the Linux kernel with the Makefile targets
+==============================================================
+
+For the Linux kernel, a handy interface is provided by the
+scripts/kconfig/Makefile patch, which can be applied with either 'git am' or
+the 'patch' utility:
+
+  $ wget -qO- https://raw.githubusercontent.com/ulfalizer/Kconfiglib/master/makefile.patch | git am
+  $ wget -qO- https://raw.githubusercontent.com/ulfalizer/Kconfiglib/master/makefile.patch | patch -p1
+
+Warning: Not passing -p1 to patch will cause the wrong file to be patched.
+
+Please tell me if the patch does not apply. It should be trivial to apply
+manually, as it's just a block of text that needs to be inserted near the other
+*conf: targets in scripts/kconfig/Makefile.
+
+Look further down for a motivation for the Makefile patch and for instructions
+on how you can use Kconfiglib without it.
+
+If you do not wish to install Kconfiglib via pip, the Makefile patch is set up
+so that you can also just clone Kconfiglib into the kernel root:
+
+  $ git clone git://github.com/ulfalizer/Kconfiglib.git
+  $ git am Kconfiglib/makefile.patch  (or 'patch -p1 < Kconfiglib/makefile.patch')
+
+Warning: The directory name Kconfiglib/ is significant in this case, because
+it's added to PYTHONPATH by the new targets in makefile.patch.
+
+The targets added by the Makefile patch are described in the following
+sections.
+
+
+make kmenuconfig
+----------------
+
+This target runs the curses menuconfig interface with Python 3. As of
+Kconfiglib 12.2.0, both Python 2 and Python 3 are supported (previously, only
+Python 3 was supported, so this was a backport).
+
+
+make guiconfig
+--------------
+
+This target runs the Tkinter menuconfig interface. Both Python 2 and Python 3
+are supported. To change the Python interpreter used, pass
+PYTHONCMD=<executable> to 'make'. The default is 'python'.
+
+
+make [ARCH=<arch>] iscriptconfig
+--------------------------------
+
+This target gives an interactive Python prompt where a Kconfig instance has
+been preloaded and is available in 'kconf'. To change the Python interpreter
+used, pass PYTHONCMD=<executable> to 'make'. The default is 'python'.
+
+To get a feel for the API, try evaluating and printing the symbols in
+kconf.defined_syms, and explore the MenuNode menu tree starting at
+kconf.top_node by following 'next' and 'list' pointers.
+
+The item contained in a menu node is found in MenuNode.item (note that this can
+be one of the constants kconfiglib.MENU and kconfiglib.COMMENT), and all
+symbols and choices have a 'nodes' attribute containing their menu nodes
+(usually only one). Printing a menu node will print its item, in Kconfig
+format.
+
+If you want to look up a symbol by name, use the kconf.syms dictionary.
+
+
+make scriptconfig SCRIPT=<script> [SCRIPT_ARG=<arg>]
+----------------------------------------------------
+
+This target runs the Python script given by the SCRIPT parameter on the
+configuration. sys.argv[1] holds the name of the top-level Kconfig file
+(currently always "Kconfig" in practice), and sys.argv[2] holds the SCRIPT_ARG
+argument, if given.
+
+See the examples/ subdirectory for example scripts.
+
+
+make dumpvarsconfig
+-------------------
+
+This target prints a list of all environment variables referenced from the
+Kconfig files, together with their values. See the
+Kconfiglib/examples/dumpvars.py script.
+
+Only environment variables that are referenced via the Kconfig preprocessor
+$(FOO) syntax are included. The preprocessor was added in Linux 4.18.
+
+
+Using Kconfiglib without the Makefile targets
+=============================================
+
+The make targets are only needed to pick up environment variables exported from
+the Kbuild makefiles and referenced inside Kconfig files, via e.g.
+'source "arch/$(SRCARCH)/Kconfig" and commands run via '$(shell,...)'.
+
+These variables are referenced as of writing (Linux 4.18), together with sample
+values:
+
+  srctree          (.)
+  ARCH             (x86)
+  SRCARCH          (x86)
+  KERNELVERSION    (4.18.0)
+  CC               (gcc)
+  HOSTCC           (gcc)
+  HOSTCXX          (g++)
+  CC_VERSION_TEXT  (gcc (Ubuntu 7.3.0-16ubuntu3) 7.3.0)
+
+Older kernels only reference ARCH, SRCARCH, and KERNELVERSION.
+
+If your kernel is recent enough (4.18+), you can get a list of referenced
+environment variables via 'make dumpvarsconfig' (see above). Note that this
+command is added by the Makefile patch.
+
+To run Kconfiglib without the Makefile patch, set the environment variables
+manually:
+
+  $ srctree=. ARCH=x86 SRCARCH=x86 KERNELVERSION=`make kernelversion` ... python(3)
+  >>> import kconfiglib
+  >>> kconf = kconfiglib.Kconfig()  # filename defaults to "Kconfig"
+
+Search the top-level Makefile for "Additional ARCH settings" to see other
+possibilities for ARCH and SRCARCH.
+
+
+Intro to symbol values
+======================
+
+Kconfiglib has the same assignment semantics as the C implementation.
+
+Any symbol can be assigned a value by the user (via Kconfig.load_config() or
+Symbol.set_value()), but this user value is only respected if the symbol is
+visible, which corresponds to it (currently) being visible in the menuconfig
+interface.
+
+For symbols with prompts, the visibility of the symbol is determined by the
+condition on the prompt. Symbols without prompts are never visible, so setting
+a user value on them is pointless. A warning will be printed by default if
+Symbol.set_value() is called on a promptless symbol. Assignments to promptless
+symbols are normal within a .config file, so no similar warning will be printed
+by load_config().
+
+Dependencies from parents and 'if'/'depends on' are propagated to properties,
+including prompts, so these two configurations are logically equivalent:
+
+(1)
+
+  menu "menu"
+      depends on A
+
+  if B
+
+  config FOO
+      tristate "foo" if D
+      default y
+      depends on C
+
+  endif
+
+  endmenu
+
+(2)
+
+  menu "menu"
+      depends on A
+
+  config FOO
+      tristate "foo" if A && B && C && D
+      default y if A && B && C
+
+  endmenu
+
+In this example, A && B && C && D (the prompt condition) needs to be non-n for
+FOO to be visible (assignable). If its value is m, the symbol can only be
+assigned the value m: The visibility sets an upper bound on the value that can
+be assigned by the user, and any higher user value will be truncated down.
+
+'default' properties are independent of the visibility, though a 'default' will
+often get the same condition as the prompt due to dependency propagation.
+'default' properties are used if the symbol is not visible or has no user
+value.
+
+Symbols with no user value (or that have a user value but are not visible) and
+no (active) 'default' default to n for bool/tristate symbols, and to the empty
+string for other symbol types.
+
+'select' works similarly to symbol visibility, but sets a lower bound on the
+value of the symbol. The lower bound is determined by the value of the
+select*ing* symbol. 'select' does not respect visibility, so non-visible
+symbols can be forced to a particular (minimum) value by a select as well.
+
+For non-bool/tristate symbols, it only matters whether the visibility is n or
+non-n: m visibility acts the same as y visibility.
+
+Conditions on 'default' and 'select' work in mostly intuitive ways. If the
+condition is n, the 'default' or 'select' is disabled. If it is m, the
+'default' or 'select' value (the value of the selecting symbol) is truncated
+down to m.
+
+When writing a configuration with Kconfig.write_config(), only symbols that are
+visible, have an (active) default, or are selected will get written out (note
+that this includes all symbols that would accept user values). Kconfiglib
+matches the .config format produced by the C implementations down to the
+character. This eases testing.
+
+For a visible bool/tristate symbol FOO with value n, this line is written to
+.config:
+
+    # CONFIG_FOO is not set
+
+The point is to remember the user n selection (which might differ from the
+default value the symbol would get), while at the same sticking to the rule
+that undefined corresponds to n (.config uses Makefile format, making the line
+above a comment). When the .config file is read back in, this line will be
+treated the same as the following assignment:
+
+    CONFIG_FOO=n
+
+In Kconfiglib, the set of (currently) assignable values for a bool/tristate
+symbol appear in Symbol.assignable. For other symbol types, just check if
+sym.visibility is non-0 (non-n) to see whether the user value will have an
+effect.
+
+
+Intro to the menu tree
+======================
+
+The menu structure, as seen in e.g. menuconfig, is represented by a tree of
+MenuNode objects. The top node of the configuration corresponds to an implicit
+top-level menu, the title of which is shown at the top in the standard
+menuconfig interface. (The title is also available in Kconfig.mainmenu_text in
+Kconfiglib.)
+
+The top node is found in Kconfig.top_node. From there, you can visit child menu
+nodes by following the 'list' pointer, and any following menu nodes by
+following the 'next' pointer. Usually, a non-None 'list' pointer indicates a
+menu or Choice, but menu nodes for symbols can sometimes have a non-None 'list'
+pointer too due to submenus created implicitly from dependencies.
+
+MenuNode.item is either a Symbol or a Choice object, or one of the constants
+MENU and COMMENT. The prompt of the menu node can be found in MenuNode.prompt,
+which also holds the title for menus and comments. For Symbol and Choice,
+MenuNode.help holds the help text (if any, otherwise None).
+
+Most symbols will only have a single menu node. A symbol defined in multiple
+locations will have one menu node for each location. The list of menu nodes for
+a Symbol or Choice can be found in the Symbol/Choice.nodes attribute.
+
+Note that prompts and help texts for symbols and choices are stored in their
+menu node(s) rather than in the Symbol or Choice objects themselves. This makes
+it possible to define a symbol in multiple locations with a different prompt or
+help text in each location. To get the help text or prompt for a symbol with a
+single menu node, do sym.nodes[0].help and sym.nodes[0].prompt, respectively.
+The prompt is a (text, condition) tuple, where condition determines the
+visibility (see 'Intro to expressions' below).
+
+This organization mirrors the C implementation. MenuNode is called
+'struct menu' there, but I thought "menu" was a confusing name.
+
+It is possible to give a Choice a name and define it in multiple locations,
+hence why Choice.nodes is also a list.
+
+As a convenience, the properties added at a particular definition location are
+available on the MenuNode itself, in e.g. MenuNode.defaults. This is helpful
+when generating documentation, so that symbols/choices defined in multiple
+locations can be shown with the correct properties at each location.
+
+
+Intro to expressions
+====================
+
+Expressions can be evaluated with the expr_value() function and printed with
+the expr_str() function (these are used internally as well). Evaluating an
+expression always yields a tristate value, where n, m, and y are represented as
+0, 1, and 2, respectively.
+
+The following table should help you figure out how expressions are represented.
+A, B, C, ... are symbols (Symbol instances), NOT is the kconfiglib.NOT
+constant, etc.
+
+Expression            Representation
+----------            --------------
+A                     A
+"A"                   A (constant symbol)
+!A                    (NOT, A)
+A && B                (AND, A, B)
+A && B && C           (AND, A, (AND, B, C))
+A || B                (OR, A, B)
+A || (B && C && D)    (OR, A, (AND, B, (AND, C, D)))
+A = B                 (EQUAL, A, B)
+A != "foo"            (UNEQUAL, A, foo (constant symbol))
+A && B = C && D       (AND, A, (AND, (EQUAL, B, C), D))
+n                     Kconfig.n (constant symbol)
+m                     Kconfig.m (constant symbol)
+y                     Kconfig.y (constant symbol)
+"y"                   Kconfig.y (constant symbol)
+
+Strings like "foo" in 'default "foo"' or 'depends on SYM = "foo"' are
+represented as constant symbols, so the only values that appear in expressions
+are symbols***. This mirrors the C implementation.
+
+***For choice symbols, the parent Choice will appear in expressions as well,
+but it's usually invisible as the value interfaces of Symbol and Choice are
+identical. This mirrors the C implementation and makes different choice modes
+"just work".
+
+Manual evaluation examples:
+
+  - The value of A && B is min(A.tri_value, B.tri_value)
+
+  - The value of A || B is max(A.tri_value, B.tri_value)
+
+  - The value of !A is 2 - A.tri_value
+
+  - The value of A = B is 2 (y) if A.str_value == B.str_value, and 0 (n)
+    otherwise. Note that str_value is used here instead of tri_value.
+
+    For constant (as well as undefined) symbols, str_value matches the name of
+    the symbol. This mirrors the C implementation and explains why
+    'depends on SYM = "foo"' above works as expected.
+
+n/m/y are automatically converted to the corresponding constant symbols
+"n"/"m"/"y" (Kconfig.n/m/y) during parsing.
+
+Kconfig.const_syms is a dictionary like Kconfig.syms but for constant symbols.
+
+If a condition is missing (e.g., <cond> when the 'if <cond>' is removed from
+'default A if <cond>'), it is actually Kconfig.y. The standard __str__()
+functions just avoid printing 'if y' conditions to give cleaner output.
+
+
+Kconfig extensions
+==================
+
+Kconfiglib includes a couple of Kconfig extensions:
+
+'source' with relative path
+---------------------------
+
+The 'rsource' statement sources Kconfig files with a path relative to directory
+of the Kconfig file containing the 'rsource' statement, instead of relative to
+the project root.
+
+Consider following directory tree:
+
+  Project
+  +--Kconfig
+  |
+  +--src
+     +--Kconfig
+     |
+     +--SubSystem1
+        +--Kconfig
+        |
+        +--ModuleA
+           +--Kconfig
+
+In this example, assume that src/SubSystem1/Kconfig wants to source
+src/SubSystem1/ModuleA/Kconfig.
+
+With 'source', this statement would be used:
+
+  source "src/SubSystem1/ModuleA/Kconfig"
+
+With 'rsource', this turns into
+
+  rsource "ModuleA/Kconfig"
+
+If an absolute path is given to 'rsource', it acts the same as 'source'.
+
+'rsource' can be used to create "position-independent" Kconfig trees that can
+be moved around freely.
+
+
+Globbing 'source'
+-----------------
+
+'source' and 'rsource' accept glob patterns, sourcing all matching Kconfig
+files. They require at least one matching file, raising a KconfigError
+otherwise.
+
+For example, the following statement might source sub1/foofoofoo and
+sub2/foobarfoo:
+
+  source "sub[12]/foo*foo"
+
+The glob patterns accepted are the same as for the standard glob.glob()
+function.
+
+Two additional statements are provided for cases where it's acceptable for a
+pattern to match no files: 'osource' and 'orsource' (the o is for "optional").
+
+For example, the following statements will be no-ops if neither "foo" nor any
+files matching "bar*" exist:
+
+  osource "foo"
+  osource "bar*"
+
+'orsource' does a relative optional source.
+
+'source' and 'osource' are analogous to 'include' and '-include' in Make.
+
+
+Generalized def_* keywords
+--------------------------
+
+def_int, def_hex, and def_string are available in addition to def_bool and
+def_tristate, allowing int, hex, and string symbols to be given a type and a
+default at the same time.
+
+
+Extra optional warnings
+-----------------------
+
+Some optional warnings can be controlled via environment variables:
+
+  - KCONFIG_WARN_UNDEF: If set to 'y', warnings will be generated for all
+    references to undefined symbols within Kconfig files. The only gotcha is
+    that all hex literals must be prefixed with "0x" or "0X", to make it
+    possible to distinguish them from symbol references.
+
+    Some projects (e.g. the Linux kernel) use multiple Kconfig trees with many
+    shared Kconfig files, leading to some safe undefined symbol references.
+    KCONFIG_WARN_UNDEF is useful in projects that only have a single Kconfig
+    tree though.
+
+    KCONFIG_STRICT is an older alias for this environment variable, supported
+    for backwards compatibility.
+
+  - KCONFIG_WARN_UNDEF_ASSIGN: If set to 'y', warnings will be generated for
+    all assignments to undefined symbols within .config files. By default, no
+    such warnings are generated.
+
+    This warning can also be enabled/disabled via the Kconfig.warn_assign_undef
+    variable.
+
+
+Preprocessor user functions defined in Python
+---------------------------------------------
+
+Preprocessor functions can be defined in Python, which makes it simple to
+integrate information from existing Python tools into Kconfig (e.g. to have
+Kconfig symbols depend on hardware information stored in some other format).
+
+Putting a Python module named kconfigfunctions(.py) anywhere in sys.path will
+cause it to be imported by Kconfiglib (in Kconfig.__init__()). Note that
+sys.path can be customized via PYTHONPATH, and includes the directory of the
+module being run by default, as well as installation directories.
+
+If the KCONFIG_FUNCTIONS environment variable is set, it gives a different
+module name to use instead of 'kconfigfunctions'.
+
+The imported module is expected to define a global dictionary named 'functions'
+that maps function names to Python functions, as follows:
+
+  def my_fn(kconf, name, arg_1, arg_2, ...):
+      # kconf:
+      #   Kconfig instance
+      #
+      # name:
+      #   Name of the user-defined function ("my-fn"). Think argv[0].
+      #
+      # arg_1, arg_2, ...:
+      #   Arguments passed to the function from Kconfig (strings)
+      #
+      # Returns a string to be substituted as the result of calling the
+      # function
+      ...
+
+  def my_other_fn(kconf, name, arg_1, arg_2, ...):
+      ...
+
+  functions = {
+      "my-fn":       (my_fn,       <min.args>, <max.args>/None),
+      "my-other-fn": (my_other_fn, <min.args>, <max.args>/None),
+      ...
+  }
+
+  ...
+
+<min.args> and <max.args> are the minimum and maximum number of arguments
+expected by the function (excluding the implicit 'name' argument). If
+<max.args> is None, there is no upper limit to the number of arguments. Passing
+an invalid number of arguments will generate a KconfigError exception.
+
+Functions can access the current parsing location as kconf.filename/linenr.
+Accessing other fields of the Kconfig object is not safe. See the warning
+below.
+
+Keep in mind that for a variable defined like 'foo = $(fn)', 'fn' will be
+called only when 'foo' is expanded. If 'fn' uses the parsing location and the
+intent is to use the location of the assignment, you want 'foo := $(fn)'
+instead, which calls the function immediately.
+
+Once defined, user functions can be called from Kconfig in the same way as
+other preprocessor functions:
+
+    config FOO
+        ...
+        depends on $(my-fn,arg1,arg2)
+
+If my_fn() returns "n", this will result in
+
+    config FOO
+        ...
+        depends on n
+
+Warning
+*******
+
+User-defined preprocessor functions are called as they're encountered at parse
+time, before all Kconfig files have been processed, and before the menu tree
+has been finalized. There are no guarantees that accessing Kconfig symbols or
+the menu tree via the 'kconf' parameter will work, and it could potentially
+lead to a crash.
+
+Preferably, user-defined functions should be stateless.
+
+
+Feedback
+========
+
+Send bug reports, suggestions, and questions to ulfalizer a.t Google's email
+service, or open a ticket on the GitHub page.
+"""
+import errno
+import importlib
+import os
+import re
+import sys
+
+# Get rid of some attribute lookups. These are obvious in context.
+from glob import iglob
+from os.path import dirname, exists, expandvars, islink, join, realpath
+
+
+VERSION = (14, 1, 0)
+
+
+# File layout:
+#
+# Public classes
+# Public functions
+# Internal functions
+# Global constants
+
+# Line length: 79 columns
+
+
+#
+# Public classes
+#
+
+
+class Kconfig(object):
+    """
+    Represents a Kconfig configuration, e.g. for x86 or ARM. This is the set of
+    symbols, choices, and menu nodes appearing in the configuration. Creating
+    any number of Kconfig objects (including for different architectures) is
+    safe. Kconfiglib doesn't keep any global state.
+
+    The following attributes are available. They should be treated as
+    read-only, and some are implemented through @property magic.
+
+    syms:
+      A dictionary with all symbols in the configuration, indexed by name. Also
+      includes all symbols that are referenced in expressions but never
+      defined, except for constant (quoted) symbols.
+
+      Undefined symbols can be recognized by Symbol.nodes being empty -- see
+      the 'Intro to the menu tree' section in the module docstring.
+
+    const_syms:
+      A dictionary like 'syms' for constant (quoted) symbols
+
+    named_choices:
+      A dictionary like 'syms' for named choices (choice FOO)
+
+    defined_syms:
+      A list with all defined symbols, in the same order as they appear in the
+      Kconfig files. Symbols defined in multiple locations appear multiple
+      times.
+
+      Note: You probably want to use 'unique_defined_syms' instead. This
+      attribute is mostly maintained for backwards compatibility.
+
+    unique_defined_syms:
+      A list like 'defined_syms', but with duplicates removed. Just the first
+      instance is kept for symbols defined in multiple locations. Kconfig order
+      is preserved otherwise.
+
+      Using this attribute instead of 'defined_syms' can save work, and
+      automatically gives reasonable behavior when writing configuration output
+      (symbols defined in multiple locations only generate output once, while
+      still preserving Kconfig order for readability).
+
+    choices:
+      A list with all choices, in the same order as they appear in the Kconfig
+      files.
+
+      Note: You probably want to use 'unique_choices' instead. This attribute
+      is mostly maintained for backwards compatibility.
+
+    unique_choices:
+      Analogous to 'unique_defined_syms', for choices. Named choices can have
+      multiple definition locations.
+
+    menus:
+      A list with all menus, in the same order as they appear in the Kconfig
+      files
+
+    comments:
+      A list with all comments, in the same order as they appear in the Kconfig
+      files
+
+    kconfig_filenames:
+      A list with the filenames of all Kconfig files included in the
+      configuration, relative to $srctree (or relative to the current directory
+      if $srctree isn't set), except absolute paths (e.g.
+      'source "/foo/Kconfig"') are kept as-is.
+
+      The files are listed in the order they are source'd, starting with the
+      top-level Kconfig file. If a file is source'd multiple times, it will
+      appear multiple times. Use set() to get unique filenames.
+
+      Note that Kconfig.sync_deps() already indirectly catches any file
+      modifications that change configuration output.
+
+    env_vars:
+      A set() with the names of all environment variables referenced in the
+      Kconfig files.
+
+      Only environment variables referenced with the preprocessor $(FOO) syntax
+      will be registered. The older $FOO syntax is only supported for backwards
+      compatibility.
+
+      Also note that $(FOO) won't be registered unless the environment variable
+      $FOO is actually set. If it isn't, $(FOO) is an expansion of an unset
+      preprocessor variable (which gives the empty string).
+
+      Another gotcha is that environment variables referenced in the values of
+      recursively expanded preprocessor variables (those defined with =) will
+      only be registered if the variable is actually used (expanded) somewhere.
+
+      The note from the 'kconfig_filenames' documentation applies here too.
+
+    n/m/y:
+      The predefined constant symbols n/m/y. Also available in const_syms.
+
+    modules:
+      The Symbol instance for the modules symbol. Currently hardcoded to
+      MODULES, which is backwards compatible. Kconfiglib will warn if
+      'option modules' is set on some other symbol. Tell me if you need proper
+      'option modules' support.
+
+      'modules' is never None. If the MODULES symbol is not explicitly defined,
+      its tri_value will be 0 (n), as expected.
+
+      A simple way to enable modules is to do 'kconf.modules.set_value(2)'
+      (provided the MODULES symbol is defined and visible). Modules are
+      disabled by default in the kernel Kconfig files as of writing, though
+      nearly all defconfig files enable them (with 'CONFIG_MODULES=y').
+
+    defconfig_list:
+      The Symbol instance for the 'option defconfig_list' symbol, or None if no
+      defconfig_list symbol exists. The defconfig filename derived from this
+      symbol can be found in Kconfig.defconfig_filename.
+
+    defconfig_filename:
+      The filename given by the defconfig_list symbol. This is taken from the
+      first 'default' with a satisfied condition where the specified file
+      exists (can be opened for reading). If a defconfig file foo/defconfig is
+      not found and $srctree was set when the Kconfig was created,
+      $srctree/foo/defconfig is looked up as well.
+
+      'defconfig_filename' is None if either no defconfig_list symbol exists,
+      or if the defconfig_list symbol has no 'default' with a satisfied
+      condition that specifies a file that exists.
+
+      Gotcha: scripts/kconfig/Makefile might pass --defconfig=<defconfig> to
+      scripts/kconfig/conf when running e.g. 'make defconfig'. This option
+      overrides the defconfig_list symbol, meaning defconfig_filename might not
+      always match what 'make defconfig' would use.
+
+    top_node:
+      The menu node (see the MenuNode class) of the implicit top-level menu.
+      Acts as the root of the menu tree.
+
+    mainmenu_text:
+      The prompt (title) of the top menu (top_node). Defaults to "Main menu".
+      Can be changed with the 'mainmenu' statement (see kconfig-language.txt).
+
+    variables:
+      A dictionary with all preprocessor variables, indexed by name. See the
+      Variable class.
+
+    warn:
+      Set this variable to True/False to enable/disable warnings. See
+      Kconfig.__init__().
+
+      When 'warn' is False, the values of the other warning-related variables
+      are ignored.
+
+      This variable as well as the other warn* variables can be read to check
+      the current warning settings.
+
+    warn_to_stderr:
+      Set this variable to True/False to enable/disable warnings on stderr. See
+      Kconfig.__init__().
+
+    warn_assign_undef:
+      Set this variable to True to generate warnings for assignments to
+      undefined symbols in configuration files.
+
+      This variable is False by default unless the KCONFIG_WARN_UNDEF_ASSIGN
+      environment variable was set to 'y' when the Kconfig instance was
+      created.
+
+    warn_assign_override:
+      Set this variable to True to generate warnings for multiple assignments
+      to the same symbol in configuration files, where the assignments set
+      different values (e.g. CONFIG_FOO=m followed by CONFIG_FOO=y, where the
+      last value would get used).
+
+      This variable is True by default. Disabling it might be useful when
+      merging configurations.
+
+    warn_assign_redun:
+      Like warn_assign_override, but for multiple assignments setting a symbol
+      to the same value.
+
+      This variable is True by default. Disabling it might be useful when
+      merging configurations.
+
+    warnings:
+      A list of strings containing all warnings that have been generated, for
+      cases where more flexibility is needed.
+
+      See the 'warn_to_stderr' parameter to Kconfig.__init__() and the
+      Kconfig.warn_to_stderr variable as well. Note that warnings still get
+      added to Kconfig.warnings when 'warn_to_stderr' is True.
+
+      Just as for warnings printed to stderr, only warnings that are enabled
+      will get added to Kconfig.warnings. See the various Kconfig.warn*
+      variables.
+
+    missing_syms:
+      A list with (name, value) tuples for all assignments to undefined symbols
+      within the most recently loaded .config file(s). 'name' is the symbol
+      name without the 'CONFIG_' prefix. 'value' is a string that gives the
+      right-hand side of the assignment verbatim.
+
+      See Kconfig.load_config() as well.
+
+    srctree:
+      The value the $srctree environment variable had when the Kconfig instance
+      was created, or the empty string if $srctree wasn't set. This gives nice
+      behavior with os.path.join(), which treats "" as the current directory,
+      without adding "./".
+
+      Kconfig files are looked up relative to $srctree (unless absolute paths
+      are used), and .config files are looked up relative to $srctree if they
+      are not found in the current directory. This is used to support
+      out-of-tree builds. The C tools use this environment variable in the same
+      way.
+
+      Changing $srctree after creating the Kconfig instance has no effect. Only
+      the value when the configuration is loaded matters. This avoids surprises
+      if multiple configurations are loaded with different values for $srctree.
+
+    config_prefix:
+      The value the CONFIG_ environment variable had when the Kconfig instance
+      was created, or "CONFIG_" if CONFIG_ wasn't set. This is the prefix used
+      (and expected) on symbol names in .config files and C headers. Used in
+      the same way in the C tools.
+
+    config_header:
+      The value the KCONFIG_CONFIG_HEADER environment variable had when the
+      Kconfig instance was created, or the empty string if
+      KCONFIG_CONFIG_HEADER wasn't set. This string is inserted verbatim at the
+      beginning of configuration files. See write_config().
+
+    header_header:
+      The value the KCONFIG_AUTOHEADER_HEADER environment variable had when the
+      Kconfig instance was created, or the empty string if
+      KCONFIG_AUTOHEADER_HEADER wasn't set. This string is inserted verbatim at
+      the beginning of header files. See write_autoconf().
+
+    filename/linenr:
+      The current parsing location, for use in Python preprocessor functions.
+      See the module docstring.
+    """
+    __slots__ = (
+        "_encoding",
+        "_functions",
+        "_set_match",
+        "_srctree_prefix",
+        "_unset_match",
+        "_warn_assign_no_prompt",
+        "choices",
+        "comments",
+        "config_header",
+        "config_prefix",
+        "const_syms",
+        "defconfig_list",
+        "defined_syms",
+        "env_vars",
+        "header_header",
+        "kconfig_filenames",
+        "m",
+        "menus",
+        "missing_syms",
+        "modules",
+        "n",
+        "named_choices",
+        "srctree",
+        "syms",
+        "top_node",
+        "unique_choices",
+        "unique_defined_syms",
+        "variables",
+        "warn",
+        "warn_assign_override",
+        "warn_assign_redun",
+        "warn_assign_undef",
+        "warn_to_stderr",
+        "warnings",
+        "y",
+
+        # Parsing-related
+        "_parsing_kconfigs",
+        "_readline",
+        "filename",
+        "linenr",
+        "_include_path",
+        "_filestack",
+        "_line",
+        "_tokens",
+        "_tokens_i",
+        "_reuse_tokens",
+    )
+
+    #
+    # Public interface
+    #
+
+    def __init__(self, filename="Kconfig", warn=True, warn_to_stderr=True,
+                 encoding="utf-8", suppress_traceback=False):
+        """
+        Creates a new Kconfig object by parsing Kconfig files.
+        Note that Kconfig files are not the same as .config files (which store
+        configuration symbol values).
+
+        See the module docstring for some environment variables that influence
+        default warning settings (KCONFIG_WARN_UNDEF and
+        KCONFIG_WARN_UNDEF_ASSIGN).
+
+        Raises KconfigError on syntax/semantic errors, and OSError or (possibly
+        a subclass of) IOError on IO errors ('errno', 'strerror', and
+        'filename' are available). Note that IOError is an alias for OSError on
+        Python 3, so it's enough to catch OSError there. If you need Python 2/3
+        compatibility, it's easiest to catch EnvironmentError, which is a
+        common base class of OSError/IOError on Python 2 and an alias for
+        OSError on Python 3.
+
+        filename (default: "Kconfig"):
+          The Kconfig file to load. For the Linux kernel, you'll want "Kconfig"
+          from the top-level directory, as environment variables will make sure
+          the right Kconfig is included from there (arch/$SRCARCH/Kconfig as of
+          writing).
+
+          If $srctree is set, 'filename' will be looked up relative to it.
+          $srctree is also used to look up source'd files within Kconfig files.
+          See the class documentation.
+
+          If you are using Kconfiglib via 'make scriptconfig', the filename of
+          the base base Kconfig file will be in sys.argv[1]. It's currently
+          always "Kconfig" in practice.
+
+        warn (default: True):
+          True if warnings related to this configuration should be generated.
+          This can be changed later by setting Kconfig.warn to True/False. It
+          is provided as a constructor argument since warnings might be
+          generated during parsing.
+
+          See the other Kconfig.warn_* variables as well, which enable or
+          suppress certain warnings when warnings are enabled.
+
+          All generated warnings are added to the Kconfig.warnings list. See
+          the class documentation.
+
+        warn_to_stderr (default: True):
+          True if warnings should be printed to stderr in addition to being
+          added to Kconfig.warnings.
+
+          This can be changed later by setting Kconfig.warn_to_stderr to
+          True/False.
+
+        encoding (default: "utf-8"):
+          The encoding to use when reading and writing files, and when decoding
+          output from commands run via $(shell). If None, the encoding
+          specified in the current locale will be used.
+
+          The "utf-8" default avoids exceptions on systems that are configured
+          to use the C locale, which implies an ASCII encoding.
+
+          This parameter has no effect on Python 2, due to implementation
+          issues (regular strings turning into Unicode strings, which are
+          distinct in Python 2). Python 2 doesn't decode regular strings
+          anyway.
+
+          Related PEP: https://www.python.org/dev/peps/pep-0538/
+
+        suppress_traceback (default: False):
+          Helper for tools. When True, any EnvironmentError or KconfigError
+          generated during parsing is caught, the exception message is printed
+          to stderr together with the command name, and sys.exit(1) is called
+          (which generates SystemExit).
+
+          This hides the Python traceback for "expected" errors like syntax
+          errors in Kconfig files.
+
+          Other exceptions besides EnvironmentError and KconfigError are still
+          propagated when suppress_traceback is True.
+        """
+        try:
+            self._init(filename, warn, warn_to_stderr, encoding)
+        except (EnvironmentError, KconfigError) as e:
+            if suppress_traceback:
+                cmd = sys.argv[0]  # Empty string if missing
+                if cmd:
+                    cmd += ": "
+                # Some long exception messages have extra newlines for better
+                # formatting when reported as an unhandled exception. Strip
+                # them here.
+                sys.exit(cmd + str(e).strip())
+            raise
+
+    def _init(self, filename, warn, warn_to_stderr, encoding):
+        # See __init__()
+
+        self._encoding = encoding
+
+        self.srctree = os.getenv("srctree", "")
+        # A prefix we can reliably strip from glob() results to get a filename
+        # relative to $srctree. relpath() can cause issues for symlinks,
+        # because it assumes symlink/../foo is the same as foo/.
+        self._srctree_prefix = realpath(self.srctree) + os.sep
+
+        self.warn = warn
+        self.warn_to_stderr = warn_to_stderr
+        self.warn_assign_undef = os.getenv("KCONFIG_WARN_UNDEF_ASSIGN") == "y"
+        self.warn_assign_override = True
+        self.warn_assign_redun = True
+        self._warn_assign_no_prompt = True
+
+        self.warnings = []
+
+        self.config_prefix = os.getenv("CONFIG_", "CONFIG_")
+        # Regular expressions for parsing .config files
+        self._set_match = _re_match(self.config_prefix + r"([^=]+)=(.*)")
+        self._unset_match = _re_match(r"# {}([^ ]+) is not set".format(
+            self.config_prefix))
+
+        self.config_header = os.getenv("KCONFIG_CONFIG_HEADER", "")
+        self.header_header = os.getenv("KCONFIG_AUTOHEADER_HEADER", "")
+
+        self.syms = {}
+        self.const_syms = {}
+        self.defined_syms = []
+        self.missing_syms = []
+        self.named_choices = {}
+        self.choices = []
+        self.menus = []
+        self.comments = []
+
+        for nmy in "n", "m", "y":
+            sym = Symbol()
+            sym.kconfig = self
+            sym.name = nmy
+            sym.is_constant = True
+            sym.orig_type = TRISTATE
+            sym._cached_tri_val = STR_TO_TRI[nmy]
+
+            self.const_syms[nmy] = sym
+
+        self.n = self.const_syms["n"]
+        self.m = self.const_syms["m"]
+        self.y = self.const_syms["y"]
+
+        # Make n/m/y well-formed symbols
+        for nmy in "n", "m", "y":
+            sym = self.const_syms[nmy]
+            sym.rev_dep = sym.weak_rev_dep = sym.direct_dep = self.n
+
+        # Maps preprocessor variables names to Variable instances
+        self.variables = {}
+
+        # Predefined preprocessor functions, with min/max number of arguments
+        self._functions = {
+            "info":       (_info_fn,       1, 1),
+            "error-if":   (_error_if_fn,   2, 2),
+            "filename":   (_filename_fn,   0, 0),
+            "lineno":     (_lineno_fn,     0, 0),
+            "shell":      (_shell_fn,      1, 1),
+            "warning-if": (_warning_if_fn, 2, 2),
+        }
+
+        # Add any user-defined preprocessor functions
+        try:
+            self._functions.update(
+                importlib.import_module(
+                    os.getenv("KCONFIG_FUNCTIONS", "kconfigfunctions")
+                ).functions)
+        except ImportError:
+            pass
+
+        # This determines whether previously unseen symbols are registered.
+        # They shouldn't be if we parse expressions after parsing, as part of
+        # Kconfig.eval_string().
+        self._parsing_kconfigs = True
+
+        self.modules = self._lookup_sym("MODULES")
+        self.defconfig_list = None
+
+        self.top_node = MenuNode()
+        self.top_node.kconfig = self
+        self.top_node.item = MENU
+        self.top_node.is_menuconfig = True
+        self.top_node.visibility = self.y
+        self.top_node.prompt = ("Main menu", self.y)
+        self.top_node.parent = None
+        self.top_node.dep = self.y
+        self.top_node.filename = filename
+        self.top_node.linenr = 1
+        self.top_node.include_path = ()
+
+        # Parse the Kconfig files
+
+        # Not used internally. Provided as a convenience.
+        self.kconfig_filenames = [filename]
+        self.env_vars = set()
+
+        # Keeps track of the location in the parent Kconfig files. Kconfig
+        # files usually source other Kconfig files. See _enter_file().
+        self._filestack = []
+        self._include_path = ()
+
+        # The current parsing location
+        self.filename = filename
+        self.linenr = 0
+
+        # Used to avoid retokenizing lines when we discover that they're not
+        # part of the construct currently being parsed. This is kinda like an
+        # unget operation.
+        self._reuse_tokens = False
+
+        # Open the top-level Kconfig file. Store the readline() method directly
+        # as a small optimization.
+        self._readline = self._open(join(self.srctree, filename), "r").readline
+
+        try:
+            # Parse the Kconfig files. Returns the last node, which we
+            # terminate with '.next = None'.
+            self._parse_block(None, self.top_node, self.top_node).next = None
+            self.top_node.list = self.top_node.next
+            self.top_node.next = None
+        except UnicodeDecodeError as e:
+            _decoding_error(e, self.filename)
+
+        # Close the top-level Kconfig file. __self__ fetches the 'file' object
+        # for the method.
+        self._readline.__self__.close()
+
+        self._parsing_kconfigs = False
+
+        # Do various menu tree post-processing
+        self._finalize_node(self.top_node, self.y)
+
+        self.unique_defined_syms = _ordered_unique(self.defined_syms)
+        self.unique_choices = _ordered_unique(self.choices)
+
+        # Do sanity checks. Some of these depend on everything being finalized.
+        self._check_sym_sanity()
+        self._check_choice_sanity()
+
+        # KCONFIG_STRICT is an older alias for KCONFIG_WARN_UNDEF, supported
+        # for backwards compatibility
+        if os.getenv("KCONFIG_WARN_UNDEF") == "y" or \
+           os.getenv("KCONFIG_STRICT") == "y":
+
+            self._check_undef_syms()
+
+        # Build Symbol._dependents for all symbols and choices
+        self._build_dep()
+
+        # Check for dependency loops
+        check_dep_loop_sym = _check_dep_loop_sym  # Micro-optimization
+        for sym in self.unique_defined_syms:
+            check_dep_loop_sym(sym, False)
+
+        # Add extra dependencies from choices to choice symbols that get
+        # awkward during dependency loop detection
+        self._add_choice_deps()
+
+    @property
+    def mainmenu_text(self):
+        """
+        See the class documentation.
+        """
+        return self.top_node.prompt[0]
+
+    @property
+    def defconfig_filename(self):
+        """
+        See the class documentation.
+        """
+        if self.defconfig_list:
+            for filename, cond in self.defconfig_list.defaults:
+                if expr_value(cond):
+                    try:
+                        with self._open_config(filename.str_value) as f:
+                            return f.name
+                    except EnvironmentError:
+                        continue
+
+        return None
+
+    def load_config(self, filename=None, replace=True, verbose=None):
+        """
+        Loads symbol values from a file in the .config format. Equivalent to
+        calling Symbol.set_value() to set each of the values.
+
+        "# CONFIG_FOO is not set" within a .config file sets the user value of
+        FOO to n. The C tools work the same way.
+
+        For each symbol, the Symbol.user_value attribute holds the value the
+        symbol was assigned in the .config file (if any). The user value might
+        differ from Symbol.str/tri_value if there are unsatisfied dependencies.
+
+        Calling this function also updates the Kconfig.missing_syms attribute
+        with a list of all assignments to undefined symbols within the
+        configuration file. Kconfig.missing_syms is cleared if 'replace' is
+        True, and appended to otherwise. See the documentation for
+        Kconfig.missing_syms as well.
+
+        See the Kconfig.__init__() docstring for raised exceptions
+        (OSError/IOError). KconfigError is never raised here.
+
+        filename (default: None):
+          Path to load configuration from (a string). Respects $srctree if set
+          (see the class documentation).
+
+          If 'filename' is None (the default), the configuration file to load
+          (if any) is calculated automatically, giving the behavior you'd
+          usually want:
+
+            1. If the KCONFIG_CONFIG environment variable is set, it gives the
+               path to the configuration file to load. Otherwise, ".config" is
+               used. See standard_config_filename().
+
+            2. If the path from (1.) doesn't exist, the configuration file
+               given by kconf.defconfig_filename is loaded instead, which is
+               derived from the 'option defconfig_list' symbol.
+
+            3. If (1.) and (2.) fail to find a configuration file to load, no
+               configuration file is loaded, and symbols retain their current
+               values (e.g., their default values). This is not an error.
+
+           See the return value as well.
+
+        replace (default: True):
+          If True, all existing user values will be cleared before loading the
+          .config. Pass False to merge configurations.
+
+        verbose (default: None):
+          Limited backwards compatibility to prevent crashes. A warning is
+          printed if anything but None is passed.
+
+          Prior to Kconfiglib 12.0.0, this option enabled printing of messages
+          to stdout when 'filename' was None. A message is (always) returned
+          now instead, which is more flexible.
+
+          Will probably be removed in some future version.
+
+        Returns a string with a message saying which file got loaded (or
+        possibly that no file got loaded, when 'filename' is None). This is
+        meant to reduce boilerplate in tools, which can do e.g.
+        print(kconf.load_config()). The returned message distinguishes between
+        loading (replace == True) and merging (replace == False).
+        """
+        if verbose is not None:
+            _warn_verbose_deprecated("load_config")
+
+        msg = None
+        if filename is None:
+            filename = standard_config_filename()
+            if not exists(filename) and \
+               not exists(join(self.srctree, filename)):
+                defconfig = self.defconfig_filename
+                if defconfig is None:
+                    return "Using default symbol values (no '{}')" \
+                           .format(filename)
+
+                msg = " default configuration '{}' (no '{}')" \
+                      .format(defconfig, filename)
+                filename = defconfig
+
+        if not msg:
+            msg = " configuration '{}'".format(filename)
+
+        # Disable the warning about assigning to symbols without prompts. This
+        # is normal and expected within a .config file.
+        self._warn_assign_no_prompt = False
+
+        # This stub only exists to make sure _warn_assign_no_prompt gets
+        # reenabled
+        try:
+            self._load_config(filename, replace)
+        except UnicodeDecodeError as e:
+            _decoding_error(e, filename)
+        finally:
+            self._warn_assign_no_prompt = True
+
+        return ("Loaded" if replace else "Merged") + msg
+
+    def _load_config(self, filename, replace):
+        with self._open_config(filename) as f:
+            if replace:
+                self.missing_syms = []
+
+                # If we're replacing the configuration, keep track of which
+                # symbols and choices got set so that we can unset the rest
+                # later. This avoids invalidating everything and is faster.
+                # Another benefit is that invalidation must be rock solid for
+                # it to work, making it a good test.
+
+                for sym in self.unique_defined_syms:
+                    sym._was_set = False
+
+                for choice in self.unique_choices:
+                    choice._was_set = False
+
+            # Small optimizations
+            set_match = self._set_match
+            unset_match = self._unset_match
+            get_sym = self.syms.get
+
+            for linenr, line in enumerate(f, 1):
+                # The C tools ignore trailing whitespace
+                line = line.rstrip()
+
+                match = set_match(line)
+                if match:
+                    name, val = match.groups()
+                    sym = get_sym(name)
+                    if not sym or not sym.nodes:
+                        self._undef_assign(name, val, filename, linenr)
+                        continue
+
+                    if sym.orig_type in _BOOL_TRISTATE:
+                        # The C implementation only checks the first character
+                        # to the right of '=', for whatever reason
+                        if not (sym.orig_type is BOOL
+                                and val.startswith(("y", "n")) or
+                                sym.orig_type is TRISTATE
+                                and val.startswith(("y", "m", "n"))):
+                            self._warn("'{}' is not a valid value for the {} "
+                                       "symbol {}. Assignment ignored."
+                                       .format(val, TYPE_TO_STR[sym.orig_type],
+                                               sym.name_and_loc),
+                                       filename, linenr)
+                            continue
+
+                        val = val[0]
+
+                        if sym.choice and val != "n":
+                            # During .config loading, we infer the mode of the
+                            # choice from the kind of values that are assigned
+                            # to the choice symbols
+
+                            prev_mode = sym.choice.user_value
+                            if prev_mode is not None and \
+                               TRI_TO_STR[prev_mode] != val:
+
+                                self._warn("both m and y assigned to symbols "
+                                           "within the same choice",
+                                           filename, linenr)
+
+                            # Set the choice's mode
+                            sym.choice.set_value(val)
+
+                    elif sym.orig_type is STRING:
+                        match = _conf_string_match(val)
+                        if not match:
+                            self._warn("malformed string literal in "
+                                       "assignment to {}. Assignment ignored."
+                                       .format(sym.name_and_loc),
+                                       filename, linenr)
+                            continue
+
+                        val = unescape(match.group(1))
+
+                else:
+                    match = unset_match(line)
+                    if not match:
+                        # Print a warning for lines that match neither
+                        # set_match() nor unset_match() and that are not blank
+                        # lines or comments. 'line' has already been
+                        # rstrip()'d, so blank lines show up as "" here.
+                        if line and not line.lstrip().startswith("#"):
+                            self._warn("ignoring malformed line '{}'"
+                                       .format(line),
+                                       filename, linenr)
+
+                        continue
+
+                    name = match.group(1)
+                    sym = get_sym(name)
+                    if not sym or not sym.nodes:
+                        self._undef_assign(name, "n", filename, linenr)
+                        continue
+
+                    if sym.orig_type not in _BOOL_TRISTATE:
+                        continue
+
+                    val = "n"
+
+                # Done parsing the assignment. Set the value.
+
+                if sym._was_set:
+                    self._assigned_twice(sym, val, filename, linenr)
+
+                sym.set_value(val)
+
+        if replace:
+            # If we're replacing the configuration, unset the symbols that
+            # didn't get set
+
+            for sym in self.unique_defined_syms:
+                if not sym._was_set:
+                    sym.unset_value()
+
+            for choice in self.unique_choices:
+                if not choice._was_set:
+                    choice.unset_value()
+
+    def _undef_assign(self, name, val, filename, linenr):
+        # Called for assignments to undefined symbols during .config loading
+
+        self.missing_syms.append((name, val))
+        if self.warn_assign_undef:
+            self._warn(
+                "attempt to assign the value '{}' to the undefined symbol {}"
+                .format(val, name), filename, linenr)
+
+    def _assigned_twice(self, sym, new_val, filename, linenr):
+        # Called when a symbol is assigned more than once in a .config file
+
+        # Use strings for bool/tristate user values in the warning
+        if sym.orig_type in _BOOL_TRISTATE:
+            user_val = TRI_TO_STR[sym.user_value]
+        else:
+            user_val = sym.user_value
+
+        msg = '{} set more than once. Old value "{}", new value "{}".'.format(
+            sym.name_and_loc, user_val, new_val)
+
+        if user_val == new_val:
+            if self.warn_assign_redun:
+                self._warn(msg, filename, linenr)
+        elif self.warn_assign_override:
+            self._warn(msg, filename, linenr)
+
+    def load_allconfig(self, filename):
+        """
+        Helper for all*config. Loads (merges) the configuration file specified
+        by KCONFIG_ALLCONFIG, if any. See Documentation/kbuild/kconfig.txt in
+        the Linux kernel.
+
+        Disables warnings for duplicated assignments within configuration files
+        for the duration of the call
+        (kconf.warn_assign_override/warn_assign_redun = False), and restores
+        the previous warning settings at the end. The KCONFIG_ALLCONFIG
+        configuration file is expected to override symbols.
+
+        Exits with sys.exit() (which raises a SystemExit exception) and prints
+        an error to stderr if KCONFIG_ALLCONFIG is set but the configuration
+        file can't be opened.
+
+        filename:
+          Command-specific configuration filename - "allyes.config",
+          "allno.config", etc.
+        """
+        load_allconfig(self, filename)
+
+    def write_autoconf(self, filename=None, header=None):
+        r"""
+        Writes out symbol values as a C header file, matching the format used
+        by include/generated/autoconf.h in the kernel.
+
+        The ordering of the #defines matches the one generated by
+        write_config(). The order in the C implementation depends on the hash
+        table implementation as of writing, and so won't match.
+
+        If 'filename' exists and its contents is identical to what would get
+        written out, it is left untouched. This avoids updating file metadata
+        like the modification time and possibly triggering redundant work in
+        build tools.
+
+        filename (default: None):
+          Path to write header to.
+
+          If None (the default), the path in the environment variable
+          KCONFIG_AUTOHEADER is used if set, and "include/generated/autoconf.h"
+          otherwise. This is compatible with the C tools.
+
+        header (default: None):
+          Text inserted verbatim at the beginning of the file. You would
+          usually want it enclosed in '/* */' to make it a C comment, and
+          include a trailing newline.
+
+          If None (the default), the value of the environment variable
+          KCONFIG_AUTOHEADER_HEADER had when the Kconfig instance was created
+          will be used if it was set, and no header otherwise. See the
+          Kconfig.header_header attribute.
+
+        Returns a string with a message saying that the header got saved, or
+        that there were no changes to it. This is meant to reduce boilerplate
+        in tools, which can do e.g. print(kconf.write_autoconf()).
+        """
+        if filename is None:
+            filename = os.getenv("KCONFIG_AUTOHEADER",
+                                 "include/generated/autoconf.h")
+
+        if self._write_if_changed(filename, self._autoconf_contents(header)):
+            return "Kconfig header saved to '{}'".format(filename)
+        return "No change to Kconfig header in '{}'".format(filename)
+
+    def _autoconf_contents(self, header):
+        # write_autoconf() helper. Returns the contents to write as a string,
+        # with 'header' or KCONFIG_AUTOHEADER_HEADER at the beginning.
+
+        if header is None:
+            header = self.header_header
+
+        chunks = [header]  # "".join()ed later
+        add = chunks.append
+
+        for sym in self.unique_defined_syms:
+            # _write_to_conf is determined when the value is calculated. This
+            # is a hidden function call due to property magic.
+            #
+            # Note: In client code, you can check if sym.config_string is empty
+            # instead, to avoid accessing the internal _write_to_conf variable
+            # (though it's likely to keep working).
+            val = sym.str_value
+            if not sym._write_to_conf:
+                continue
+
+            if sym.orig_type in _BOOL_TRISTATE:
+                if val == "y":
+                    add("#define {}{} 1\n"
+                        .format(self.config_prefix, sym.name))
+                elif val == "m":
+                    add("#define {}{}_MODULE 1\n"
+                        .format(self.config_prefix, sym.name))
+
+            elif sym.orig_type is STRING:
+                add('#define {}{} "{}"\n'
+                    .format(self.config_prefix, sym.name, escape(val)))
+
+            else:  # sym.orig_type in _INT_HEX:
+                if sym.orig_type is HEX and \
+                   not val.startswith(("0x", "0X")):
+                    val = "0x" + val
+
+                add("#define {}{} {}\n"
+                    .format(self.config_prefix, sym.name, val))
+
+        return "".join(chunks)
+
+    def write_config(self, filename=None, header=None, save_old=True,
+                     verbose=None):
+        r"""
+        Writes out symbol values in the .config format. The format matches the
+        C implementation, including ordering.
+
+        Symbols appear in the same order in generated .config files as they do
+        in the Kconfig files. For symbols defined in multiple locations, a
+        single assignment is written out corresponding to the first location
+        where the symbol is defined.
+
+        See the 'Intro to symbol values' section in the module docstring to
+        understand which symbols get written out.
+
+        If 'filename' exists and its contents is identical to what would get
+        written out, it is left untouched. This avoids updating file metadata
+        like the modification time and possibly triggering redundant work in
+        build tools.
+
+        See the Kconfig.__init__() docstring for raised exceptions
+        (OSError/IOError). KconfigError is never raised here.
+
+        filename (default: None):
+          Path to write configuration to (a string).
+
+          If None (the default), the path in the environment variable
+          KCONFIG_CONFIG is used if set, and ".config" otherwise. See
+          standard_config_filename().
+
+        header (default: None):
+          Text inserted verbatim at the beginning of the file. You would
+          usually want each line to start with '#' to make it a comment, and
+          include a trailing newline.
+
+          if None (the default), the value of the environment variable
+          KCONFIG_CONFIG_HEADER had when the Kconfig instance was created will
+          be used if it was set, and no header otherwise. See the
+          Kconfig.config_header attribute.
+
+        save_old (default: True):
+          If True and <filename> already exists, a copy of it will be saved to
+          <filename>.old in the same directory before the new configuration is
+          written.
+
+          Errors are silently ignored if <filename>.old cannot be written (e.g.
+          due to being a directory, or <filename> being something like
+          /dev/null).
+
+        verbose (default: None):
+          Limited backwards compatibility to prevent crashes. A warning is
+          printed if anything but None is passed.
+
+          Prior to Kconfiglib 12.0.0, this option enabled printing of messages
+          to stdout when 'filename' was None. A message is (always) returned
+          now instead, which is more flexible.
+
+          Will probably be removed in some future version.
+
+        Returns a string with a message saying which file got saved. This is
+        meant to reduce boilerplate in tools, which can do e.g.
+        print(kconf.write_config()).
+        """
+        if verbose is not None:
+            _warn_verbose_deprecated("write_config")
+
+        if filename is None:
+            filename = standard_config_filename()
+
+        contents = self._config_contents(header)
+        if self._contents_eq(filename, contents):
+            return "No change to configuration in '{}'".format(filename)
+
+        if save_old:
+            _save_old(filename)
+
+        with self._open(filename, "w") as f:
+            f.write(contents)
+
+        return "Configuration saved to '{}'".format(filename)
+
+    def _config_contents(self, header):
+        # write_config() helper. Returns the contents to write as a string,
+        # with 'header' or KCONFIG_CONFIG_HEADER at the beginning.
+        #
+        # More memory friendly would be to 'yield' the strings and
+        # "".join(_config_contents()), but it was a bit slower on my system.
+
+        # node_iter() was used here before commit 3aea9f7 ("Add '# end of
+        # <menu>' after menus in .config"). Those comments get tricky to
+        # implement with it.
+
+        for sym in self.unique_defined_syms:
+            sym._visited = False
+
+        if header is None:
+            header = self.config_header
+
+        chunks = [header]  # "".join()ed later
+        add = chunks.append
+
+        # Did we just print an '# end of ...' comment?
+        after_end_comment = False
+
+        node = self.top_node
+        while 1:
+            # Jump to the next node with an iterative tree walk
+            if node.list:
+                node = node.list
+            elif node.next:
+                node = node.next
+            else:
+                while node.parent:
+                    node = node.parent
+
+                    # Add a comment when leaving visible menus
+                    if node.item is MENU and expr_value(node.dep) and \
+                       expr_value(node.visibility) and \
+                       node is not self.top_node:
+                        add("# end of {}\n".format(node.prompt[0]))
+                        after_end_comment = True
+
+                    if node.next:
+                        node = node.next
+                        break
+                else:
+                    # No more nodes
+                    return "".join(chunks)
+
+            # Generate configuration output for the node
+
+            item = node.item
+
+            if item.__class__ is Symbol:
+                if item._visited:
+                    continue
+                item._visited = True
+
+                conf_string = item.config_string
+                if not conf_string:
+                    continue
+
+                if after_end_comment:
+                    # Add a blank line before the first symbol printed after an
+                    # '# end of ...' comment
+                    after_end_comment = False
+                    add("\n")
+                add(conf_string)
+
+            elif expr_value(node.dep) and \
+                 ((item is MENU and expr_value(node.visibility)) or
+                  item is COMMENT):
+
+                add("\n#\n# {}\n#\n".format(node.prompt[0]))
+                after_end_comment = False
+
+    def write_min_config(self, filename, header=None):
+        """
+        Writes out a "minimal" configuration file, omitting symbols whose value
+        matches their default value. The format matches the one produced by
+        'make savedefconfig'.
+
+        The resulting configuration file is incomplete, but a complete
+        configuration can be derived from it by loading it. Minimal
+        configuration files can serve as a more manageable configuration format
+        compared to a "full" .config file, especially when configurations files
+        are merged or edited by hand.
+
+        See the Kconfig.__init__() docstring for raised exceptions
+        (OSError/IOError). KconfigError is never raised here.
+
+        filename:
+          Path to write minimal configuration to.
+
+        header (default: None):
+          Text inserted verbatim at the beginning of the file. You would
+          usually want each line to start with '#' to make it a comment, and
+          include a final terminating newline.
+
+          if None (the default), the value of the environment variable
+          KCONFIG_CONFIG_HEADER had when the Kconfig instance was created will
+          be used if it was set, and no header otherwise. See the
+          Kconfig.config_header attribute.
+
+        Returns a string with a message saying the minimal configuration got
+        saved, or that there were no changes to it. This is meant to reduce
+        boilerplate in tools, which can do e.g.
+        print(kconf.write_min_config()).
+        """
+        if self._write_if_changed(filename, self._min_config_contents(header)):
+            return "Minimal configuration saved to '{}'".format(filename)
+        return "No change to minimal configuration in '{}'".format(filename)
+
+    def _min_config_contents(self, header):
+        # write_min_config() helper. Returns the contents to write as a string,
+        # with 'header' or KCONFIG_CONFIG_HEADER at the beginning.
+
+        if header is None:
+            header = self.config_header
+
+        chunks = [header]  # "".join()ed later
+        add = chunks.append
+
+        for sym in self.unique_defined_syms:
+            # Skip symbols that cannot be changed. Only check
+            # non-choice symbols, as selects don't affect choice
+            # symbols.
+            if not sym.choice and \
+               sym.visibility <= expr_value(sym.rev_dep):
+                continue
+
+            # Skip symbols whose value matches their default
+            if sym.str_value == sym._str_default():
+                continue
+
+            # Skip symbols that would be selected by default in a
+            # choice, unless the choice is optional or the symbol type
+            # isn't bool (it might be possible to set the choice mode
+            # to n or the symbol to m in those cases).
+            if sym.choice and \
+               not sym.choice.is_optional and \
+               sym.choice._selection_from_defaults() is sym and \
+               sym.orig_type is BOOL and \
+               sym.tri_value == 2:
+                continue
+
+            add(sym.config_string)
+
+        return "".join(chunks)
+
+    def sync_deps(self, path):
+        """
+        Creates or updates a directory structure that can be used to avoid
+        doing a full rebuild whenever the configuration is changed, mirroring
+        include/config/ in the kernel.
+
+        This function is intended to be called during each build, before
+        compiling source files that depend on configuration symbols.
+
+        See the Kconfig.__init__() docstring for raised exceptions
+        (OSError/IOError). KconfigError is never raised here.
+
+        path:
+          Path to directory
+
+        sync_deps(path) does the following:
+
+          1. If the directory <path> does not exist, it is created.
+
+          2. If <path>/auto.conf exists, old symbol values are loaded from it,
+             which are then compared against the current symbol values. If a
+             symbol has changed value (would generate different output in
+             autoconf.h compared to before), the change is signaled by
+             touch'ing a file corresponding to the symbol.
+
+             The first time sync_deps() is run on a directory, <path>/auto.conf
+             won't exist, and no old symbol values will be available. This
+             logically has the same effect as updating the entire
+             configuration.
+
+             The path to a symbol's file is calculated from the symbol's name
+             by replacing all '_' with '/' and appending '.h'. For example, the
+             symbol FOO_BAR_BAZ gets the file <path>/foo/bar/baz.h, and FOO
+             gets the file <path>/foo.h.
+
+             This scheme matches the C tools. The point is to avoid having a
+             single directory with a huge number of files, which the underlying
+             filesystem might not handle well.
+
+          3. A new auto.conf with the current symbol values is written, to keep
+             track of them for the next build.
+
+             If auto.conf exists and its contents is identical to what would
+             get written out, it is left untouched. This avoids updating file
+             metadata like the modification time and possibly triggering
+             redundant work in build tools.
+
+
+        The last piece of the puzzle is knowing what symbols each source file
+        depends on. Knowing that, dependencies can be added from source files
+        to the files corresponding to the symbols they depends on. The source
+        file will then get recompiled (only) when the symbol value changes
+        (provided sync_deps() is run first during each build).
+
+        The tool in the kernel that extracts symbol dependencies from source
+        files is scripts/basic/fixdep.c. Missing symbol files also correspond
+        to "not changed", which fixdep deals with by using the $(wildcard) Make
+        function when adding symbol prerequisites to source files.
+
+        In case you need a different scheme for your project, the sync_deps()
+        implementation can be used as a template.
+        """
+        if not exists(path):
+            os.mkdir(path, 0o755)
+
+        # Load old values from auto.conf, if any
+        self._load_old_vals(path)
+
+        for sym in self.unique_defined_syms:
+            # _write_to_conf is determined when the value is calculated. This
+            # is a hidden function call due to property magic.
+            #
+            # Note: In client code, you can check if sym.config_string is empty
+            # instead, to avoid accessing the internal _write_to_conf variable
+            # (though it's likely to keep working).
+            val = sym.str_value
+
+            # n tristate values do not get written to auto.conf and autoconf.h,
+            # making a missing symbol logically equivalent to n
+
+            if sym._write_to_conf:
+                if sym._old_val is None and \
+                   sym.orig_type in _BOOL_TRISTATE and \
+                   val == "n":
+                    # No old value (the symbol was missing or n), new value n.
+                    # No change.
+                    continue
+
+                if val == sym._old_val:
+                    # New value matches old. No change.
+                    continue
+
+            elif sym._old_val is None:
+                # The symbol wouldn't appear in autoconf.h (because
+                # _write_to_conf is false), and it wouldn't have appeared in
+                # autoconf.h previously either (because it didn't appear in
+                # auto.conf). No change.
+                continue
+
+            # 'sym' has a new value. Flag it.
+            _touch_dep_file(path, sym.name)
+
+        # Remember the current values as the "new old" values.
+        #
+        # This call could go anywhere after the call to _load_old_vals(), but
+        # putting it last means _sync_deps() can be safely rerun if it fails
+        # before this point.
+        self._write_old_vals(path)
+
+    def _load_old_vals(self, path):
+        # Loads old symbol values from auto.conf into a dedicated
+        # Symbol._old_val field. Mirrors load_config().
+        #
+        # The extra field could be avoided with some trickery involving dumping
+        # symbol values and restoring them later, but this is simpler and
+        # faster. The C tools also use a dedicated field for this purpose.
+
+        for sym in self.unique_defined_syms:
+            sym._old_val = None
+
+        try:
+            auto_conf = self._open(join(path, "auto.conf"), "r")
+        except EnvironmentError as e:
+            if e.errno == errno.ENOENT:
+                # No old values
+                return
+            raise
+
+        with auto_conf as f:
+            for line in f:
+                match = self._set_match(line)
+                if not match:
+                    # We only expect CONFIG_FOO=... (and possibly a header
+                    # comment) in auto.conf
+                    continue
+
+                name, val = match.groups()
+                if name in self.syms:
+                    sym = self.syms[name]
+
+                    if sym.orig_type is STRING:
+                        match = _conf_string_match(val)
+                        if not match:
+                            continue
+                        val = unescape(match.group(1))
+
+                    self.syms[name]._old_val = val
+                else:
+                    # Flag that the symbol no longer exists, in
+                    # case something still depends on it
+                    _touch_dep_file(path, name)
+
+    def _write_old_vals(self, path):
+        # Helper for writing auto.conf. Basically just a simplified
+        # write_config() that doesn't write any comments (including
+        # '# CONFIG_FOO is not set' comments). The format matches the C
+        # implementation, though the ordering is arbitrary there (depends on
+        # the hash table implementation).
+        #
+        # A separate helper function is neater than complicating write_config()
+        # by passing a flag to it, plus we only need to look at symbols here.
+
+        self._write_if_changed(
+            os.path.join(path, "auto.conf"),
+            self._old_vals_contents())
+
+    def _old_vals_contents(self):
+        # _write_old_vals() helper. Returns the contents to write as a string.
+
+        # Temporary list instead of generator makes this a bit faster
+        return "".join([
+            sym.config_string for sym in self.unique_defined_syms
+                if not (sym.orig_type in _BOOL_TRISTATE and not sym.tri_value)
+        ])
+
+    def node_iter(self, unique_syms=False):
+        """
+        Returns a generator for iterating through all MenuNode's in the Kconfig
+        tree. The iteration is done in Kconfig definition order (each node is
+        visited before its children, and the children of a node are visited
+        before the next node).
+
+        The Kconfig.top_node menu node is skipped. It contains an implicit menu
+        that holds the top-level items.
+
+        As an example, the following code will produce a list equal to
+        Kconfig.defined_syms:
+
+          defined_syms = [node.item for node in kconf.node_iter()
+                          if isinstance(node.item, Symbol)]
+
+        unique_syms (default: False):
+          If True, only the first MenuNode will be included for symbols defined
+          in multiple locations.
+
+          Using kconf.node_iter(True) in the example above would give a list
+          equal to unique_defined_syms.
+        """
+        if unique_syms:
+            for sym in self.unique_defined_syms:
+                sym._visited = False
+
+        node = self.top_node
+        while 1:
+            # Jump to the next node with an iterative tree walk
+            if node.list:
+                node = node.list
+            elif node.next:
+                node = node.next
+            else:
+                while node.parent:
+                    node = node.parent
+                    if node.next:
+                        node = node.next
+                        break
+                else:
+                    # No more nodes
+                    return
+
+            if unique_syms and node.item.__class__ is Symbol:
+                if node.item._visited:
+                    continue
+                node.item._visited = True
+
+            yield node
+
+    def eval_string(self, s):
+        """
+        Returns the tristate value of the expression 's', represented as 0, 1,
+        and 2 for n, m, and y, respectively. Raises KconfigError on syntax
+        errors. Warns if undefined symbols are referenced.
+
+        As an example, if FOO and BAR are tristate symbols at least one of
+        which has the value y, then eval_string("y && (FOO || BAR)") returns
+        2 (y).
+
+        To get the string value of non-bool/tristate symbols, use
+        Symbol.str_value. eval_string() always returns a tristate value, and
+        all non-bool/tristate symbols have the tristate value 0 (n).
+
+        The expression parsing is consistent with how parsing works for
+        conditional ('if ...') expressions in the configuration, and matches
+        the C implementation. m is rewritten to 'm && MODULES', so
+        eval_string("m") will return 0 (n) unless modules are enabled.
+        """
+        # The parser is optimized to be fast when parsing Kconfig files (where
+        # an expression can never appear at the beginning of a line). We have
+        # to monkey-patch things a bit here to reuse it.
+
+        self.filename = None
+
+        self._tokens = self._tokenize("if " + s)
+        # Strip "if " to avoid giving confusing error messages
+        self._line = s
+        self._tokens_i = 1  # Skip the 'if' token
+
+        return expr_value(self._expect_expr_and_eol())
+
+    def unset_values(self):
+        """
+        Removes any user values from all symbols, as if Kconfig.load_config()
+        or Symbol.set_value() had never been called.
+        """
+        self._warn_assign_no_prompt = False
+        try:
+            # set_value() already rejects undefined symbols, and they don't
+            # need to be invalidated (because their value never changes), so we
+            # can just iterate over defined symbols
+            for sym in self.unique_defined_syms:
+                sym.unset_value()
+
+            for choice in self.unique_choices:
+                choice.unset_value()
+        finally:
+            self._warn_assign_no_prompt = True
+
+    def enable_warnings(self):
+        """
+        Do 'Kconfig.warn = True' instead. Maintained for backwards
+        compatibility.
+        """
+        self.warn = True
+
+    def disable_warnings(self):
+        """
+        Do 'Kconfig.warn = False' instead. Maintained for backwards
+        compatibility.
+        """
+        self.warn = False
+
+    def enable_stderr_warnings(self):
+        """
+        Do 'Kconfig.warn_to_stderr = True' instead. Maintained for backwards
+        compatibility.
+        """
+        self.warn_to_stderr = True
+
+    def disable_stderr_warnings(self):
+        """
+        Do 'Kconfig.warn_to_stderr = False' instead. Maintained for backwards
+        compatibility.
+        """
+        self.warn_to_stderr = False
+
+    def enable_undef_warnings(self):
+        """
+        Do 'Kconfig.warn_assign_undef = True' instead. Maintained for backwards
+        compatibility.
+        """
+        self.warn_assign_undef = True
+
+    def disable_undef_warnings(self):
+        """
+        Do 'Kconfig.warn_assign_undef = False' instead. Maintained for
+        backwards compatibility.
+        """
+        self.warn_assign_undef = False
+
+    def enable_override_warnings(self):
+        """
+        Do 'Kconfig.warn_assign_override = True' instead. Maintained for
+        backwards compatibility.
+        """
+        self.warn_assign_override = True
+
+    def disable_override_warnings(self):
+        """
+        Do 'Kconfig.warn_assign_override = False' instead. Maintained for
+        backwards compatibility.
+        """
+        self.warn_assign_override = False
+
+    def enable_redun_warnings(self):
+        """
+        Do 'Kconfig.warn_assign_redun = True' instead. Maintained for backwards
+        compatibility.
+        """
+        self.warn_assign_redun = True
+
+    def disable_redun_warnings(self):
+        """
+        Do 'Kconfig.warn_assign_redun = False' instead. Maintained for
+        backwards compatibility.
+        """
+        self.warn_assign_redun = False
+
+    def __repr__(self):
+        """
+        Returns a string with information about the Kconfig object when it is
+        evaluated on e.g. the interactive Python prompt.
+        """
+        def status(flag):
+            return "enabled" if flag else "disabled"
+
+        return "<{}>".format(", ".join((
+            "configuration with {} symbols".format(len(self.syms)),
+            'main menu prompt "{}"'.format(self.mainmenu_text),
+            "srctree is current directory" if not self.srctree else
+                'srctree "{}"'.format(self.srctree),
+            'config symbol prefix "{}"'.format(self.config_prefix),
+            "warnings " + status(self.warn),
+            "printing of warnings to stderr " + status(self.warn_to_stderr),
+            "undef. symbol assignment warnings " +
+                status(self.warn_assign_undef),
+            "overriding symbol assignment warnings " +
+                status(self.warn_assign_override),
+            "redundant symbol assignment warnings " +
+                status(self.warn_assign_redun)
+        )))
+
+    #
+    # Private methods
+    #
+
+
+    #
+    # File reading
+    #
+
+    def _open_config(self, filename):
+        # Opens a .config file. First tries to open 'filename', then
+        # '$srctree/filename' if $srctree was set when the configuration was
+        # loaded.
+
+        try:
+            return self._open(filename, "r")
+        except EnvironmentError as e:
+            # This will try opening the same file twice if $srctree is unset,
+            # but it's not a big deal
+            try:
+                return self._open(join(self.srctree, filename), "r")
+            except EnvironmentError as e2:
+                # This is needed for Python 3, because e2 is deleted after
+                # the try block:
+                #
+                # https://docs.python.org/3/reference/compound_stmts.html#the-try-statement
+                e = e2
+
+            raise _KconfigIOError(
+                e, "Could not open '{}' ({}: {}). Check that the $srctree "
+                   "environment variable ({}) is set correctly."
+                   .format(filename, errno.errorcode[e.errno], e.strerror,
+                           "set to '{}'".format(self.srctree) if self.srctree
+                               else "unset or blank"))
+
+    def _enter_file(self, filename):
+        # Jumps to the beginning of a sourced Kconfig file, saving the previous
+        # position and file object.
+        #
+        # filename:
+        #   Absolute path to file
+
+        # Path relative to $srctree, stored in e.g. self.filename (which makes
+        # it indirectly show up in MenuNode.filename). Equals 'filename' for
+        # absolute paths passed to 'source'.
+        if filename.startswith(self._srctree_prefix):
+            # Relative path (or a redundant absolute path to within $srctree,
+            # but it's probably fine to reduce those too)
+            rel_filename = filename[len(self._srctree_prefix):]
+        else:
+            # Absolute path
+            rel_filename = filename
+
+        self.kconfig_filenames.append(rel_filename)
+
+        # The parent Kconfig files are represented as a list of
+        # (<include path>, <Python 'file' object for Kconfig file>) tuples.
+        #
+        # <include path> is immutable and holds a *tuple* of
+        # (<filename>, <linenr>) tuples, giving the locations of the 'source'
+        # statements in the parent Kconfig files. The current include path is
+        # also available in Kconfig._include_path.
+        #
+        # The point of this redundant setup is to allow Kconfig._include_path
+        # to be assigned directly to MenuNode.include_path without having to
+        # copy it, sharing it wherever possible.
+
+        # Save include path and 'file' object (via its 'readline' function)
+        # before entering the file
+        self._filestack.append((self._include_path, self._readline))
+
+        # _include_path is a tuple, so this rebinds the variable instead of
+        # doing in-place modification
+        self._include_path += ((self.filename, self.linenr),)
+
+        # Check for recursive 'source'
+        for name, _ in self._include_path:
+            if name == rel_filename:
+                raise KconfigError(
+                    "\n{}:{}: recursive 'source' of '{}' detected. Check that "
+                    "environment variables are set correctly.\n"
+                    "Include path:\n{}"
+                    .format(self.filename, self.linenr, rel_filename,
+                            "\n".join("{}:{}".format(name, linenr)
+                                      for name, linenr in self._include_path)))
+
+        try:
+            self._readline = self._open(filename, "r").readline
+        except EnvironmentError as e:
+            # We already know that the file exists
+            raise _KconfigIOError(
+                e, "{}:{}: Could not open '{}' (in '{}') ({}: {})"
+                   .format(self.filename, self.linenr, filename,
+                           self._line.strip(),
+                           errno.errorcode[e.errno], e.strerror))
+
+        self.filename = rel_filename
+        self.linenr = 0
+
+    def _leave_file(self):
+        # Returns from a Kconfig file to the file that sourced it. See
+        # _enter_file().
+
+        # Restore location from parent Kconfig file
+        self.filename, self.linenr = self._include_path[-1]
+        # Restore include path and 'file' object
+        self._readline.__self__.close()  # __self__ fetches the 'file' object
+        self._include_path, self._readline = self._filestack.pop()
+
+    def _next_line(self):
+        # Fetches and tokenizes the next line from the current Kconfig file.
+        # Returns False at EOF and True otherwise.
+
+        # We might already have tokens from parsing a line and discovering that
+        # it's part of a different construct
+        if self._reuse_tokens:
+            self._reuse_tokens = False
+            # self._tokens_i is known to be 1 here, because _parse_props()
+            # leaves it like that when it can't recognize a line (or parses a
+            # help text)
+            return True
+
+        # readline() returns '' over and over at EOF, which we rely on for help
+        # texts at the end of files (see _line_after_help())
+        line = self._readline()
+        if not line:
+            return False
+        self.linenr += 1
+
+        # Handle line joining
+        while line.endswith("\\\n"):
+            line = line[:-2] + self._readline()
+            self.linenr += 1
+
+        self._tokens = self._tokenize(line)
+        # Initialize to 1 instead of 0 to factor out code from _parse_block()
+        # and _parse_props(). They immediately fetch self._tokens[0].
+        self._tokens_i = 1
+
+        return True
+
+    def _line_after_help(self, line):
+        # Tokenizes a line after a help text. This case is special in that the
+        # line has already been fetched (to discover that it isn't part of the
+        # help text).
+        #
+        # An earlier version used a _saved_line variable instead that was
+        # checked in _next_line(). This special-casing gets rid of it and makes
+        # _reuse_tokens alone sufficient to handle unget.
+
+        # Handle line joining
+        while line.endswith("\\\n"):
+            line = line[:-2] + self._readline()
+            self.linenr += 1
+
+        self._tokens = self._tokenize(line)
+        self._reuse_tokens = True
+
+    def _write_if_changed(self, filename, contents):
+        # Writes 'contents' into 'filename', but only if it differs from the
+        # current contents of the file.
+        #
+        # Another variant would be write a temporary file on the same
+        # filesystem, compare the files, and rename() the temporary file if it
+        # differs, but it breaks stuff like write_config("/dev/null"), which is
+        # used out there to force evaluation-related warnings to be generated.
+        # This simple version is pretty failsafe and portable.
+        #
+        # Returns True if the file has changed and is updated, and False
+        # otherwise.
+
+        if self._contents_eq(filename, contents):
+            return False
+        with self._open(filename, "w") as f:
+            f.write(contents)
+        return True
+
+    def _contents_eq(self, filename, contents):
+        # Returns True if the contents of 'filename' is 'contents' (a string),
+        # and False otherwise (including if 'filename' can't be opened/read)
+
+        try:
+            with self._open(filename, "r") as f:
+                # Robust re. things like encoding and line endings (mmap()
+                # trickery isn't)
+                return f.read(len(contents) + 1) == contents
+        except EnvironmentError:
+            # If the error here would prevent writing the file as well, we'll
+            # notice it later
+            return False
+
+    #
+    # Tokenization
+    #
+
+    def _lookup_sym(self, name):
+        # Fetches the symbol 'name' from the symbol table, creating and
+        # registering it if it does not exist. If '_parsing_kconfigs' is False,
+        # it means we're in eval_string(), and new symbols won't be registered.
+
+        if name in self.syms:
+            return self.syms[name]
+
+        sym = Symbol()
+        sym.kconfig = self
+        sym.name = name
+        sym.is_constant = False
+        sym.rev_dep = sym.weak_rev_dep = sym.direct_dep = self.n
+
+        if self._parsing_kconfigs:
+            self.syms[name] = sym
+        else:
+            self._warn("no symbol {} in configuration".format(name))
+
+        return sym
+
+    def _lookup_const_sym(self, name):
+        # Like _lookup_sym(), for constant (quoted) symbols
+
+        if name in self.const_syms:
+            return self.const_syms[name]
+
+        sym = Symbol()
+        sym.kconfig = self
+        sym.name = name
+        sym.is_constant = True
+        sym.rev_dep = sym.weak_rev_dep = sym.direct_dep = self.n
+
+        if self._parsing_kconfigs:
+            self.const_syms[name] = sym
+
+        return sym
+
+    def _tokenize(self, s):
+        # Parses 's', returning a None-terminated list of tokens. Registers any
+        # new symbols encountered with _lookup(_const)_sym().
+        #
+        # Tries to be reasonably speedy by processing chunks of text via
+        # regexes and string operations where possible. This is the biggest
+        # hotspot during parsing.
+        #
+        # It might be possible to rewrite this to 'yield' tokens instead,
+        # working across multiple lines. Lookback and compatibility with old
+        # janky versions of the C tools complicate things though.
+
+        self._line = s  # Used for error reporting
+
+        # Initial token on the line
+        match = _command_match(s)
+        if not match:
+            if s.isspace() or s.lstrip().startswith("#"):
+                return (None,)
+            self._parse_error("unknown token at start of line")
+
+        # Tricky implementation detail: While parsing a token, 'token' refers
+        # to the previous token. See _STRING_LEX for why this is needed.
+        token = _get_keyword(match.group(1))
+        if not token:
+            # Backwards compatibility with old versions of the C tools, which
+            # (accidentally) accepted stuff like "--help--" and "-help---".
+            # This was fixed in the C tools by commit c2264564 ("kconfig: warn
+            # of unhandled characters in Kconfig commands"), committed in July
+            # 2015, but it seems people still run Kconfiglib on older kernels.
+            if s.strip(" \t\n-") == "help":
+                return (_T_HELP, None)
+
+            # If the first token is not a keyword (and not a weird help token),
+            # we have a preprocessor variable assignment (or a bare macro on a
+            # line)
+            self._parse_assignment(s)
+            return (None,)
+
+        tokens = [token]
+        # The current index in the string being tokenized
+        i = match.end()
+
+        # Main tokenization loop (for tokens past the first one)
+        while i < len(s):
+            # Test for an identifier/keyword first. This is the most common
+            # case.
+            match = _id_keyword_match(s, i)
+            if match:
+                # We have an identifier or keyword
+
+                # Check what it is. lookup_sym() will take care of allocating
+                # new symbols for us the first time we see them. Note that
+                # 'token' still refers to the previous token.
+
+                name = match.group(1)
+                keyword = _get_keyword(name)
+                if keyword:
+                    # It's a keyword
+                    token = keyword
+                    # Jump past it
+                    i = match.end()
+
+                elif token not in _STRING_LEX:
+                    # It's a non-const symbol, except we translate n, m, and y
+                    # into the corresponding constant symbols, like the C
+                    # implementation
+
+                    if "$" in name:
+                        # Macro expansion within symbol name
+                        name, s, i = self._expand_name(s, i)
+                    else:
+                        i = match.end()
+
+                    token = self.const_syms[name] if name in STR_TO_TRI else \
+                        self._lookup_sym(name)
+
+                else:
+                    # It's a case of missing quotes. For example, the
+                    # following is accepted:
+                    #
+                    #   menu unquoted_title
+                    #
+                    #   config A
+                    #       tristate unquoted_prompt
+                    #
+                    #   endmenu
+                    #
+                    # Named choices ('choice FOO') also end up here.
+
+                    if token is not _T_CHOICE:
+                        self._warn("style: quotes recommended around '{}' in '{}'"
+                                   .format(name, self._line.strip()),
+                                   self.filename, self.linenr)
+
+                    token = name
+                    i = match.end()
+
+            else:
+                # Neither a keyword nor a non-const symbol
+
+                # We always strip whitespace after tokens, so it is safe to
+                # assume that s[i] is the start of a token here.
+                c = s[i]
+
+                if c in "\"'":
+                    if "$" not in s and "\\" not in s:
+                        # Fast path for lines without $ and \. Find the
+                        # matching quote.
+                        end_i = s.find(c, i + 1) + 1
+                        if not end_i:
+                            self._parse_error("unterminated string")
+                        val = s[i + 1:end_i - 1]
+                        i = end_i
+                    else:
+                        # Slow path
+                        s, end_i = self._expand_str(s, i)
+
+                        # os.path.expandvars() and the $UNAME_RELEASE replace()
+                        # is a backwards compatibility hack, which should be
+                        # reasonably safe as expandvars() leaves references to
+                        # undefined env. vars. as is.
+                        #
+                        # The preprocessor functionality changed how
+                        # environment variables are referenced, to $(FOO).
+                        val = expandvars(s[i + 1:end_i - 1]
+                                         .replace("$UNAME_RELEASE",
+                                                  _UNAME_RELEASE))
+
+                        i = end_i
+
+                    # This is the only place where we don't survive with a
+                    # single token of lookback: 'option env="FOO"' does not
+                    # refer to a constant symbol named "FOO".
+                    token = \
+                        val if token in _STRING_LEX or tokens[0] is _T_OPTION \
+                        else self._lookup_const_sym(val)
+
+                elif s.startswith("&&", i):
+                    token = _T_AND
+                    i += 2
+
+                elif s.startswith("||", i):
+                    token = _T_OR
+                    i += 2
+
+                elif c == "=":
+                    token = _T_EQUAL
+                    i += 1
+
+                elif s.startswith("!=", i):
+                    token = _T_UNEQUAL
+                    i += 2
+
+                elif c == "!":
+                    token = _T_NOT
+                    i += 1
+
+                elif c == "(":
+                    token = _T_OPEN_PAREN
+                    i += 1
+
+                elif c == ")":
+                    token = _T_CLOSE_PAREN
+                    i += 1
+
+                elif c == "#":
+                    break
+
+
+                # Very rare
+
+                elif s.startswith("<=", i):
+                    token = _T_LESS_EQUAL
+                    i += 2
+
+                elif c == "<":
+                    token = _T_LESS
+                    i += 1
+
+                elif s.startswith(">=", i):
+                    token = _T_GREATER_EQUAL
+                    i += 2
+
+                elif c == ">":
+                    token = _T_GREATER
+                    i += 1
+
+
+                else:
+                    self._parse_error("unknown tokens in line")
+
+
+                # Skip trailing whitespace
+                while i < len(s) and s[i].isspace():
+                    i += 1
+
+
+            # Add the token
+            tokens.append(token)
+
+        # None-terminating the token list makes token fetching simpler/faster
+        tokens.append(None)
+
+        return tokens
+
+    # Helpers for syntax checking and token fetching. See the
+    # 'Intro to expressions' section for what a constant symbol is.
+    #
+    # More of these could be added, but the single-use cases are inlined as an
+    # optimization.
+
+    def _expect_sym(self):
+        token = self._tokens[self._tokens_i]
+        self._tokens_i += 1
+
+        if token.__class__ is not Symbol:
+            self._parse_error("expected symbol")
+
+        return token
+
+    def _expect_nonconst_sym(self):
+        # Used for 'select' and 'imply' only. We know the token indices.
+
+        token = self._tokens[1]
+        self._tokens_i = 2
+
+        if token.__class__ is not Symbol or token.is_constant:
+            self._parse_error("expected nonconstant symbol")
+
+        return token
+
+    def _expect_str_and_eol(self):
+        token = self._tokens[self._tokens_i]
+        self._tokens_i += 1
+
+        if token.__class__ is not str:
+            self._parse_error("expected string")
+
+        if self._tokens[self._tokens_i] is not None:
+            self._trailing_tokens_error()
+
+        return token
+
+    def _expect_expr_and_eol(self):
+        expr = self._parse_expr(True)
+
+        if self._tokens[self._tokens_i] is not None:
+            self._trailing_tokens_error()
+
+        return expr
+
+    def _check_token(self, token):
+        # If the next token is 'token', removes it and returns True
+
+        if self._tokens[self._tokens_i] is token:
+            self._tokens_i += 1
+            return True
+        return False
+
+    #
+    # Preprocessor logic
+    #
+
+    def _parse_assignment(self, s):
+        # Parses a preprocessor variable assignment, registering the variable
+        # if it doesn't already exist. Also takes care of bare macros on lines
+        # (which are allowed, and can be useful for their side effects).
+
+        # Expand any macros in the left-hand side of the assignment (the
+        # variable name)
+        s = s.lstrip()
+        i = 0
+        while 1:
+            i = _assignment_lhs_fragment_match(s, i).end()
+            if s.startswith("$(", i):
+                s, i = self._expand_macro(s, i, ())
+            else:
+                break
+
+        if s.isspace():
+            # We also accept a bare macro on a line (e.g.
+            # $(warning-if,$(foo),ops)), provided it expands to a blank string
+            return
+
+        # Assigned variable
+        name = s[:i]
+
+
+        # Extract assignment operator (=, :=, or +=) and value
+        rhs_match = _assignment_rhs_match(s, i)
+        if not rhs_match:
+            self._parse_error("syntax error")
+
+        op, val = rhs_match.groups()
+
+
+        if name in self.variables:
+            # Already seen variable
+            var = self.variables[name]
+        else:
+            # New variable
+            var = Variable()
+            var.kconfig = self
+            var.name = name
+            var._n_expansions = 0
+            self.variables[name] = var
+
+            # += acts like = on undefined variables (defines a recursive
+            # variable)
+            if op == "+=":
+                op = "="
+
+        if op == "=":
+            var.is_recursive = True
+            var.value = val
+        elif op == ":=":
+            var.is_recursive = False
+            var.value = self._expand_whole(val, ())
+        else:  # op == "+="
+            # += does immediate expansion if the variable was last set
+            # with :=
+            var.value += " " + (val if var.is_recursive else
+                                self._expand_whole(val, ()))
+
+    def _expand_whole(self, s, args):
+        # Expands preprocessor macros in all of 's'. Used whenever we don't
+        # have to worry about delimiters. See _expand_macro() re. the 'args'
+        # parameter.
+        #
+        # Returns the expanded string.
+
+        i = 0
+        while 1:
+            i = s.find("$(", i)
+            if i == -1:
+                break
+            s, i = self._expand_macro(s, i, args)
+        return s
+
+    def _expand_name(self, s, i):
+        # Expands a symbol name starting at index 'i' in 's'.
+        #
+        # Returns the expanded name, the expanded 's' (including the part
+        # before the name), and the index of the first character in the next
+        # token after the name.
+
+        s, end_i = self._expand_name_iter(s, i)
+        name = s[i:end_i]
+        # isspace() is False for empty strings
+        if not name.strip():
+            # Avoid creating a Kconfig symbol with a blank name. It's almost
+            # guaranteed to be an error.
+            self._parse_error("macro expanded to blank string")
+
+        # Skip trailing whitespace
+        while end_i < len(s) and s[end_i].isspace():
+            end_i += 1
+
+        return name, s, end_i
+
+    def _expand_name_iter(self, s, i):
+        # Expands a symbol name starting at index 'i' in 's'.
+        #
+        # Returns the expanded 's' (including the part before the name) and the
+        # index of the first character after the expanded name in 's'.
+
+        while 1:
+            match = _name_special_search(s, i)
+
+            if match.group() != "$(":
+                return (s, match.start())
+            s, i = self._expand_macro(s, match.start(), ())
+
+    def _expand_str(self, s, i):
+        # Expands a quoted string starting at index 'i' in 's'. Handles both
+        # backslash escapes and macro expansion.
+        #
+        # Returns the expanded 's' (including the part before the string) and
+        # the index of the first character after the expanded string in 's'.
+
+        quote = s[i]
+        i += 1  # Skip over initial "/'
+        while 1:
+            match = _string_special_search(s, i)
+            if not match:
+                self._parse_error("unterminated string")
+
+
+            if match.group() == quote:
+                # Found the end of the string
+                return (s, match.end())
+
+            elif match.group() == "\\":
+                # Replace '\x' with 'x'. 'i' ends up pointing to the character
+                # after 'x', which allows macros to be canceled with '\$(foo)'.
+                i = match.end()
+                s = s[:match.start()] + s[i:]
+
+            elif match.group() == "$(":
+                # A macro call within the string
+                s, i = self._expand_macro(s, match.start(), ())
+
+            else:
+                # A ' quote within " quotes or vice versa
+                i += 1
+
+    def _expand_macro(self, s, i, args):
+        # Expands a macro starting at index 'i' in 's'. If this macro resulted
+        # from the expansion of another macro, 'args' holds the arguments
+        # passed to that macro.
+        #
+        # Returns the expanded 's' (including the part before the macro) and
+        # the index of the first character after the expanded macro in 's'.
+
+        res = s[:i]
+        i += 2  # Skip over "$("
+
+        arg_start = i  # Start of current macro argument
+        new_args = []  # Arguments of this macro call
+        nesting = 0  # Current parentheses nesting level
+
+        while 1:
+            match = _macro_special_search(s, i)
+            if not match:
+                self._parse_error("missing end parenthesis in macro expansion")
+
+
+            if match.group() == "(":
+                nesting += 1
+                i = match.end()
+
+            elif match.group() == ")":
+                if nesting:
+                    nesting -= 1
+                    i = match.end()
+                    continue
+
+                # Found the end of the macro
+
+                new_args.append(s[arg_start:match.start()])
+
+                # $(1) is replaced by the first argument to the function, etc.,
+                # provided at least that many arguments were passed
+
+                try:
+                    # Does the macro look like an integer, with a corresponding
+                    # argument? If so, expand it to the value of the argument.
+                    res += args[int(new_args[0])]
+                except (ValueError, IndexError):
+                    # Regular variables are just functions without arguments,
+                    # and also go through the function value path
+                    res += self._fn_val(new_args)
+
+                return (res + s[match.end():], len(res))
+
+            elif match.group() == ",":
+                i = match.end()
+                if nesting:
+                    continue
+
+                # Found the end of a macro argument
+                new_args.append(s[arg_start:match.start()])
+                arg_start = i
+
+            else:  # match.group() == "$("
+                # A nested macro call within the macro
+                s, i = self._expand_macro(s, match.start(), args)
+
+    def _fn_val(self, args):
+        # Returns the result of calling the function args[0] with the arguments
+        # args[1..len(args)-1]. Plain variables are treated as functions
+        # without arguments.
+
+        fn = args[0]
+
+        if fn in self.variables:
+            var = self.variables[fn]
+
+            if len(args) == 1:
+                # Plain variable
+                if var._n_expansions:
+                    self._parse_error("Preprocessor variable {} recursively "
+                                      "references itself".format(var.name))
+            elif var._n_expansions > 100:
+                # Allow functions to call themselves, but guess that functions
+                # that are overly recursive are stuck
+                self._parse_error("Preprocessor function {} seems stuck "
+                                  "in infinite recursion".format(var.name))
+
+            var._n_expansions += 1
+            res = self._expand_whole(self.variables[fn].value, args)
+            var._n_expansions -= 1
+            return res
+
+        if fn in self._functions:
+            # Built-in or user-defined function
+
+            py_fn, min_arg, max_arg = self._functions[fn]
+
+            if len(args) - 1 < min_arg or \
+               (max_arg is not None and len(args) - 1 > max_arg):
+
+                if min_arg == max_arg:
+                    expected_args = min_arg
+                elif max_arg is None:
+                    expected_args = "{} or more".format(min_arg)
+                else:
+                    expected_args = "{}-{}".format(min_arg, max_arg)
+
+                raise KconfigError("{}:{}: bad number of arguments in call "
+                                   "to {}, expected {}, got {}"
+                                   .format(self.filename, self.linenr, fn,
+                                           expected_args, len(args) - 1))
+
+            return py_fn(self, *args)
+
+        # Environment variables are tried last
+        if fn in os.environ:
+            self.env_vars.add(fn)
+            return os.environ[fn]
+
+        return ""
+
+    #
+    # Parsing
+    #
+
+    def _make_and(self, e1, e2):
+        # Constructs an AND (&&) expression. Performs trivial simplification.
+
+        if e1 is self.y:
+            return e2
+
+        if e2 is self.y:
+            return e1
+
+        if e1 is self.n or e2 is self.n:
+            return self.n
+
+        return (AND, e1, e2)
+
+    def _make_or(self, e1, e2):
+        # Constructs an OR (||) expression. Performs trivial simplification.
+
+        if e1 is self.n:
+            return e2
+
+        if e2 is self.n:
+            return e1
+
+        if e1 is self.y or e2 is self.y:
+            return self.y
+
+        return (OR, e1, e2)
+
+    def _parse_block(self, end_token, parent, prev):
+        # Parses a block, which is the contents of either a file or an if,
+        # menu, or choice statement.
+        #
+        # end_token:
+        #   The token that ends the block, e.g. _T_ENDIF ("endif") for ifs.
+        #   None for files.
+        #
+        # parent:
+        #   The parent menu node, corresponding to a menu, Choice, or 'if'.
+        #   'if's are flattened after parsing.
+        #
+        # prev:
+        #   The previous menu node. New nodes will be added after this one (by
+        #   modifying 'next' pointers).
+        #
+        #   'prev' is reused to parse a list of child menu nodes (for a menu or
+        #   Choice): After parsing the children, the 'next' pointer is assigned
+        #   to the 'list' pointer to "tilt up" the children above the node.
+        #
+        # Returns the final menu node in the block (or 'prev' if the block is
+        # empty). This allows chaining.
+
+        while self._next_line():
+            t0 = self._tokens[0]
+
+            if t0 is _T_CONFIG or t0 is _T_MENUCONFIG:
+                # The tokenizer allocates Symbol objects for us
+                sym = self._tokens[1]
+
+                if sym.__class__ is not Symbol or sym.is_constant:
+                    self._parse_error("missing or bad symbol name")
+
+                if self._tokens[2] is not None:
+                    self._trailing_tokens_error()
+
+                self.defined_syms.append(sym)
+
+                node = MenuNode()
+                node.kconfig = self
+                node.item = sym
+                node.is_menuconfig = (t0 is _T_MENUCONFIG)
+                node.prompt = node.help = node.list = None
+                node.parent = parent
+                node.filename = self.filename
+                node.linenr = self.linenr
+                node.include_path = self._include_path
+
+                sym.nodes.append(node)
+
+                self._parse_props(node)
+
+                if node.is_menuconfig and not node.prompt:
+                    self._warn("the menuconfig symbol {} has no prompt"
+                               .format(sym.name_and_loc))
+
+                # Equivalent to
+                #
+                #   prev.next = node
+                #   prev = node
+                #
+                # due to tricky Python semantics. The order matters.
+                prev.next = prev = node
+
+            elif t0 is None:
+                # Blank line
+                continue
+
+            elif t0 in _SOURCE_TOKENS:
+                pattern = self._expect_str_and_eol()
+
+                if t0 in _REL_SOURCE_TOKENS:
+                    # Relative source
+                    pattern = join(dirname(self.filename), pattern)
+
+                # - glob() doesn't support globbing relative to a directory, so
+                #   we need to prepend $srctree to 'pattern'. Use join()
+                #   instead of '+' so that an absolute path in 'pattern' is
+                #   preserved.
+                #
+                # - Sort the glob results to ensure a consistent ordering of
+                #   Kconfig symbols, which indirectly ensures a consistent
+                #   ordering in e.g. .config files
+                filenames = sorted(iglob(join(self._srctree_prefix, pattern)))
+
+                if not filenames and t0 in _OBL_SOURCE_TOKENS:
+                    raise KconfigError(
+                        "{}:{}: '{}' not found (in '{}'). Check that "
+                        "environment variables are set correctly (e.g. "
+                        "$srctree, which is {}). Also note that unset "
+                        "environment variables expand to the empty string."
+                        .format(self.filename, self.linenr, pattern,
+                                self._line.strip(),
+                                "set to '{}'".format(self.srctree)
+                                    if self.srctree else "unset or blank"))
+
+                for filename in filenames:
+                    self._enter_file(filename)
+                    prev = self._parse_block(None, parent, prev)
+                    self._leave_file()
+
+            elif t0 is end_token:
+                # Reached the end of the block. Terminate the final node and
+                # return it.
+
+                if self._tokens[1] is not None:
+                    self._trailing_tokens_error()
+
+                prev.next = None
+                return prev
+
+            elif t0 is _T_IF:
+                node = MenuNode()
+                node.item = node.prompt = None
+                node.parent = parent
+                node.dep = self._expect_expr_and_eol()
+
+                self._parse_block(_T_ENDIF, node, node)
+                node.list = node.next
+
+                prev.next = prev = node
+
+            elif t0 is _T_MENU:
+                node = MenuNode()
+                node.kconfig = self
+                node.item = t0  # _T_MENU == MENU
+                node.is_menuconfig = True
+                node.prompt = (self._expect_str_and_eol(), self.y)
+                node.visibility = self.y
+                node.parent = parent
+                node.filename = self.filename
+                node.linenr = self.linenr
+                node.include_path = self._include_path
+
+                self.menus.append(node)
+
+                self._parse_props(node)
+                self._parse_block(_T_ENDMENU, node, node)
+                node.list = node.next
+
+                prev.next = prev = node
+
+            elif t0 is _T_COMMENT:
+                node = MenuNode()
+                node.kconfig = self
+                node.item = t0  # _T_COMMENT == COMMENT
+                node.is_menuconfig = False
+                node.prompt = (self._expect_str_and_eol(), self.y)
+                node.list = None
+                node.parent = parent
+                node.filename = self.filename
+                node.linenr = self.linenr
+                node.include_path = self._include_path
+
+                self.comments.append(node)
+
+                self._parse_props(node)
+
+                prev.next = prev = node
+
+            elif t0 is _T_CHOICE:
+                if self._tokens[1] is None:
+                    choice = Choice()
+                    choice.direct_dep = self.n
+                else:
+                    # Named choice
+                    name = self._expect_str_and_eol()
+                    choice = self.named_choices.get(name)
+                    if not choice:
+                        choice = Choice()
+                        choice.name = name
+                        choice.direct_dep = self.n
+                        self.named_choices[name] = choice
+
+                self.choices.append(choice)
+
+                node = MenuNode()
+                node.kconfig = choice.kconfig = self
+                node.item = choice
+                node.is_menuconfig = True
+                node.prompt = node.help = None
+                node.parent = parent
+                node.filename = self.filename
+                node.linenr = self.linenr
+                node.include_path = self._include_path
+
+                choice.nodes.append(node)
+
+                self._parse_props(node)
+                self._parse_block(_T_ENDCHOICE, node, node)
+                node.list = node.next
+
+                prev.next = prev = node
+
+            elif t0 is _T_MAINMENU:
+                self.top_node.prompt = (self._expect_str_and_eol(), self.y)
+
+            else:
+                # A valid endchoice/endif/endmenu is caught by the 'end_token'
+                # check above
+                self._parse_error(
+                    "no corresponding 'choice'" if t0 is _T_ENDCHOICE else
+                    "no corresponding 'if'"     if t0 is _T_ENDIF else
+                    "no corresponding 'menu'"   if t0 is _T_ENDMENU else
+                    "unrecognized construct")
+
+        # End of file reached. Return the last node.
+
+        if end_token:
+            raise KconfigError(
+                "error: expected '{}' at end of '{}'"
+                .format("endchoice" if end_token is _T_ENDCHOICE else
+                        "endif"     if end_token is _T_ENDIF else
+                        "endmenu",
+                        self.filename))
+
+        return prev
+
+    def _parse_cond(self):
+        # Parses an optional 'if <expr>' construct and returns the parsed
+        # <expr>, or self.y if the next token is not _T_IF
+
+        expr = self._parse_expr(True) if self._check_token(_T_IF) else self.y
+
+        if self._tokens[self._tokens_i] is not None:
+            self._trailing_tokens_error()
+
+        return expr
+
+    def _parse_props(self, node):
+        # Parses and adds properties to the MenuNode 'node' (type, 'prompt',
+        # 'default's, etc.) Properties are later copied up to symbols and
+        # choices in a separate pass after parsing, in e.g.
+        # _add_props_to_sym().
+        #
+        # An older version of this code added properties directly to symbols
+        # and choices instead of to their menu nodes (and handled dependency
+        # propagation simultaneously), but that loses information on where a
+        # property is added when a symbol or choice is defined in multiple
+        # locations. Some Kconfig configuration systems rely heavily on such
+        # symbols, and better docs can be generated by keeping track of where
+        # properties are added.
+        #
+        # node:
+        #   The menu node we're parsing properties on
+
+        # Dependencies from 'depends on'. Will get propagated to the properties
+        # below.
+        node.dep = self.y
+
+        while self._next_line():
+            t0 = self._tokens[0]
+
+            if t0 in _TYPE_TOKENS:
+                # Relies on '_T_BOOL is BOOL', etc., to save a conversion
+                self._set_type(node.item, t0)
+                if self._tokens[1] is not None:
+                    self._parse_prompt(node)
+
+            elif t0 is _T_DEPENDS:
+                if not self._check_token(_T_ON):
+                    self._parse_error("expected 'on' after 'depends'")
+
+                node.dep = self._make_and(node.dep,
+                                          self._expect_expr_and_eol())
+
+            elif t0 is _T_HELP:
+                self._parse_help(node)
+
+            elif t0 is _T_SELECT:
+                if node.item.__class__ is not Symbol:
+                    self._parse_error("only symbols can select")
+
+                node.selects.append((self._expect_nonconst_sym(),
+                                     self._parse_cond()))
+
+            elif t0 is None:
+                # Blank line
+                continue
+
+            elif t0 is _T_DEFAULT:
+                node.defaults.append((self._parse_expr(False),
+                                      self._parse_cond()))
+
+            elif t0 in _DEF_TOKEN_TO_TYPE:
+                self._set_type(node.item, _DEF_TOKEN_TO_TYPE[t0])
+                node.defaults.append((self._parse_expr(False),
+                                      self._parse_cond()))
+
+            elif t0 is _T_PROMPT:
+                self._parse_prompt(node)
+
+            elif t0 is _T_RANGE:
+                node.ranges.append((self._expect_sym(), self._expect_sym(),
+                                    self._parse_cond()))
+
+            elif t0 is _T_IMPLY:
+                if node.item.__class__ is not Symbol:
+                    self._parse_error("only symbols can imply")
+
+                node.implies.append((self._expect_nonconst_sym(),
+                                     self._parse_cond()))
+
+            elif t0 is _T_VISIBLE:
+                if not self._check_token(_T_IF):
+                    self._parse_error("expected 'if' after 'visible'")
+
+                node.visibility = self._make_and(node.visibility,
+                                                 self._expect_expr_and_eol())
+
+            elif t0 is _T_OPTION:
+                if self._check_token(_T_ENV):
+                    if not self._check_token(_T_EQUAL):
+                        self._parse_error("expected '=' after 'env'")
+
+                    env_var = self._expect_str_and_eol()
+                    node.item.env_var = env_var
+
+                    if env_var in os.environ:
+                        node.defaults.append(
+                            (self._lookup_const_sym(os.environ[env_var]),
+                             self.y))
+                    else:
+                        self._warn("{1} has 'option env=\"{0}\"', "
+                                   "but the environment variable {0} is not "
+                                   "set".format(node.item.name, env_var),
+                                   self.filename, self.linenr)
+
+                    if env_var != node.item.name:
+                        self._warn("Kconfiglib expands environment variables "
+                                   "in strings directly, meaning you do not "
+                                   "need 'option env=...' \"bounce\" symbols. "
+                                   "For compatibility with the C tools, "
+                                   "rename {} to {} (so that the symbol name "
+                                   "matches the environment variable name)."
+                                   .format(node.item.name, env_var),
+                                   self.filename, self.linenr)
+
+                elif self._check_token(_T_DEFCONFIG_LIST):
+                    if not self.defconfig_list:
+                        self.defconfig_list = node.item
+                    else:
+                        self._warn("'option defconfig_list' set on multiple "
+                                   "symbols ({0} and {1}). Only {0} will be "
+                                   "used.".format(self.defconfig_list.name,
+                                                  node.item.name),
+                                   self.filename, self.linenr)
+
+                elif self._check_token(_T_MODULES):
+                    # To reduce warning spam, only warn if 'option modules' is
+                    # set on some symbol that isn't MODULES, which should be
+                    # safe. I haven't run into any projects that make use
+                    # modules besides the kernel yet, and there it's likely to
+                    # keep being called "MODULES".
+                    if node.item is not self.modules:
+                        self._warn("the 'modules' option is not supported. "
+                                   "Let me know if this is a problem for you, "
+                                   "as it wouldn't be that hard to implement. "
+                                   "Note that modules are supported -- "
+                                   "Kconfiglib just assumes the symbol name "
+                                   "MODULES, like older versions of the C "
+                                   "implementation did when 'option modules' "
+                                   "wasn't used.",
+                                   self.filename, self.linenr)
+
+                elif self._check_token(_T_ALLNOCONFIG_Y):
+                    if node.item.__class__ is not Symbol:
+                        self._parse_error("the 'allnoconfig_y' option is only "
+                                          "valid for symbols")
+
+                    node.item.is_allnoconfig_y = True
+
+                else:
+                    self._parse_error("unrecognized option")
+
+            elif t0 is _T_OPTIONAL:
+                if node.item.__class__ is not Choice:
+                    self._parse_error('"optional" is only valid for choices')
+
+                node.item.is_optional = True
+
+            else:
+                # Reuse the tokens for the non-property line later
+                self._reuse_tokens = True
+                return
+
+    def _set_type(self, sc, new_type):
+        # Sets the type of 'sc' (symbol or choice) to 'new_type'
+
+        # UNKNOWN is falsy
+        if sc.orig_type and sc.orig_type is not new_type:
+            self._warn("{} defined with multiple types, {} will be used"
+                       .format(sc.name_and_loc, TYPE_TO_STR[new_type]))
+
+        sc.orig_type = new_type
+
+    def _parse_prompt(self, node):
+        # 'prompt' properties override each other within a single definition of
+        # a symbol, but additional prompts can be added by defining the symbol
+        # multiple times
+
+        if node.prompt:
+            self._warn(node.item.name_and_loc +
+                       " defined with multiple prompts in single location")
+
+        prompt = self._tokens[1]
+        self._tokens_i = 2
+
+        if prompt.__class__ is not str:
+            self._parse_error("expected prompt string")
+
+        if prompt != prompt.strip():
+            self._warn(node.item.name_and_loc +
+                       " has leading or trailing whitespace in its prompt")
+
+            # This avoid issues for e.g. reStructuredText documentation, where
+            # '*prompt *' is invalid
+            prompt = prompt.strip()
+
+        node.prompt = (prompt, self._parse_cond())
+
+    def _parse_help(self, node):
+        if node.help is not None:
+            self._warn(node.item.name_and_loc + " defined with more than "
+                       "one help text -- only the last one will be used")
+
+        # Micro-optimization. This code is pretty hot.
+        readline = self._readline
+
+        # Find first non-blank (not all-space) line and get its
+        # indentation
+
+        while 1:
+            line = readline()
+            self.linenr += 1
+            if not line:
+                self._empty_help(node, line)
+                return
+            if not line.isspace():
+                break
+
+        len_ = len  # Micro-optimization
+
+        # Use a separate 'expline' variable here and below to avoid stomping on
+        # any tabs people might've put deliberately into the first line after
+        # the help text
+        expline = line.expandtabs()
+        indent = len_(expline) - len_(expline.lstrip())
+        if not indent:
+            self._empty_help(node, line)
+            return
+
+        # The help text goes on till the first non-blank line with less indent
+        # than the first line
+
+        # Add the first line
+        lines = [expline[indent:]]
+        add_line = lines.append  # Micro-optimization
+
+        while 1:
+            line = readline()
+            if line.isspace():
+                # No need to preserve the exact whitespace in these
+                add_line("\n")
+            elif not line:
+                # End of file
+                break
+            else:
+                expline = line.expandtabs()
+                if len_(expline) - len_(expline.lstrip()) < indent:
+                    break
+                add_line(expline[indent:])
+
+        self.linenr += len_(lines)
+        node.help = "".join(lines).rstrip()
+        if line:
+            self._line_after_help(line)
+
+    def _empty_help(self, node, line):
+        self._warn(node.item.name_and_loc +
+                   " has 'help' but empty help text")
+        node.help = ""
+        if line:
+            self._line_after_help(line)
+
+    def _parse_expr(self, transform_m):
+        # Parses an expression from the tokens in Kconfig._tokens using a
+        # simple top-down approach. See the module docstring for the expression
+        # format.
+        #
+        # transform_m:
+        #   True if m should be rewritten to m && MODULES. See the
+        #   Kconfig.eval_string() documentation.
+
+        # Grammar:
+        #
+        #   expr:     and_expr ['||' expr]
+        #   and_expr: factor ['&&' and_expr]
+        #   factor:   <symbol> ['='/'!='/'<'/... <symbol>]
+        #             '!' factor
+        #             '(' expr ')'
+        #
+        # It helps to think of the 'expr: and_expr' case as a single-operand OR
+        # (no ||), and of the 'and_expr: factor' case as a single-operand AND
+        # (no &&). Parsing code is always a bit tricky.
+
+        # Mind dump: parse_factor() and two nested loops for OR and AND would
+        # work as well. The straightforward implementation there gives a
+        # (op, (op, (op, A, B), C), D) parse for A op B op C op D. Representing
+        # expressions as (op, [list of operands]) instead goes nicely with that
+        # version, but is wasteful for short expressions and complicates
+        # expression evaluation and other code that works on expressions (more
+        # complicated code likely offsets any performance gain from less
+        # recursion too). If we also try to optimize the list representation by
+        # merging lists when possible (e.g. when ANDing two AND expressions),
+        # we end up allocating a ton of lists instead of reusing expressions,
+        # which is bad.
+
+        and_expr = self._parse_and_expr(transform_m)
+
+        # Return 'and_expr' directly if we have a "single-operand" OR.
+        # Otherwise, parse the expression on the right and make an OR node.
+        # This turns A || B || C || D into (OR, A, (OR, B, (OR, C, D))).
+        return and_expr if not self._check_token(_T_OR) else \
+            (OR, and_expr, self._parse_expr(transform_m))
+
+    def _parse_and_expr(self, transform_m):
+        factor = self._parse_factor(transform_m)
+
+        # Return 'factor' directly if we have a "single-operand" AND.
+        # Otherwise, parse the right operand and make an AND node. This turns
+        # A && B && C && D into (AND, A, (AND, B, (AND, C, D))).
+        return factor if not self._check_token(_T_AND) else \
+            (AND, factor, self._parse_and_expr(transform_m))
+
+    def _parse_factor(self, transform_m):
+        token = self._tokens[self._tokens_i]
+        self._tokens_i += 1
+
+        if token.__class__ is Symbol:
+            # Plain symbol or relation
+
+            if self._tokens[self._tokens_i] not in _RELATIONS:
+                # Plain symbol
+
+                # For conditional expressions ('depends on <expr>',
+                # '... if <expr>', etc.), m is rewritten to m && MODULES.
+                if transform_m and token is self.m:
+                    return (AND, self.m, self.modules)
+
+                return token
+
+            # Relation
+            #
+            # _T_EQUAL, _T_UNEQUAL, etc., deliberately have the same values as
+            # EQUAL, UNEQUAL, etc., so we can just use the token directly
+            self._tokens_i += 1
+            return (self._tokens[self._tokens_i - 1], token,
+                    self._expect_sym())
+
+        if token is _T_NOT:
+            # token == _T_NOT == NOT
+            return (token, self._parse_factor(transform_m))
+
+        if token is _T_OPEN_PAREN:
+            expr_parse = self._parse_expr(transform_m)
+            if self._check_token(_T_CLOSE_PAREN):
+                return expr_parse
+
+        self._parse_error("malformed expression")
+
+    #
+    # Caching and invalidation
+    #
+
+    def _build_dep(self):
+        # Populates the Symbol/Choice._dependents sets, which contain all other
+        # items (symbols and choices) that immediately depend on the item in
+        # the sense that changing the value of the item might affect the value
+        # of the dependent items. This is used for caching/invalidation.
+        #
+        # The calculated sets might be larger than necessary as we don't do any
+        # complex analysis of the expressions.
+
+        depend_on = _depend_on  # Micro-optimization
+
+        # Only calculate _dependents for defined symbols. Constant and
+        # undefined symbols could theoretically be selected/implied, but it
+        # wouldn't change their value, so it's not a true dependency.
+        for sym in self.unique_defined_syms:
+            # Symbols depend on the following:
+
+            # The prompt conditions
+            for node in sym.nodes:
+                if node.prompt:
+                    depend_on(sym, node.prompt[1])
+
+            # The default values and their conditions
+            for value, cond in sym.defaults:
+                depend_on(sym, value)
+                depend_on(sym, cond)
+
+            # The reverse and weak reverse dependencies
+            depend_on(sym, sym.rev_dep)
+            depend_on(sym, sym.weak_rev_dep)
+
+            # The ranges along with their conditions
+            for low, high, cond in sym.ranges:
+                depend_on(sym, low)
+                depend_on(sym, high)
+                depend_on(sym, cond)
+
+            # The direct dependencies. This is usually redundant, as the direct
+            # dependencies get propagated to properties, but it's needed to get
+            # invalidation solid for 'imply', which only checks the direct
+            # dependencies (even if there are no properties to propagate it
+            # to).
+            depend_on(sym, sym.direct_dep)
+
+            # In addition to the above, choice symbols depend on the choice
+            # they're in, but that's handled automatically since the Choice is
+            # propagated to the conditions of the properties before
+            # _build_dep() runs.
+
+        for choice in self.unique_choices:
+            # Choices depend on the following:
+
+            # The prompt conditions
+            for node in choice.nodes:
+                if node.prompt:
+                    depend_on(choice, node.prompt[1])
+
+            # The default symbol conditions
+            for _, cond in choice.defaults:
+                depend_on(choice, cond)
+
+    def _add_choice_deps(self):
+        # Choices also depend on the choice symbols themselves, because the
+        # y-mode selection of the choice might change if a choice symbol's
+        # visibility changes.
+        #
+        # We add these dependencies separately after dependency loop detection.
+        # The invalidation algorithm can handle the resulting
+        # <choice symbol> <-> <choice> dependency loops, but they make loop
+        # detection awkward.
+
+        for choice in self.unique_choices:
+            for sym in choice.syms:
+                sym._dependents.add(choice)
+
+    def _invalidate_all(self):
+        # Undefined symbols never change value and don't need to be
+        # invalidated, so we can just iterate over defined symbols.
+        # Invalidating constant symbols would break things horribly.
+        for sym in self.unique_defined_syms:
+            sym._invalidate()
+
+        for choice in self.unique_choices:
+            choice._invalidate()
+
+    #
+    # Post-parsing menu tree processing, including dependency propagation and
+    # implicit submenu creation
+    #
+
+    def _finalize_node(self, node, visible_if):
+        # Finalizes a menu node and its children:
+        #
+        #  - Copies properties from menu nodes up to their contained
+        #    symbols/choices
+        #
+        #  - Propagates dependencies from parent to child nodes
+        #
+        #  - Creates implicit menus (see kconfig-language.txt)
+        #
+        #  - Removes 'if' nodes
+        #
+        #  - Sets 'choice' types and registers choice symbols
+        #
+        # menu_finalize() in the C implementation is similar.
+        #
+        # node:
+        #   The menu node to finalize. This node and its children will have
+        #   been finalized when the function returns, and any implicit menus
+        #   will have been created.
+        #
+        # visible_if:
+        #   Dependencies from 'visible if' on parent menus. These are added to
+        #   the prompts of symbols and choices.
+
+        if node.item.__class__ is Symbol:
+            # Copy defaults, ranges, selects, and implies to the Symbol
+            self._add_props_to_sym(node)
+
+            # Find any items that should go in an implicit menu rooted at the
+            # symbol
+            cur = node
+            while cur.next and _auto_menu_dep(node, cur.next):
+                # This makes implicit submenu creation work recursively, with
+                # implicit menus inside implicit menus
+                self._finalize_node(cur.next, visible_if)
+                cur = cur.next
+                cur.parent = node
+
+            if cur is not node:
+                # Found symbols that should go in an implicit submenu. Tilt
+                # them up above us.
+                node.list = node.next
+                node.next = cur.next
+                cur.next = None
+
+        elif node.list:
+            # The menu node is a choice, menu, or if. Finalize each child node.
+
+            if node.item is MENU:
+                visible_if = self._make_and(visible_if, node.visibility)
+
+            # Propagate the menu node's dependencies to each child menu node.
+            #
+            # This needs to go before the recursive _finalize_node() call so
+            # that implicit submenu creation can look ahead at dependencies.
+            self._propagate_deps(node, visible_if)
+
+            # Finalize the children
+            cur = node.list
+            while cur:
+                self._finalize_node(cur, visible_if)
+                cur = cur.next
+
+        if node.list:
+            # node's children have been individually finalized. Do final steps
+            # to finalize this "level" in the menu tree.
+            _flatten(node.list)
+            _remove_ifs(node)
+
+        # Empty choices (node.list None) are possible, so this needs to go
+        # outside
+        if node.item.__class__ is Choice:
+            # Add the node's non-node-specific properties to the choice, like
+            # _add_props_to_sym() does
+            choice = node.item
+            choice.direct_dep = self._make_or(choice.direct_dep, node.dep)
+            choice.defaults += node.defaults
+
+            _finalize_choice(node)
+
+    def _propagate_deps(self, node, visible_if):
+        # Propagates 'node's dependencies to its child menu nodes
+
+        # If the parent node holds a Choice, we use the Choice itself as the
+        # parent dependency. This makes sense as the value (mode) of the choice
+        # limits the visibility of the contained choice symbols. The C
+        # implementation works the same way.
+        #
+        # Due to the similar interface, Choice works as a drop-in replacement
+        # for Symbol here.
+        basedep = node.item if node.item.__class__ is Choice else node.dep
+
+        cur = node.list
+        while cur:
+            dep = cur.dep = self._make_and(cur.dep, basedep)
+
+            if cur.item.__class__ in _SYMBOL_CHOICE:
+                # Propagate 'visible if' and dependencies to the prompt
+                if cur.prompt:
+                    cur.prompt = (cur.prompt[0],
+                                  self._make_and(
+                                      cur.prompt[1],
+                                      self._make_and(visible_if, dep)))
+
+                # Propagate dependencies to defaults
+                if cur.defaults:
+                    cur.defaults = [(default, self._make_and(cond, dep))
+                                    for default, cond in cur.defaults]
+
+                # Propagate dependencies to ranges
+                if cur.ranges:
+                    cur.ranges = [(low, high, self._make_and(cond, dep))
+                                  for low, high, cond in cur.ranges]
+
+                # Propagate dependencies to selects
+                if cur.selects:
+                    cur.selects = [(target, self._make_and(cond, dep))
+                                   for target, cond in cur.selects]
+
+                # Propagate dependencies to implies
+                if cur.implies:
+                    cur.implies = [(target, self._make_and(cond, dep))
+                                   for target, cond in cur.implies]
+
+            elif cur.prompt:  # Not a symbol/choice
+                # Propagate dependencies to the prompt. 'visible if' is only
+                # propagated to symbols/choices.
+                cur.prompt = (cur.prompt[0],
+                              self._make_and(cur.prompt[1], dep))
+
+            cur = cur.next
+
+    def _add_props_to_sym(self, node):
+        # Copies properties from the menu node 'node' up to its contained
+        # symbol, and adds (weak) reverse dependencies to selected/implied
+        # symbols.
+        #
+        # This can't be rolled into _propagate_deps(), because that function
+        # traverses the menu tree roughly breadth-first, meaning properties on
+        # symbols defined in multiple locations could end up in the wrong
+        # order.
+
+        sym = node.item
+
+        # See the Symbol class docstring
+        sym.direct_dep = self._make_or(sym.direct_dep, node.dep)
+
+        sym.defaults += node.defaults
+        sym.ranges += node.ranges
+        sym.selects += node.selects
+        sym.implies += node.implies
+
+        # Modify the reverse dependencies of the selected symbol
+        for target, cond in node.selects:
+            target.rev_dep = self._make_or(
+                target.rev_dep,
+                self._make_and(sym, cond))
+
+        # Modify the weak reverse dependencies of the implied
+        # symbol
+        for target, cond in node.implies:
+            target.weak_rev_dep = self._make_or(
+                target.weak_rev_dep,
+                self._make_and(sym, cond))
+
+    #
+    # Misc.
+    #
+
+    def _check_sym_sanity(self):
+        # Checks various symbol properties that are handiest to check after
+        # parsing. Only generates errors and warnings.
+
+        def num_ok(sym, type_):
+            # Returns True if the (possibly constant) symbol 'sym' is valid as a value
+            # for a symbol of type type_ (INT or HEX)
+
+            # 'not sym.nodes' implies a constant or undefined symbol, e.g. a plain
+            # "123"
+            if not sym.nodes:
+                return _is_base_n(sym.name, _TYPE_TO_BASE[type_])
+
+            return sym.orig_type is type_
+
+        for sym in self.unique_defined_syms:
+            if sym.orig_type in _BOOL_TRISTATE:
+                # A helper function could be factored out here, but keep it
+                # speedy/straightforward
+
+                for target_sym, _ in sym.selects:
+                    if target_sym.orig_type not in _BOOL_TRISTATE_UNKNOWN:
+                        self._warn("{} selects the {} symbol {}, which is not "
+                                   "bool or tristate"
+                                   .format(sym.name_and_loc,
+                                           TYPE_TO_STR[target_sym.orig_type],
+                                           target_sym.name_and_loc))
+
+                for target_sym, _ in sym.implies:
+                    if target_sym.orig_type not in _BOOL_TRISTATE_UNKNOWN:
+                        self._warn("{} implies the {} symbol {}, which is not "
+                                   "bool or tristate"
+                                   .format(sym.name_and_loc,
+                                           TYPE_TO_STR[target_sym.orig_type],
+                                           target_sym.name_and_loc))
+
+            elif sym.orig_type:  # STRING/INT/HEX
+                for default, _ in sym.defaults:
+                    if default.__class__ is not Symbol:
+                        raise KconfigError(
+                            "the {} symbol {} has a malformed default {} -- "
+                            "expected a single symbol"
+                            .format(TYPE_TO_STR[sym.orig_type],
+                                    sym.name_and_loc, expr_str(default)))
+
+                    if sym.orig_type is STRING:
+                        if not default.is_constant and not default.nodes and \
+                           not default.name.isupper():
+                            # 'default foo' on a string symbol could be either a symbol
+                            # reference or someone leaving out the quotes. Guess that
+                            # the quotes were left out if 'foo' isn't all-uppercase
+                            # (and no symbol named 'foo' exists).
+                            self._warn("style: quotes recommended around "
+                                       "default value for string symbol "
+                                       + sym.name_and_loc)
+
+                    elif not num_ok(default, sym.orig_type):  # INT/HEX
+                        self._warn("the {0} symbol {1} has a non-{0} default {2}"
+                                   .format(TYPE_TO_STR[sym.orig_type],
+                                           sym.name_and_loc,
+                                           default.name_and_loc))
+
+                if sym.selects or sym.implies:
+                    self._warn("the {} symbol {} has selects or implies"
+                               .format(TYPE_TO_STR[sym.orig_type],
+                                       sym.name_and_loc))
+
+            else:  # UNKNOWN
+                self._warn("{} defined without a type"
+                           .format(sym.name_and_loc))
+
+
+            if sym.ranges:
+                if sym.orig_type not in _INT_HEX:
+                    self._warn(
+                        "the {} symbol {} has ranges, but is not int or hex"
+                        .format(TYPE_TO_STR[sym.orig_type],
+                                sym.name_and_loc))
+                else:
+                    for low, high, _ in sym.ranges:
+                        if not num_ok(low, sym.orig_type) or \
+                           not num_ok(high, sym.orig_type):
+
+                            self._warn("the {0} symbol {1} has a non-{0} "
+                                       "range [{2}, {3}]"
+                                       .format(TYPE_TO_STR[sym.orig_type],
+                                               sym.name_and_loc,
+                                               low.name_and_loc,
+                                               high.name_and_loc))
+
+    def _check_choice_sanity(self):
+        # Checks various choice properties that are handiest to check after
+        # parsing. Only generates errors and warnings.
+
+        def warn_select_imply(sym, expr, expr_type):
+            msg = "the choice symbol {} is {} by the following symbols, but " \
+                  "select/imply has no effect on choice symbols" \
+                  .format(sym.name_and_loc, expr_type)
+
+            # si = select/imply
+            for si in split_expr(expr, OR):
+                msg += "\n - " + split_expr(si, AND)[0].name_and_loc
+
+            self._warn(msg)
+
+        for choice in self.unique_choices:
+            if choice.orig_type not in _BOOL_TRISTATE:
+                self._warn("{} defined with type {}"
+                           .format(choice.name_and_loc,
+                                   TYPE_TO_STR[choice.orig_type]))
+
+            for node in choice.nodes:
+                if node.prompt:
+                    break
+            else:
+                self._warn(choice.name_and_loc + " defined without a prompt")
+
+            for default, _ in choice.defaults:
+                if default.__class__ is not Symbol:
+                    raise KconfigError(
+                        "{} has a malformed default {}"
+                        .format(choice.name_and_loc, expr_str(default)))
+
+                if default.choice is not choice:
+                    self._warn("the default selection {} of {} is not "
+                               "contained in the choice"
+                               .format(default.name_and_loc,
+                                       choice.name_and_loc))
+
+            for sym in choice.syms:
+                if sym.defaults:
+                    self._warn("default on the choice symbol {} will have "
+                               "no effect, as defaults do not affect choice "
+                               "symbols".format(sym.name_and_loc))
+
+                if sym.rev_dep is not sym.kconfig.n:
+                    warn_select_imply(sym, sym.rev_dep, "selected")
+
+                if sym.weak_rev_dep is not sym.kconfig.n:
+                    warn_select_imply(sym, sym.weak_rev_dep, "implied")
+
+                for node in sym.nodes:
+                    if node.parent.item is choice:
+                        if not node.prompt:
+                            self._warn("the choice symbol {} has no prompt"
+                                       .format(sym.name_and_loc))
+
+                    elif node.prompt:
+                        self._warn("the choice symbol {} is defined with a "
+                                   "prompt outside the choice"
+                                   .format(sym.name_and_loc))
+
+    def _parse_error(self, msg):
+        raise KconfigError("{}error: couldn't parse '{}': {}".format(
+            "" if self.filename is None else
+                "{}:{}: ".format(self.filename, self.linenr),
+            self._line.strip(), msg))
+
+    def _trailing_tokens_error(self):
+        self._parse_error("extra tokens at end of line")
+
+    def _open(self, filename, mode):
+        # open() wrapper:
+        #
+        # - Enable universal newlines mode on Python 2 to ease
+        #   interoperability between Linux and Windows. It's already the
+        #   default on Python 3.
+        #
+        #   The "U" flag would currently work for both Python 2 and 3, but it's
+        #   deprecated on Python 3, so play it future-safe.
+        #
+        #   io.open() defaults to universal newlines on Python 2 (and is an
+        #   alias for open() on Python 3), but it returns 'unicode' strings and
+        #   slows things down:
+        #
+        #     Parsing x86 Kconfigs on Python 2
+        #
+        #     with open(..., "rU"):
+        #
+        #       real  0m0.930s
+        #       user  0m0.905s
+        #       sys   0m0.025s
+        #
+        #     with io.open():
+        #
+        #       real  0m1.069s
+        #       user  0m1.040s
+        #       sys   0m0.029s
+        #
+        #   There's no appreciable performance difference between "r" and
+        #   "rU" for parsing performance on Python 2.
+        #
+        # - For Python 3, force the encoding. Forcing the encoding on Python 2
+        #   turns strings into Unicode strings, which gets messy. Python 2
+        #   doesn't decode regular strings anyway.
+        return open(filename, "rU" if mode == "r" else mode) if _IS_PY2 else \
+               open(filename, mode, encoding=self._encoding)
+
+    def _check_undef_syms(self):
+        # Prints warnings for all references to undefined symbols within the
+        # Kconfig files
+
+        def is_num(s):
+            # Returns True if the string 's' looks like a number.
+            #
+            # Internally, all operands in Kconfig are symbols, only undefined symbols
+            # (which numbers usually are) get their name as their value.
+            #
+            # Only hex numbers that start with 0x/0X are classified as numbers.
+            # Otherwise, symbols whose names happen to contain only the letters A-F
+            # would trigger false positives.
+
+            try:
+                int(s)
+            except ValueError:
+                if not s.startswith(("0x", "0X")):
+                    return False
+
+                try:
+                    int(s, 16)
+                except ValueError:
+                    return False
+
+            return True
+
+        for sym in (self.syms.viewvalues if _IS_PY2 else self.syms.values)():
+            # - sym.nodes empty means the symbol is undefined (has no
+            #   definition locations)
+            #
+            # - Due to Kconfig internals, numbers show up as undefined Kconfig
+            #   symbols, but shouldn't be flagged
+            #
+            # - The MODULES symbol always exists
+            if not sym.nodes and not is_num(sym.name) and \
+               sym.name != "MODULES":
+
+                msg = "undefined symbol {}:".format(sym.name)
+                for node in self.node_iter():
+                    if sym in node.referenced:
+                        msg += "\n\n- Referenced at {}:{}:\n\n{}" \
+                               .format(node.filename, node.linenr, node)
+                self._warn(msg)
+
+    def _warn(self, msg, filename=None, linenr=None):
+        # For printing general warnings
+
+        if not self.warn:
+            return
+
+        msg = "warning: " + msg
+        if filename is not None:
+            msg = "{}:{}: {}".format(filename, linenr, msg)
+
+        self.warnings.append(msg)
+        if self.warn_to_stderr:
+            sys.stderr.write(msg + "\n")
+
+
+class Symbol(object):
+    """
+    Represents a configuration symbol:
+
+      (menu)config FOO
+          ...
+
+    The following attributes are available. They should be viewed as read-only,
+    and some are implemented through @property magic (but are still efficient
+    to access due to internal caching).
+
+    Note: Prompts, help texts, and locations are stored in the Symbol's
+    MenuNode(s) rather than in the Symbol itself. Check the MenuNode class and
+    the Symbol.nodes attribute. This organization matches the C tools.
+
+    name:
+      The name of the symbol, e.g. "FOO" for 'config FOO'.
+
+    type:
+      The type of the symbol. One of BOOL, TRISTATE, STRING, INT, HEX, UNKNOWN.
+      UNKNOWN is for undefined symbols, (non-special) constant symbols, and
+      symbols defined without a type.
+
+      When running without modules (MODULES having the value n), TRISTATE
+      symbols magically change type to BOOL. This also happens for symbols
+      within choices in "y" mode. This matches the C tools, and makes sense for
+      menuconfig-like functionality.
+
+    orig_type:
+      The type as given in the Kconfig file, without any magic applied. Used
+      when printing the symbol.
+
+    tri_value:
+      The tristate value of the symbol as an integer. One of 0, 1, 2,
+      representing n, m, y. Always 0 (n) for non-bool/tristate symbols.
+
+      This is the symbol value that's used outside of relation expressions
+      (A, !A, A && B, A || B).
+
+    str_value:
+      The value of the symbol as a string. Gives the value for string/int/hex
+      symbols. For bool/tristate symbols, gives "n", "m", or "y".
+
+      This is the symbol value that's used in relational expressions
+      (A = B, A != B, etc.)
+
+      Gotcha: For int/hex symbols, the exact format of the value is often
+      preserved (e.g. when writing a .config file), hence why you can't get it
+      directly as an int. Do int(int_sym.str_value) or
+      int(hex_sym.str_value, 16) to get the integer value.
+
+    user_value:
+      The user value of the symbol. None if no user value has been assigned
+      (via Kconfig.load_config() or Symbol.set_value()).
+
+      Holds 0, 1, or 2 for bool/tristate symbols, and a string for the other
+      symbol types.
+
+      WARNING: Do not assign directly to this. It will break things. Use
+      Symbol.set_value().
+
+    assignable:
+      A tuple containing the tristate user values that can currently be
+      assigned to the symbol (that would be respected), ordered from lowest (0,
+      representing n) to highest (2, representing y). This corresponds to the
+      selections available in the menuconfig interface. The set of assignable
+      values is calculated from the symbol's visibility and selects/implies.
+
+      Returns the empty set for non-bool/tristate symbols and for symbols with
+      visibility n. The other possible values are (0, 2), (0, 1, 2), (1, 2),
+      (1,), and (2,). A (1,) or (2,) result means the symbol is visible but
+      "locked" to m or y through a select, perhaps in combination with the
+      visibility. menuconfig represents this as -M- and -*-, respectively.
+
+      For string/hex/int symbols, check if Symbol.visibility is non-0 (non-n)
+      instead to determine if the value can be changed.
+
+      Some handy 'assignable' idioms:
+
+        # Is 'sym' an assignable (visible) bool/tristate symbol?
+        if sym.assignable:
+            # What's the highest value it can be assigned? [-1] in Python
+            # gives the last element.
+            sym_high = sym.assignable[-1]
+
+            # The lowest?
+            sym_low = sym.assignable[0]
+
+            # Can the symbol be set to at least m?
+            if sym.assignable[-1] >= 1:
+                ...
+
+        # Can the symbol be set to m?
+        if 1 in sym.assignable:
+            ...
+
+    visibility:
+      The visibility of the symbol. One of 0, 1, 2, representing n, m, y. See
+      the module documentation for an overview of symbol values and visibility.
+
+    config_string:
+      The .config assignment string that would get written out for the symbol
+      by Kconfig.write_config(). Returns the empty string if no .config
+      assignment would get written out.
+
+      In general, visible symbols, symbols with (active) defaults, and selected
+      symbols get written out. This includes all non-n-valued bool/tristate
+      symbols, and all visible string/int/hex symbols.
+
+      Symbols with the (no longer needed) 'option env=...' option generate no
+      configuration output, and neither does the special
+      'option defconfig_list' symbol.
+
+      Tip: This field is useful when generating custom configuration output,
+      even for non-.config-like formats. To write just the symbols that would
+      get written out to .config files, do this:
+
+        if sym.config_string:
+            *Write symbol, e.g. by looking sym.str_value*
+
+      This is a superset of the symbols written out by write_autoconf().
+      That function skips all n-valued symbols.
+
+      There usually won't be any great harm in just writing all symbols either,
+      though you might get some special symbols and possibly some "redundant"
+      n-valued symbol entries in there.
+
+    name_and_loc:
+      Holds a string like
+
+        "MY_SYMBOL (defined at foo/Kconfig:12, bar/Kconfig:14)"
+
+      , giving the name of the symbol and its definition location(s).
+
+      If the symbol is undefined, the location is given as "(undefined)".
+
+    nodes:
+      A list of MenuNodes for this symbol. Will contain a single MenuNode for
+      most symbols. Undefined and constant symbols have an empty nodes list.
+      Symbols defined in multiple locations get one node for each location.
+
+    choice:
+      Holds the parent Choice for choice symbols, and None for non-choice
+      symbols. Doubles as a flag for whether a symbol is a choice symbol.
+
+    defaults:
+      List of (default, cond) tuples for the symbol's 'default' properties. For
+      example, 'default A && B if C || D' is represented as
+      ((AND, A, B), (OR, C, D)). If no condition was given, 'cond' is
+      self.kconfig.y.
+
+      Note that 'depends on' and parent dependencies are propagated to
+      'default' conditions.
+
+    selects:
+      List of (symbol, cond) tuples for the symbol's 'select' properties. For
+      example, 'select A if B && C' is represented as (A, (AND, B, C)). If no
+      condition was given, 'cond' is self.kconfig.y.
+
+      Note that 'depends on' and parent dependencies are propagated to 'select'
+      conditions.
+
+    implies:
+      Like 'selects', for imply.
+
+    ranges:
+      List of (low, high, cond) tuples for the symbol's 'range' properties. For
+      example, 'range 1 2 if A' is represented as (1, 2, A). If there is no
+      condition, 'cond' is self.kconfig.y.
+
+      Note that 'depends on' and parent dependencies are propagated to 'range'
+      conditions.
+
+      Gotcha: 1 and 2 above will be represented as (undefined) Symbols rather
+      than plain integers. Undefined symbols get their name as their string
+      value, so this works out. The C tools work the same way.
+
+    orig_defaults:
+    orig_selects:
+    orig_implies:
+    orig_ranges:
+      See the corresponding attributes on the MenuNode class.
+
+    rev_dep:
+      Reverse dependency expression from other symbols selecting this symbol.
+      Multiple selections get ORed together. A condition on a select is ANDed
+      with the selecting symbol.
+
+      For example, if A has 'select FOO' and B has 'select FOO if C', then
+      FOO's rev_dep will be (OR, A, (AND, B, C)).
+
+    weak_rev_dep:
+      Like rev_dep, for imply.
+
+    direct_dep:
+      The direct ('depends on') dependencies for the symbol, or self.kconfig.y
+      if there are no direct dependencies.
+
+      This attribute includes any dependencies from surrounding menus and ifs.
+      Those get propagated to the direct dependencies, and the resulting direct
+      dependencies in turn get propagated to the conditions of all properties.
+
+      If the symbol is defined in multiple locations, the dependencies from the
+      different locations get ORed together.
+
+    referenced:
+      A set() with all symbols and choices referenced in the properties and
+      property conditions of the symbol.
+
+      Also includes dependencies from surrounding menus and ifs, because those
+      get propagated to the symbol (see the 'Intro to symbol values' section in
+      the module docstring).
+
+      Choices appear in the dependencies of choice symbols.
+
+      For the following definitions, only B and not C appears in A's
+      'referenced'. To get transitive references, you'll have to recursively
+      expand 'references' until no new items appear.
+
+        config A
+                bool
+                depends on B
+
+        config B
+                bool
+                depends on C
+
+        config C
+                bool
+
+      See the Symbol.direct_dep attribute if you're only interested in the
+      direct dependencies of the symbol (its 'depends on'). You can extract the
+      symbols in it with the global expr_items() function.
+
+    env_var:
+      If the Symbol has an 'option env="FOO"' option, this contains the name
+      ("FOO") of the environment variable. None for symbols without no
+      'option env'.
+
+      'option env="FOO"' acts like a 'default' property whose value is the
+      value of $FOO.
+
+      Symbols with 'option env' are never written out to .config files, even if
+      they are visible. env_var corresponds to a flag called SYMBOL_AUTO in the
+      C implementation.
+
+    is_allnoconfig_y:
+      True if the symbol has 'option allnoconfig_y' set on it. This has no
+      effect internally (except when printing symbols), but can be checked by
+      scripts.
+
+    is_constant:
+      True if the symbol is a constant (quoted) symbol.
+
+    kconfig:
+      The Kconfig instance this symbol is from.
+    """
+    __slots__ = (
+        "_cached_assignable",
+        "_cached_str_val",
+        "_cached_tri_val",
+        "_cached_vis",
+        "_dependents",
+        "_old_val",
+        "_visited",
+        "_was_set",
+        "_write_to_conf",
+        "choice",
+        "defaults",
+        "direct_dep",
+        "env_var",
+        "implies",
+        "is_allnoconfig_y",
+        "is_constant",
+        "kconfig",
+        "name",
+        "nodes",
+        "orig_type",
+        "ranges",
+        "rev_dep",
+        "selects",
+        "user_value",
+        "weak_rev_dep",
+    )
+
+    #
+    # Public interface
+    #
+
+    @property
+    def type(self):
+        """
+        See the class documentation.
+        """
+        if self.orig_type is TRISTATE and \
+           (self.choice and self.choice.tri_value == 2 or
+            not self.kconfig.modules.tri_value):
+
+            return BOOL
+
+        return self.orig_type
+
+    @property
+    def str_value(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_str_val is not None:
+            return self._cached_str_val
+
+        if self.orig_type in _BOOL_TRISTATE:
+            # Also calculates the visibility, so invalidation safe
+            self._cached_str_val = TRI_TO_STR[self.tri_value]
+            return self._cached_str_val
+
+        # As a quirk of Kconfig, undefined symbols get their name as their
+        # string value. This is why things like "FOO = bar" work for seeing if
+        # FOO has the value "bar".
+        if not self.orig_type:  # UNKNOWN
+            self._cached_str_val = self.name
+            return self.name
+
+        val = ""
+        # Warning: See Symbol._rec_invalidate(), and note that this is a hidden
+        # function call (property magic)
+        vis = self.visibility
+
+        self._write_to_conf = (vis != 0)
+
+        if self.orig_type in _INT_HEX:
+            # The C implementation checks the user value against the range in a
+            # separate code path (post-processing after loading a .config).
+            # Checking all values here instead makes more sense for us. It
+            # requires that we check for a range first.
+
+            base = _TYPE_TO_BASE[self.orig_type]
+
+            # Check if a range is in effect
+            for low_expr, high_expr, cond in self.ranges:
+                if expr_value(cond):
+                    has_active_range = True
+
+                    # The zeros are from the C implementation running strtoll()
+                    # on empty strings
+                    low = int(low_expr.str_value, base) if \
+                      _is_base_n(low_expr.str_value, base) else 0
+                    high = int(high_expr.str_value, base) if \
+                      _is_base_n(high_expr.str_value, base) else 0
+
+                    break
+            else:
+                has_active_range = False
+
+            # Defaults are used if the symbol is invisible, lacks a user value,
+            # or has an out-of-range user value
+            use_defaults = True
+
+            if vis and self.user_value:
+                user_val = int(self.user_value, base)
+                if has_active_range and not low <= user_val <= high:
+                    num2str = str if base == 10 else hex
+                    self.kconfig._warn(
+                        "user value {} on the {} symbol {} ignored due to "
+                        "being outside the active range ([{}, {}]) -- falling "
+                        "back on defaults"
+                        .format(num2str(user_val), TYPE_TO_STR[self.orig_type],
+                                self.name_and_loc,
+                                num2str(low), num2str(high)))
+                else:
+                    # If the user value is well-formed and satisfies range
+                    # contraints, it is stored in exactly the same form as
+                    # specified in the assignment (with or without "0x", etc.)
+                    val = self.user_value
+                    use_defaults = False
+
+            if use_defaults:
+                # No user value or invalid user value. Look at defaults.
+
+                # Used to implement the warning below
+                has_default = False
+
+                for sym, cond in self.defaults:
+                    if expr_value(cond):
+                        has_default = self._write_to_conf = True
+
+                        val = sym.str_value
+
+                        if _is_base_n(val, base):
+                            val_num = int(val, base)
+                        else:
+                            val_num = 0  # strtoll() on empty string
+
+                        break
+                else:
+                    val_num = 0  # strtoll() on empty string
+
+                # This clamping procedure runs even if there's no default
+                if has_active_range:
+                    clamp = None
+                    if val_num < low:
+                        clamp = low
+                    elif val_num > high:
+                        clamp = high
+
+                    if clamp is not None:
+                        # The value is rewritten to a standard form if it is
+                        # clamped
+                        val = str(clamp) \
+                              if self.orig_type is INT else \
+                              hex(clamp)
+
+                        if has_default:
+                            num2str = str if base == 10 else hex
+                            self.kconfig._warn(
+                                "default value {} on {} clamped to {} due to "
+                                "being outside the active range ([{}, {}])"
+                                .format(val_num, self.name_and_loc,
+                                        num2str(clamp), num2str(low),
+                                        num2str(high)))
+
+        elif self.orig_type is STRING:
+            if vis and self.user_value is not None:
+                # If the symbol is visible and has a user value, use that
+                val = self.user_value
+            else:
+                # Otherwise, look at defaults
+                for sym, cond in self.defaults:
+                    if expr_value(cond):
+                        val = sym.str_value
+                        self._write_to_conf = True
+                        break
+
+        # env_var corresponds to SYMBOL_AUTO in the C implementation, and is
+        # also set on the defconfig_list symbol there. Test for the
+        # defconfig_list symbol explicitly instead here, to avoid a nonsensical
+        # env_var setting and the defconfig_list symbol being printed
+        # incorrectly. This code is pretty cold anyway.
+        if self.env_var is not None or self is self.kconfig.defconfig_list:
+            self._write_to_conf = False
+
+        self._cached_str_val = val
+        return val
+
+    @property
+    def tri_value(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_tri_val is not None:
+            return self._cached_tri_val
+
+        if self.orig_type not in _BOOL_TRISTATE:
+            if self.orig_type:  # != UNKNOWN
+                # Would take some work to give the location here
+                self.kconfig._warn(
+                    "The {} symbol {} is being evaluated in a logical context "
+                    "somewhere. It will always evaluate to n."
+                    .format(TYPE_TO_STR[self.orig_type], self.name_and_loc))
+
+            self._cached_tri_val = 0
+            return 0
+
+        # Warning: See Symbol._rec_invalidate(), and note that this is a hidden
+        # function call (property magic)
+        vis = self.visibility
+        self._write_to_conf = (vis != 0)
+
+        val = 0
+
+        if not self.choice:
+            # Non-choice symbol
+
+            if vis and self.user_value is not None:
+                # If the symbol is visible and has a user value, use that
+                val = min(self.user_value, vis)
+
+            else:
+                # Otherwise, look at defaults and weak reverse dependencies
+                # (implies)
+
+                for default, cond in self.defaults:
+                    dep_val = expr_value(cond)
+                    if dep_val:
+                        val = min(expr_value(default), dep_val)
+                        if val:
+                            self._write_to_conf = True
+                        break
+
+                # Weak reverse dependencies are only considered if our
+                # direct dependencies are met
+                dep_val = expr_value(self.weak_rev_dep)
+                if dep_val and expr_value(self.direct_dep):
+                    val = max(dep_val, val)
+                    self._write_to_conf = True
+
+            # Reverse (select-related) dependencies take precedence
+            dep_val = expr_value(self.rev_dep)
+            if dep_val:
+                if expr_value(self.direct_dep) < dep_val:
+                    self._warn_select_unsatisfied_deps()
+
+                val = max(dep_val, val)
+                self._write_to_conf = True
+
+            # m is promoted to y for (1) bool symbols and (2) symbols with a
+            # weak_rev_dep (from imply) of y
+            if val == 1 and \
+               (self.type is BOOL or expr_value(self.weak_rev_dep) == 2):
+                val = 2
+
+        elif vis == 2:
+            # Visible choice symbol in y-mode choice. The choice mode limits
+            # the visibility of choice symbols, so it's sufficient to just
+            # check the visibility of the choice symbols themselves.
+            val = 2 if self.choice.selection is self else 0
+
+        elif vis and self.user_value:
+            # Visible choice symbol in m-mode choice, with set non-0 user value
+            val = 1
+
+        self._cached_tri_val = val
+        return val
+
+    @property
+    def assignable(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_assignable is None:
+            self._cached_assignable = self._assignable()
+        return self._cached_assignable
+
+    @property
+    def visibility(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_vis is None:
+            self._cached_vis = _visibility(self)
+        return self._cached_vis
+
+    @property
+    def config_string(self):
+        """
+        See the class documentation.
+        """
+        # _write_to_conf is determined when the value is calculated. This is a
+        # hidden function call due to property magic.
+        val = self.str_value
+        if not self._write_to_conf:
+            return ""
+
+        if self.orig_type in _BOOL_TRISTATE:
+            return "{}{}={}\n" \
+                   .format(self.kconfig.config_prefix, self.name, val) \
+                   if val != "n" else \
+                   "# {}{} is not set\n" \
+                   .format(self.kconfig.config_prefix, self.name)
+
+        if self.orig_type in _INT_HEX:
+            return "{}{}={}\n" \
+                   .format(self.kconfig.config_prefix, self.name, val)
+
+        # sym.orig_type is STRING
+        return '{}{}="{}"\n' \
+               .format(self.kconfig.config_prefix, self.name, escape(val))
+
+    @property
+    def name_and_loc(self):
+        """
+        See the class documentation.
+        """
+        return self.name + " " + _locs(self)
+
+    def set_value(self, value):
+        """
+        Sets the user value of the symbol.
+
+        Equal in effect to assigning the value to the symbol within a .config
+        file. For bool and tristate symbols, use the 'assignable' attribute to
+        check which values can currently be assigned. Setting values outside
+        'assignable' will cause Symbol.user_value to differ from
+        Symbol.str/tri_value (be truncated down or up).
+
+        Setting a choice symbol to 2 (y) sets Choice.user_selection to the
+        choice symbol in addition to setting Symbol.user_value.
+        Choice.user_selection is considered when the choice is in y mode (the
+        "normal" mode).
+
+        Other symbols that depend (possibly indirectly) on this symbol are
+        automatically recalculated to reflect the assigned value.
+
+        value:
+          The user value to give to the symbol. For bool and tristate symbols,
+          n/m/y can be specified either as 0/1/2 (the usual format for tristate
+          values in Kconfiglib) or as one of the strings "n", "m", or "y". For
+          other symbol types, pass a string.
+
+          Note that the value for an int/hex symbol is passed as a string, e.g.
+          "123" or "0x0123". The format of this string is preserved in the
+          output.
+
+          Values that are invalid for the type (such as "foo" or 1 (m) for a
+          BOOL or "0x123" for an INT) are ignored and won't be stored in
+          Symbol.user_value. Kconfiglib will print a warning by default for
+          invalid assignments, and set_value() will return False.
+
+        Returns True if the value is valid for the type of the symbol, and
+        False otherwise. This only looks at the form of the value. For BOOL and
+        TRISTATE symbols, check the Symbol.assignable attribute to see what
+        values are currently in range and would actually be reflected in the
+        value of the symbol. For other symbol types, check whether the
+        visibility is non-n.
+        """
+        if self.orig_type in _BOOL_TRISTATE and value in STR_TO_TRI:
+            value = STR_TO_TRI[value]
+
+        # If the new user value matches the old, nothing changes, and we can
+        # avoid invalidating cached values.
+        #
+        # This optimization is skipped for choice symbols: Setting a choice
+        # symbol's user value to y might change the state of the choice, so it
+        # wouldn't be safe (symbol user values always match the values set in a
+        # .config file or via set_value(), and are never implicitly updated).
+        if value == self.user_value and not self.choice:
+            self._was_set = True
+            return True
+
+        # Check if the value is valid for our type
+        if not (self.orig_type is BOOL     and value in (2, 0)     or
+                self.orig_type is TRISTATE and value in TRI_TO_STR or
+                value.__class__ is str and
+                (self.orig_type is STRING                        or
+                 self.orig_type is INT and _is_base_n(value, 10) or
+                 self.orig_type is HEX and _is_base_n(value, 16)
+                                       and int(value, 16) >= 0)):
+
+            # Display tristate values as n, m, y in the warning
+            self.kconfig._warn(
+                "the value {} is invalid for {}, which has type {} -- "
+                "assignment ignored"
+                .format(TRI_TO_STR[value] if value in TRI_TO_STR else
+                            "'{}'".format(value),
+                        self.name_and_loc, TYPE_TO_STR[self.orig_type]))
+
+            return False
+
+        self.user_value = value
+        self._was_set = True
+
+        if self.choice and value == 2:
+            # Setting a choice symbol to y makes it the user selection of the
+            # choice. Like for symbol user values, the user selection is not
+            # guaranteed to match the actual selection of the choice, as
+            # dependencies come into play.
+            self.choice.user_selection = self
+            self.choice._was_set = True
+            self.choice._rec_invalidate()
+        else:
+            self._rec_invalidate_if_has_prompt()
+
+        return True
+
+    def unset_value(self):
+        """
+        Removes any user value from the symbol, as if the symbol had never
+        gotten a user value via Kconfig.load_config() or Symbol.set_value().
+        """
+        if self.user_value is not None:
+            self.user_value = None
+            self._rec_invalidate_if_has_prompt()
+
+    @property
+    def referenced(self):
+        """
+        See the class documentation.
+        """
+        return {item for node in self.nodes for item in node.referenced}
+
+    @property
+    def orig_defaults(self):
+        """
+        See the class documentation.
+        """
+        return [d for node in self.nodes for d in node.orig_defaults]
+
+    @property
+    def orig_selects(self):
+        """
+        See the class documentation.
+        """
+        return [s for node in self.nodes for s in node.orig_selects]
+
+    @property
+    def orig_implies(self):
+        """
+        See the class documentation.
+        """
+        return [i for node in self.nodes for i in node.orig_implies]
+
+    @property
+    def orig_ranges(self):
+        """
+        See the class documentation.
+        """
+        return [r for node in self.nodes for r in node.orig_ranges]
+
+    def __repr__(self):
+        """
+        Returns a string with information about the symbol (including its name,
+        value, visibility, and location(s)) when it is evaluated on e.g. the
+        interactive Python prompt.
+        """
+        fields = ["symbol " + self.name, TYPE_TO_STR[self.type]]
+        add = fields.append
+
+        for node in self.nodes:
+            if node.prompt:
+                add('"{}"'.format(node.prompt[0]))
+
+        # Only add quotes for non-bool/tristate symbols
+        add("value " + (self.str_value if self.orig_type in _BOOL_TRISTATE
+                        else '"{}"'.format(self.str_value)))
+
+        if not self.is_constant:
+            # These aren't helpful to show for constant symbols
+
+            if self.user_value is not None:
+                # Only add quotes for non-bool/tristate symbols
+                add("user value " + (TRI_TO_STR[self.user_value]
+                                     if self.orig_type in _BOOL_TRISTATE
+                                     else '"{}"'.format(self.user_value)))
+
+            add("visibility " + TRI_TO_STR[self.visibility])
+
+            if self.choice:
+                add("choice symbol")
+
+            if self.is_allnoconfig_y:
+                add("allnoconfig_y")
+
+            if self is self.kconfig.defconfig_list:
+                add("is the defconfig_list symbol")
+
+            if self.env_var is not None:
+                add("from environment variable " + self.env_var)
+
+            if self is self.kconfig.modules:
+                add("is the modules symbol")
+
+            add("direct deps " + TRI_TO_STR[expr_value(self.direct_dep)])
+
+        if self.nodes:
+            for node in self.nodes:
+                add("{}:{}".format(node.filename, node.linenr))
+        else:
+            add("constant" if self.is_constant else "undefined")
+
+        return "<{}>".format(", ".join(fields))
+
+    def __str__(self):
+        """
+        Returns a string representation of the symbol when it is printed.
+        Matches the Kconfig format, with any parent dependencies propagated to
+        the 'depends on' condition.
+
+        The string is constructed by joining the strings returned by
+        MenuNode.__str__() for each of the symbol's menu nodes, so symbols
+        defined in multiple locations will return a string with all
+        definitions.
+
+        The returned string does not end in a newline. An empty string is
+        returned for undefined and constant symbols.
+        """
+        return self.custom_str(standard_sc_expr_str)
+
+    def custom_str(self, sc_expr_str_fn):
+        """
+        Works like Symbol.__str__(), but allows a custom format to be used for
+        all symbol/choice references. See expr_str().
+        """
+        return "\n\n".join(node.custom_str(sc_expr_str_fn)
+                           for node in self.nodes)
+
+    #
+    # Private methods
+    #
+
+    def __init__(self):
+        """
+        Symbol constructor -- not intended to be called directly by Kconfiglib
+        clients.
+        """
+        # These attributes are always set on the instance from outside and
+        # don't need defaults:
+        #   kconfig
+        #   direct_dep
+        #   is_constant
+        #   name
+        #   rev_dep
+        #   weak_rev_dep
+
+        # - UNKNOWN == 0
+        # - _visited is used during tree iteration and dep. loop detection
+        self.orig_type = self._visited = 0
+
+        self.nodes = []
+
+        self.defaults = []
+        self.selects = []
+        self.implies = []
+        self.ranges = []
+
+        self.user_value = \
+        self.choice = \
+        self.env_var = \
+        self._cached_str_val = self._cached_tri_val = self._cached_vis = \
+        self._cached_assignable = None
+
+        # _write_to_conf is calculated along with the value. If True, the
+        # Symbol gets a .config entry.
+
+        self.is_allnoconfig_y = \
+        self._was_set = \
+        self._write_to_conf = False
+
+        # See Kconfig._build_dep()
+        self._dependents = set()
+
+    def _assignable(self):
+        # Worker function for the 'assignable' attribute
+
+        if self.orig_type not in _BOOL_TRISTATE:
+            return ()
+
+        # Warning: See Symbol._rec_invalidate(), and note that this is a hidden
+        # function call (property magic)
+        vis = self.visibility
+        if not vis:
+            return ()
+
+        rev_dep_val = expr_value(self.rev_dep)
+
+        if vis == 2:
+            if self.choice:
+                return (2,)
+
+            if not rev_dep_val:
+                if self.type is BOOL or expr_value(self.weak_rev_dep) == 2:
+                    return (0, 2)
+                return (0, 1, 2)
+
+            if rev_dep_val == 2:
+                return (2,)
+
+            # rev_dep_val == 1
+
+            if self.type is BOOL or expr_value(self.weak_rev_dep) == 2:
+                return (2,)
+            return (1, 2)
+
+        # vis == 1
+
+        # Must be a tristate here, because bool m visibility gets promoted to y
+
+        if not rev_dep_val:
+            return (0, 1) if expr_value(self.weak_rev_dep) != 2 else (0, 2)
+
+        if rev_dep_val == 2:
+            return (2,)
+
+        # vis == rev_dep_val == 1
+
+        return (1,)
+
+    def _invalidate(self):
+        # Marks the symbol as needing to be recalculated
+
+        self._cached_str_val = self._cached_tri_val = self._cached_vis = \
+        self._cached_assignable = None
+
+    def _rec_invalidate(self):
+        # Invalidates the symbol and all items that (possibly) depend on it
+
+        if self is self.kconfig.modules:
+            # Invalidating MODULES has wide-ranging effects
+            self.kconfig._invalidate_all()
+        else:
+            self._invalidate()
+
+            for item in self._dependents:
+                # _cached_vis doubles as a flag that tells us whether 'item'
+                # has cached values, because it's calculated as a side effect
+                # of calculating all other (non-constant) cached values.
+                #
+                # If item._cached_vis is None, it means there can't be cached
+                # values on other items that depend on 'item', because if there
+                # were, some value on 'item' would have been calculated and
+                # item._cached_vis set as a side effect. It's therefore safe to
+                # stop the invalidation at symbols with _cached_vis None.
+                #
+                # This approach massively speeds up scripts that set a lot of
+                # values, vs simply invalidating all possibly dependent symbols
+                # (even when you already have a list of all the dependent
+                # symbols, because some symbols get huge dependency trees).
+                #
+                # This gracefully handles dependency loops too, which is nice
+                # for choices, where the choice depends on the choice symbols
+                # and vice versa.
+                if item._cached_vis is not None:
+                    item._rec_invalidate()
+
+    def _rec_invalidate_if_has_prompt(self):
+        # Invalidates the symbol and its dependent symbols, but only if the
+        # symbol has a prompt. User values never have an effect on promptless
+        # symbols, so we skip invalidation for them as an optimization.
+        #
+        # This also prevents constant (quoted) symbols from being invalidated
+        # if set_value() is called on them, which would make them lose their
+        # value and break things.
+        #
+        # Prints a warning if the symbol has no prompt. In some contexts (e.g.
+        # when loading a .config files) assignments to promptless symbols are
+        # normal and expected, so the warning can be disabled.
+
+        for node in self.nodes:
+            if node.prompt:
+                self._rec_invalidate()
+                return
+
+        if self.kconfig._warn_assign_no_prompt:
+            self.kconfig._warn(self.name_and_loc + " has no prompt, meaning "
+                               "user values have no effect on it")
+
+    def _str_default(self):
+        # write_min_config() helper function. Returns the value the symbol
+        # would get from defaults if it didn't have a user value. Uses exactly
+        # the same algorithm as the C implementation (though a bit cleaned up),
+        # for compatibility.
+
+        if self.orig_type in _BOOL_TRISTATE:
+            val = 0
+
+            # Defaults, selects, and implies do not affect choice symbols
+            if not self.choice:
+                for default, cond in self.defaults:
+                    cond_val = expr_value(cond)
+                    if cond_val:
+                        val = min(expr_value(default), cond_val)
+                        break
+
+                val = max(expr_value(self.rev_dep),
+                          expr_value(self.weak_rev_dep),
+                          val)
+
+                # Transpose mod to yes if type is bool (possibly due to modules
+                # being disabled)
+                if val == 1 and self.type is BOOL:
+                    val = 2
+
+            return TRI_TO_STR[val]
+
+        if self.orig_type:  # STRING/INT/HEX
+            for default, cond in self.defaults:
+                if expr_value(cond):
+                    return default.str_value
+
+        return ""
+
+    def _warn_select_unsatisfied_deps(self):
+        # Helper for printing an informative warning when a symbol with
+        # unsatisfied direct dependencies (dependencies from 'depends on', ifs,
+        # and menus) is selected by some other symbol. Also warn if a symbol
+        # whose direct dependencies evaluate to m is selected to y.
+
+        msg = "{} has direct dependencies {} with value {}, but is " \
+              "currently being {}-selected by the following symbols:" \
+              .format(self.name_and_loc, expr_str(self.direct_dep),
+                      TRI_TO_STR[expr_value(self.direct_dep)],
+                      TRI_TO_STR[expr_value(self.rev_dep)])
+
+        # The reverse dependencies from each select are ORed together
+        for select in split_expr(self.rev_dep, OR):
+            if expr_value(select) <= expr_value(self.direct_dep):
+                # Only include selects that exceed the direct dependencies
+                continue
+
+            # - 'select A if B' turns into A && B
+            # - 'select A' just turns into A
+            #
+            # In both cases, we can split on AND and pick the first operand
+            selecting_sym = split_expr(select, AND)[0]
+
+            msg += "\n - {}, with value {}, direct dependencies {} " \
+                   "(value: {})" \
+                   .format(selecting_sym.name_and_loc,
+                           selecting_sym.str_value,
+                           expr_str(selecting_sym.direct_dep),
+                           TRI_TO_STR[expr_value(selecting_sym.direct_dep)])
+
+            if select.__class__ is tuple:
+                msg += ", and select condition {} (value: {})" \
+                       .format(expr_str(select[2]),
+                               TRI_TO_STR[expr_value(select[2])])
+
+        self.kconfig._warn(msg)
+
+
+class Choice(object):
+    """
+    Represents a choice statement:
+
+      choice
+          ...
+      endchoice
+
+    The following attributes are available on Choice instances. They should be
+    treated as read-only, and some are implemented through @property magic (but
+    are still efficient to access due to internal caching).
+
+    Note: Prompts, help texts, and locations are stored in the Choice's
+    MenuNode(s) rather than in the Choice itself. Check the MenuNode class and
+    the Choice.nodes attribute. This organization matches the C tools.
+
+    name:
+      The name of the choice, e.g. "FOO" for 'choice FOO', or None if the
+      Choice has no name.
+
+    type:
+      The type of the choice. One of BOOL, TRISTATE, UNKNOWN. UNKNOWN is for
+      choices defined without a type where none of the contained symbols have a
+      type either (otherwise the choice inherits the type of the first symbol
+      defined with a type).
+
+      When running without modules (CONFIG_MODULES=n), TRISTATE choices
+      magically change type to BOOL. This matches the C tools, and makes sense
+      for menuconfig-like functionality.
+
+    orig_type:
+      The type as given in the Kconfig file, without any magic applied. Used
+      when printing the choice.
+
+    tri_value:
+      The tristate value (mode) of the choice. A choice can be in one of three
+      modes:
+
+        0 (n) - The choice is disabled and no symbols can be selected. For
+                visible choices, this mode is only possible for choices with
+                the 'optional' flag set (see kconfig-language.txt).
+
+        1 (m) - Any number of choice symbols can be set to m, the rest will
+                be n.
+
+        2 (y) - One symbol will be y, the rest n.
+
+      Only tristate choices can be in m mode. The visibility of the choice is
+      an upper bound on the mode, and the mode in turn is an upper bound on the
+      visibility of the choice symbols.
+
+      To change the mode, use Choice.set_value().
+
+      Implementation note:
+        The C tools internally represent choices as a type of symbol, with
+        special-casing in many code paths. This is why there is a lot of
+        similarity to Symbol. The value (mode) of a choice is really just a
+        normal symbol value, and an implicit reverse dependency forces its
+        lower bound to m for visible non-optional choices (the reverse
+        dependency is 'm && <visibility>').
+
+        Symbols within choices get the choice propagated as a dependency to
+        their properties. This turns the mode of the choice into an upper bound
+        on e.g. the visibility of choice symbols, and explains the gotcha
+        related to printing choice symbols mentioned in the module docstring.
+
+        Kconfiglib uses a separate Choice class only because it makes the code
+        and interface less confusing (especially in a user-facing interface).
+        Corresponding attributes have the same name in the Symbol and Choice
+        classes, for consistency and compatibility.
+
+    str_value:
+      Like choice.tri_value, but gives the value as one of the strings
+      "n", "m", or "y"
+
+    user_value:
+      The value (mode) selected by the user through Choice.set_value(). Either
+      0, 1, or 2, or None if the user hasn't selected a mode. See
+      Symbol.user_value.
+
+      WARNING: Do not assign directly to this. It will break things. Use
+      Choice.set_value() instead.
+
+    assignable:
+      See the symbol class documentation. Gives the assignable values (modes).
+
+    selection:
+      The Symbol instance of the currently selected symbol. None if the Choice
+      is not in y mode or has no selected symbol (due to unsatisfied
+      dependencies on choice symbols).
+
+      WARNING: Do not assign directly to this. It will break things. Call
+      sym.set_value(2) on the choice symbol you want to select instead.
+
+    user_selection:
+      The symbol selected by the user (by setting it to y). Ignored if the
+      choice is not in y mode, but still remembered so that the choice "snaps
+      back" to the user selection if the mode is changed back to y. This might
+      differ from 'selection' due to unsatisfied dependencies.
+
+      WARNING: Do not assign directly to this. It will break things. Call
+      sym.set_value(2) on the choice symbol to be selected instead.
+
+    visibility:
+      See the Symbol class documentation. Acts on the value (mode).
+
+    name_and_loc:
+      Holds a string like
+
+        "<choice MY_CHOICE> (defined at foo/Kconfig:12)"
+
+      , giving the name of the choice and its definition location(s). If the
+      choice has no name (isn't defined with 'choice MY_CHOICE'), then it will
+      be shown as "<choice>" before the list of locations (always a single one
+      in that case).
+
+    syms:
+      List of symbols contained in the choice.
+
+      Obscure gotcha: If a symbol depends on the previous symbol within a
+      choice so that an implicit menu is created, it won't be a choice symbol,
+      and won't be included in 'syms'.
+
+    nodes:
+      A list of MenuNodes for this choice. In practice, the list will probably
+      always contain a single MenuNode, but it is possible to give a choice a
+      name and define it in multiple locations.
+
+    defaults:
+      List of (symbol, cond) tuples for the choice's 'defaults' properties. For
+      example, 'default A if B && C' is represented as (A, (AND, B, C)). If
+      there is no condition, 'cond' is self.kconfig.y.
+
+      Note that 'depends on' and parent dependencies are propagated to
+      'default' conditions.
+
+    orig_defaults:
+      See the corresponding attribute on the MenuNode class.
+
+    direct_dep:
+      See Symbol.direct_dep.
+
+    referenced:
+      A set() with all symbols referenced in the properties and property
+      conditions of the choice.
+
+      Also includes dependencies from surrounding menus and ifs, because those
+      get propagated to the choice (see the 'Intro to symbol values' section in
+      the module docstring).
+
+    is_optional:
+      True if the choice has the 'optional' flag set on it and can be in
+      n mode.
+
+    kconfig:
+      The Kconfig instance this choice is from.
+    """
+    __slots__ = (
+        "_cached_assignable",
+        "_cached_selection",
+        "_cached_vis",
+        "_dependents",
+        "_visited",
+        "_was_set",
+        "defaults",
+        "direct_dep",
+        "is_constant",
+        "is_optional",
+        "kconfig",
+        "name",
+        "nodes",
+        "orig_type",
+        "syms",
+        "user_selection",
+        "user_value",
+    )
+
+    #
+    # Public interface
+    #
+
+    @property
+    def type(self):
+        """
+        Returns the type of the choice. See Symbol.type.
+        """
+        if self.orig_type is TRISTATE and not self.kconfig.modules.tri_value:
+            return BOOL
+        return self.orig_type
+
+    @property
+    def str_value(self):
+        """
+        See the class documentation.
+        """
+        return TRI_TO_STR[self.tri_value]
+
+    @property
+    def tri_value(self):
+        """
+        See the class documentation.
+        """
+        # This emulates a reverse dependency of 'm && visibility' for
+        # non-optional choices, which is how the C implementation does it
+
+        val = 0 if self.is_optional else 1
+
+        if self.user_value is not None:
+            val = max(val, self.user_value)
+
+        # Warning: See Symbol._rec_invalidate(), and note that this is a hidden
+        # function call (property magic)
+        val = min(val, self.visibility)
+
+        # Promote m to y for boolean choices
+        return 2 if val == 1 and self.type is BOOL else val
+
+    @property
+    def assignable(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_assignable is None:
+            self._cached_assignable = self._assignable()
+        return self._cached_assignable
+
+    @property
+    def visibility(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_vis is None:
+            self._cached_vis = _visibility(self)
+        return self._cached_vis
+
+    @property
+    def name_and_loc(self):
+        """
+        See the class documentation.
+        """
+        # Reuse the expression format, which is '<choice (name, if any)>'.
+        return standard_sc_expr_str(self) + " " + _locs(self)
+
+    @property
+    def selection(self):
+        """
+        See the class documentation.
+        """
+        if self._cached_selection is _NO_CACHED_SELECTION:
+            self._cached_selection = self._selection()
+        return self._cached_selection
+
+    def set_value(self, value):
+        """
+        Sets the user value (mode) of the choice. Like for Symbol.set_value(),
+        the visibility might truncate the value. Choices without the 'optional'
+        attribute (is_optional) can never be in n mode, but 0/"n" is still
+        accepted since it's not a malformed value (though it will have no
+        effect).
+
+        Returns True if the value is valid for the type of the choice, and
+        False otherwise. This only looks at the form of the value. Check the
+        Choice.assignable attribute to see what values are currently in range
+        and would actually be reflected in the mode of the choice.
+        """
+        if value in STR_TO_TRI:
+            value = STR_TO_TRI[value]
+
+        if value == self.user_value:
+            # We know the value must be valid if it was successfully set
+            # previously
+            self._was_set = True
+            return True
+
+        if not (self.orig_type is BOOL     and value in (2, 0) or
+                self.orig_type is TRISTATE and value in TRI_TO_STR):
+
+            # Display tristate values as n, m, y in the warning
+            self.kconfig._warn(
+                "the value {} is invalid for {}, which has type {} -- "
+                "assignment ignored"
+                .format(TRI_TO_STR[value] if value in TRI_TO_STR else
+                            "'{}'".format(value),
+                        self.name_and_loc, TYPE_TO_STR[self.orig_type]))
+
+            return False
+
+        self.user_value = value
+        self._was_set = True
+        self._rec_invalidate()
+
+        return True
+
+    def unset_value(self):
+        """
+        Resets the user value (mode) and user selection of the Choice, as if
+        the user had never touched the mode or any of the choice symbols.
+        """
+        if self.user_value is not None or self.user_selection:
+            self.user_value = self.user_selection = None
+            self._rec_invalidate()
+
+    @property
+    def referenced(self):
+        """
+        See the class documentation.
+        """
+        return {item for node in self.nodes for item in node.referenced}
+
+    @property
+    def orig_defaults(self):
+        """
+        See the class documentation.
+        """
+        return [d for node in self.nodes for d in node.orig_defaults]
+
+    def __repr__(self):
+        """
+        Returns a string with information about the choice when it is evaluated
+        on e.g. the interactive Python prompt.
+        """
+        fields = ["choice " + self.name if self.name else "choice",
+                  TYPE_TO_STR[self.type]]
+        add = fields.append
+
+        for node in self.nodes:
+            if node.prompt:
+                add('"{}"'.format(node.prompt[0]))
+
+        add("mode " + self.str_value)
+
+        if self.user_value is not None:
+            add('user mode {}'.format(TRI_TO_STR[self.user_value]))
+
+        if self.selection:
+            add("{} selected".format(self.selection.name))
+
+        if self.user_selection:
+            user_sel_str = "{} selected by user" \
+                           .format(self.user_selection.name)
+
+            if self.selection is not self.user_selection:
+                user_sel_str += " (overridden)"
+
+            add(user_sel_str)
+
+        add("visibility " + TRI_TO_STR[self.visibility])
+
+        if self.is_optional:
+            add("optional")
+
+        for node in self.nodes:
+            add("{}:{}".format(node.filename, node.linenr))
+
+        return "<{}>".format(", ".join(fields))
+
+    def __str__(self):
+        """
+        Returns a string representation of the choice when it is printed.
+        Matches the Kconfig format (though without the contained choice
+        symbols), with any parent dependencies propagated to the 'depends on'
+        condition.
+
+        The returned string does not end in a newline.
+
+        See Symbol.__str__() as well.
+        """
+        return self.custom_str(standard_sc_expr_str)
+
+    def custom_str(self, sc_expr_str_fn):
+        """
+        Works like Choice.__str__(), but allows a custom format to be used for
+        all symbol/choice references. See expr_str().
+        """
+        return "\n\n".join(node.custom_str(sc_expr_str_fn)
+                           for node in self.nodes)
+
+    #
+    # Private methods
+    #
+
+    def __init__(self):
+        """
+        Choice constructor -- not intended to be called directly by Kconfiglib
+        clients.
+        """
+        # These attributes are always set on the instance from outside and
+        # don't need defaults:
+        #   direct_dep
+        #   kconfig
+
+        # - UNKNOWN == 0
+        # - _visited is used during dep. loop detection
+        self.orig_type = self._visited = 0
+
+        self.nodes = []
+
+        self.syms = []
+        self.defaults = []
+
+        self.name = \
+        self.user_value = self.user_selection = \
+        self._cached_vis = self._cached_assignable = None
+
+        self._cached_selection = _NO_CACHED_SELECTION
+
+        # is_constant is checked by _depend_on(). Just set it to avoid having
+        # to special-case choices.
+        self.is_constant = self.is_optional = False
+
+        # See Kconfig._build_dep()
+        self._dependents = set()
+
+    def _assignable(self):
+        # Worker function for the 'assignable' attribute
+
+        # Warning: See Symbol._rec_invalidate(), and note that this is a hidden
+        # function call (property magic)
+        vis = self.visibility
+
+        if not vis:
+            return ()
+
+        if vis == 2:
+            if not self.is_optional:
+                return (2,) if self.type is BOOL else (1, 2)
+            return (0, 2) if self.type is BOOL else (0, 1, 2)
+
+        # vis == 1
+
+        return (0, 1) if self.is_optional else (1,)
+
+    def _selection(self):
+        # Worker function for the 'selection' attribute
+
+        # Warning: See Symbol._rec_invalidate(), and note that this is a hidden
+        # function call (property magic)
+        if self.tri_value != 2:
+            # Not in y mode, so no selection
+            return None
+
+        # Use the user selection if it's visible
+        if self.user_selection and self.user_selection.visibility:
+            return self.user_selection
+
+        # Otherwise, check if we have a default
+        return self._selection_from_defaults()
+
+    def _selection_from_defaults(self):
+        # Check if we have a default
+        for sym, cond in self.defaults:
+            # The default symbol must be visible too
+            if expr_value(cond) and sym.visibility:
+                return sym
+
+        # Otherwise, pick the first visible symbol, if any
+        for sym in self.syms:
+            if sym.visibility:
+                return sym
+
+        # Couldn't find a selection
+        return None
+
+    def _invalidate(self):
+        self._cached_vis = self._cached_assignable = None
+        self._cached_selection = _NO_CACHED_SELECTION
+
+    def _rec_invalidate(self):
+        # See Symbol._rec_invalidate()
+
+        self._invalidate()
+
+        for item in self._dependents:
+            if item._cached_vis is not None:
+                item._rec_invalidate()
+
+
+class MenuNode(object):
+    """
+    Represents a menu node in the configuration. This corresponds to an entry
+    in e.g. the 'make menuconfig' interface, though non-visible choices, menus,
+    and comments also get menu nodes. If a symbol or choice is defined in
+    multiple locations, it gets one menu node for each location.
+
+    The top-level menu node, corresponding to the implicit top-level menu, is
+    available in Kconfig.top_node.
+
+    The menu nodes for a Symbol or Choice can be found in the
+    Symbol/Choice.nodes attribute. Menus and comments are represented as plain
+    menu nodes, with their text stored in the prompt attribute (prompt[0]).
+    This mirrors the C implementation.
+
+    The following attributes are available on MenuNode instances. They should
+    be viewed as read-only.
+
+    item:
+      Either a Symbol, a Choice, or one of the constants MENU and COMMENT.
+      Menus and comments are represented as plain menu nodes. Ifs are collapsed
+      (matching the C implementation) and do not appear in the final menu tree.
+
+    next:
+      The following menu node. None if there is no following node.
+
+    list:
+      The first child menu node. None if there are no children.
+
+      Choices and menus naturally have children, but Symbols can also have
+      children because of menus created automatically from dependencies (see
+      kconfig-language.txt).
+
+    parent:
+      The parent menu node. None if there is no parent.
+
+    prompt:
+      A (string, cond) tuple with the prompt for the menu node and its
+      conditional expression (which is self.kconfig.y if there is no
+      condition). None if there is no prompt.
+
+      For symbols and choices, the prompt is stored in the MenuNode rather than
+      the Symbol or Choice instance. For menus and comments, the prompt holds
+      the text.
+
+    defaults:
+      The 'default' properties for this particular menu node. See
+      symbol.defaults.
+
+      When evaluating defaults, you should use Symbol/Choice.defaults instead,
+      as it include properties from all menu nodes (a symbol/choice can have
+      multiple definition locations/menu nodes). MenuNode.defaults is meant for
+      documentation generation.
+
+    selects:
+      Like MenuNode.defaults, for selects.
+
+    implies:
+      Like MenuNode.defaults, for implies.
+
+    ranges:
+      Like MenuNode.defaults, for ranges.
+
+    orig_prompt:
+    orig_defaults:
+    orig_selects:
+    orig_implies:
+    orig_ranges:
+      These work the like the corresponding attributes without orig_*, but omit
+      any dependencies propagated from 'depends on' and surrounding 'if's (the
+      direct dependencies, stored in MenuNode.dep).
+
+      One use for this is generating less cluttered documentation, by only
+      showing the direct dependencies in one place.
+
+    help:
+      The help text for the menu node for Symbols and Choices. None if there is
+      no help text. Always stored in the node rather than the Symbol or Choice.
+      It is possible to have a separate help text at each location if a symbol
+      is defined in multiple locations.
+
+      Trailing whitespace (including a final newline) is stripped from the help
+      text. This was not the case before Kconfiglib 10.21.0, where the format
+      was undocumented.
+
+    dep:
+      The direct ('depends on') dependencies for the menu node, or
+      self.kconfig.y if there are no direct dependencies.
+
+      This attribute includes any dependencies from surrounding menus and ifs.
+      Those get propagated to the direct dependencies, and the resulting direct
+      dependencies in turn get propagated to the conditions of all properties.
+
+      If a symbol or choice is defined in multiple locations, only the
+      properties defined at a particular location get the corresponding
+      MenuNode.dep dependencies propagated to them.
+
+    visibility:
+      The 'visible if' dependencies for the menu node (which must represent a
+      menu), or self.kconfig.y if there are no 'visible if' dependencies.
+      'visible if' dependencies are recursively propagated to the prompts of
+      symbols and choices within the menu.
+
+    referenced:
+      A set() with all symbols and choices referenced in the properties and
+      property conditions of the menu node.
+
+      Also includes dependencies inherited from surrounding menus and ifs.
+      Choices appear in the dependencies of choice symbols.
+
+    is_menuconfig:
+      Set to True if the children of the menu node should be displayed in a
+      separate menu. This is the case for the following items:
+
+        - Menus (node.item == MENU)
+
+        - Choices
+
+        - Symbols defined with the 'menuconfig' keyword. The children come from
+          implicitly created submenus, and should be displayed in a separate
+          menu rather than being indented.
+
+      'is_menuconfig' is just a hint on how to display the menu node. It's
+      ignored internally by Kconfiglib, except when printing symbols.
+
+    filename/linenr:
+      The location where the menu node appears. The filename is relative to
+      $srctree (or to the current directory if $srctree isn't set), except
+      absolute paths are used for paths outside $srctree.
+
+    include_path:
+      A tuple of (filename, linenr) tuples, giving the locations of the
+      'source' statements via which the Kconfig file containing this menu node
+      was included. The first element is the location of the 'source' statement
+      in the top-level Kconfig file passed to Kconfig.__init__(), etc.
+
+      Note that the Kconfig file of the menu node itself isn't included. Check
+      'filename' and 'linenr' for that.
+
+    kconfig:
+      The Kconfig instance the menu node is from.
+    """
+    __slots__ = (
+        "dep",
+        "filename",
+        "help",
+        "include_path",
+        "is_menuconfig",
+        "item",
+        "kconfig",
+        "linenr",
+        "list",
+        "next",
+        "parent",
+        "prompt",
+        "visibility",
+
+        # Properties
+        "defaults",
+        "selects",
+        "implies",
+        "ranges",
+    )
+
+    def __init__(self):
+        # Properties defined on this particular menu node. A local 'depends on'
+        # only applies to these, in case a symbol is defined in multiple
+        # locations.
+        self.defaults = []
+        self.selects = []
+        self.implies = []
+        self.ranges = []
+
+    @property
+    def orig_prompt(self):
+        """
+        See the class documentation.
+        """
+        if not self.prompt:
+            return None
+        return (self.prompt[0], self._strip_dep(self.prompt[1]))
+
+    @property
+    def orig_defaults(self):
+        """
+        See the class documentation.
+        """
+        return [(default, self._strip_dep(cond))
+                for default, cond in self.defaults]
+
+    @property
+    def orig_selects(self):
+        """
+        See the class documentation.
+        """
+        return [(select, self._strip_dep(cond))
+                for select, cond in self.selects]
+
+    @property
+    def orig_implies(self):
+        """
+        See the class documentation.
+        """
+        return [(imply, self._strip_dep(cond))
+                for imply, cond in self.implies]
+
+    @property
+    def orig_ranges(self):
+        """
+        See the class documentation.
+        """
+        return [(low, high, self._strip_dep(cond))
+                for low, high, cond in self.ranges]
+
+    @property
+    def referenced(self):
+        """
+        See the class documentation.
+        """
+        # self.dep is included to catch dependencies from a lone 'depends on'
+        # when there are no properties to propagate it to
+        res = expr_items(self.dep)
+
+        if self.prompt:
+            res |= expr_items(self.prompt[1])
+
+        if self.item is MENU:
+            res |= expr_items(self.visibility)
+
+        for value, cond in self.defaults:
+            res |= expr_items(value)
+            res |= expr_items(cond)
+
+        for value, cond in self.selects:
+            res.add(value)
+            res |= expr_items(cond)
+
+        for value, cond in self.implies:
+            res.add(value)
+            res |= expr_items(cond)
+
+        for low, high, cond in self.ranges:
+            res.add(low)
+            res.add(high)
+            res |= expr_items(cond)
+
+        return res
+
+    def __repr__(self):
+        """
+        Returns a string with information about the menu node when it is
+        evaluated on e.g. the interactive Python prompt.
+        """
+        fields = []
+        add = fields.append
+
+        if self.item.__class__ is Symbol:
+            add("menu node for symbol " + self.item.name)
+
+        elif self.item.__class__ is Choice:
+            s = "menu node for choice"
+            if self.item.name is not None:
+                s += " " + self.item.name
+            add(s)
+
+        elif self.item is MENU:
+            add("menu node for menu")
+
+        else:  # self.item is COMMENT
+            add("menu node for comment")
+
+        if self.prompt:
+            add('prompt "{}" (visibility {})'.format(
+                self.prompt[0], TRI_TO_STR[expr_value(self.prompt[1])]))
+
+        if self.item.__class__ is Symbol and self.is_menuconfig:
+            add("is menuconfig")
+
+        add("deps " + TRI_TO_STR[expr_value(self.dep)])
+
+        if self.item is MENU:
+            add("'visible if' deps " + TRI_TO_STR[expr_value(self.visibility)])
+
+        if self.item.__class__ in _SYMBOL_CHOICE and self.help is not None:
+            add("has help")
+
+        if self.list:
+            add("has child")
+
+        if self.next:
+            add("has next")
+
+        add("{}:{}".format(self.filename, self.linenr))
+
+        return "<{}>".format(", ".join(fields))
+
+    def __str__(self):
+        """
+        Returns a string representation of the menu node. Matches the Kconfig
+        format, with any parent dependencies propagated to the 'depends on'
+        condition.
+
+        The output could (almost) be fed back into a Kconfig parser to redefine
+        the object associated with the menu node. See the module documentation
+        for a gotcha related to choice symbols.
+
+        For symbols and choices with multiple menu nodes (multiple definition
+        locations), properties that aren't associated with a particular menu
+        node are shown on all menu nodes ('option env=...', 'optional' for
+        choices, etc.).
+
+        The returned string does not end in a newline.
+        """
+        return self.custom_str(standard_sc_expr_str)
+
+    def custom_str(self, sc_expr_str_fn):
+        """
+        Works like MenuNode.__str__(), but allows a custom format to be used
+        for all symbol/choice references. See expr_str().
+        """
+        return self._menu_comment_node_str(sc_expr_str_fn) \
+               if self.item in _MENU_COMMENT else \
+               self._sym_choice_node_str(sc_expr_str_fn)
+
+    def _menu_comment_node_str(self, sc_expr_str_fn):
+        s = '{} "{}"'.format("menu" if self.item is MENU else "comment",
+                             self.prompt[0])
+
+        if self.dep is not self.kconfig.y:
+            s += "\n\tdepends on {}".format(expr_str(self.dep, sc_expr_str_fn))
+
+        if self.item is MENU and self.visibility is not self.kconfig.y:
+            s += "\n\tvisible if {}".format(expr_str(self.visibility,
+                                                     sc_expr_str_fn))
+
+        return s
+
+    def _sym_choice_node_str(self, sc_expr_str_fn):
+        def indent_add(s):
+            lines.append("\t" + s)
+
+        def indent_add_cond(s, cond):
+            if cond is not self.kconfig.y:
+                s += " if " + expr_str(cond, sc_expr_str_fn)
+            indent_add(s)
+
+        sc = self.item
+
+        if sc.__class__ is Symbol:
+            lines = [("menuconfig " if self.is_menuconfig else "config ")
+                     + sc.name]
+        else:
+            lines = ["choice " + sc.name if sc.name else "choice"]
+
+        if sc.orig_type and not self.prompt:  # sc.orig_type != UNKNOWN
+            # If there's a prompt, we'll use the '<type> "prompt"' shorthand
+            # instead
+            indent_add(TYPE_TO_STR[sc.orig_type])
+
+        if self.prompt:
+            if sc.orig_type:
+                prefix = TYPE_TO_STR[sc.orig_type]
+            else:
+                # Symbol defined without a type (which generates a warning)
+                prefix = "prompt"
+
+            indent_add_cond(prefix + ' "{}"'.format(escape(self.prompt[0])),
+                            self.orig_prompt[1])
+
+        if sc.__class__ is Symbol:
+            if sc.is_allnoconfig_y:
+                indent_add("option allnoconfig_y")
+
+            if sc is sc.kconfig.defconfig_list:
+                indent_add("option defconfig_list")
+
+            if sc.env_var is not None:
+                indent_add('option env="{}"'.format(sc.env_var))
+
+            if sc is sc.kconfig.modules:
+                indent_add("option modules")
+
+            for low, high, cond in self.orig_ranges:
+                indent_add_cond(
+                    "range {} {}".format(sc_expr_str_fn(low),
+                                         sc_expr_str_fn(high)),
+                    cond)
+
+        for default, cond in self.orig_defaults:
+            indent_add_cond("default " + expr_str(default, sc_expr_str_fn),
+                            cond)
+
+        if sc.__class__ is Choice and sc.is_optional:
+            indent_add("optional")
+
+        if sc.__class__ is Symbol:
+            for select, cond in self.orig_selects:
+                indent_add_cond("select " + sc_expr_str_fn(select), cond)
+
+            for imply, cond in self.orig_implies:
+                indent_add_cond("imply " + sc_expr_str_fn(imply), cond)
+
+        if self.dep is not sc.kconfig.y:
+            indent_add("depends on " + expr_str(self.dep, sc_expr_str_fn))
+
+        if self.help is not None:
+            indent_add("help")
+            for line in self.help.splitlines():
+                indent_add("  " + line)
+
+        return "\n".join(lines)
+
+    def _strip_dep(self, expr):
+        # Helper function for removing MenuNode.dep from 'expr'. Uses two
+        # pieces of internal knowledge: (1) Expressions are reused rather than
+        # copied, and (2) the direct dependencies always appear at the end.
+
+        # ... if dep -> ... if y
+        if self.dep is expr:
+            return self.kconfig.y
+
+        # (AND, X, dep) -> X
+        if expr.__class__ is tuple and expr[0] is AND and expr[2] is self.dep:
+            return expr[1]
+
+        return expr
+
+
+class Variable(object):
+    """
+    Represents a preprocessor variable/function.
+
+    The following attributes are available:
+
+    name:
+      The name of the variable.
+
+    value:
+      The unexpanded value of the variable.
+
+    expanded_value:
+      The expanded value of the variable. For simple variables (those defined
+      with :=), this will equal 'value'. Accessing this property will raise a
+      KconfigError if the expansion seems to be stuck in a loop.
+
+      Accessing this field is the same as calling expanded_value_w_args() with
+      no arguments. I hadn't considered function arguments when adding it. It
+      is retained for backwards compatibility though.
+
+    is_recursive:
+      True if the variable is recursive (defined with =).
+    """
+    __slots__ = (
+        "_n_expansions",
+        "is_recursive",
+        "kconfig",
+        "name",
+        "value",
+    )
+
+    @property
+    def expanded_value(self):
+        """
+        See the class documentation.
+        """
+        return self.expanded_value_w_args()
+
+    def expanded_value_w_args(self, *args):
+        """
+        Returns the expanded value of the variable/function. Any arguments
+        passed will be substituted for $(1), $(2), etc.
+
+        Raises a KconfigError if the expansion seems to be stuck in a loop.
+        """
+        return self.kconfig._fn_val((self.name,) + args)
+
+    def __repr__(self):
+        return "<variable {}, {}, value '{}'>" \
+               .format(self.name,
+                       "recursive" if self.is_recursive else "immediate",
+                       self.value)
+
+
+class KconfigError(Exception):
+    """
+    Exception raised for Kconfig-related errors.
+
+    KconfigError and KconfigSyntaxError are the same class. The
+    KconfigSyntaxError alias is only maintained for backwards compatibility.
+    """
+
+KconfigSyntaxError = KconfigError  # Backwards compatibility
+
+
+class InternalError(Exception):
+    "Never raised. Kept around for backwards compatibility."
+
+
+# Workaround:
+#
+# If 'errno' and 'strerror' are set on IOError, then __str__() always returns
+# "[Errno <errno>] <strerror>", ignoring any custom message passed to the
+# constructor. By defining our own subclass, we can use a custom message while
+# also providing 'errno', 'strerror', and 'filename' to scripts.
+class _KconfigIOError(IOError):
+    def __init__(self, ioerror, msg):
+        self.msg = msg
+        super(_KconfigIOError, self).__init__(
+            ioerror.errno, ioerror.strerror, ioerror.filename)
+
+    def __str__(self):
+        return self.msg
+
+
+#
+# Public functions
+#
+
+
+def expr_value(expr):
+    """
+    Evaluates the expression 'expr' to a tristate value. Returns 0 (n), 1 (m),
+    or 2 (y).
+
+    'expr' must be an already-parsed expression from a Symbol, Choice, or
+    MenuNode property. To evaluate an expression represented as a string, use
+    Kconfig.eval_string().
+
+    Passing subexpressions of expressions to this function works as expected.
+    """
+    if expr.__class__ is not tuple:
+        return expr.tri_value
+
+    if expr[0] is AND:
+        v1 = expr_value(expr[1])
+        # Short-circuit the n case as an optimization (~5% faster
+        # allnoconfig.py and allyesconfig.py, as of writing)
+        return 0 if not v1 else min(v1, expr_value(expr[2]))
+
+    if expr[0] is OR:
+        v1 = expr_value(expr[1])
+        # Short-circuit the y case as an optimization
+        return 2 if v1 == 2 else max(v1, expr_value(expr[2]))
+
+    if expr[0] is NOT:
+        return 2 - expr_value(expr[1])
+
+    # Relation
+    #
+    # Implements <, <=, >, >= comparisons as well. These were added to
+    # kconfig in 31847b67 (kconfig: allow use of relations other than
+    # (in)equality).
+
+    rel, v1, v2 = expr
+
+    # If both operands are strings...
+    if v1.orig_type is STRING and v2.orig_type is STRING:
+        # ...then compare them lexicographically
+        comp = _strcmp(v1.str_value, v2.str_value)
+    else:
+        # Otherwise, try to compare them as numbers
+        try:
+            comp = _sym_to_num(v1) - _sym_to_num(v2)
+        except ValueError:
+            # Fall back on a lexicographic comparison if the operands don't
+            # parse as numbers
+            comp = _strcmp(v1.str_value, v2.str_value)
+
+    return 2*(comp == 0 if rel is EQUAL else
+              comp != 0 if rel is UNEQUAL else
+              comp <  0 if rel is LESS else
+              comp <= 0 if rel is LESS_EQUAL else
+              comp >  0 if rel is GREATER else
+              comp >= 0)
+
+
+def standard_sc_expr_str(sc):
+    """
+    Standard symbol/choice printing function. Uses plain Kconfig syntax, and
+    displays choices as <choice> (or <choice NAME>, for named choices).
+
+    See expr_str().
+    """
+    if sc.__class__ is Symbol:
+        if sc.is_constant and sc.name not in STR_TO_TRI:
+            return '"{}"'.format(escape(sc.name))
+        return sc.name
+
+    return "<choice {}>".format(sc.name) if sc.name else "<choice>"
+
+
+def expr_str(expr, sc_expr_str_fn=standard_sc_expr_str):
+    """
+    Returns the string representation of the expression 'expr', as in a Kconfig
+    file.
+
+    Passing subexpressions of expressions to this function works as expected.
+
+    sc_expr_str_fn (default: standard_sc_expr_str):
+      This function is called for every symbol/choice (hence "sc") appearing in
+      the expression, with the symbol/choice as the argument. It is expected to
+      return a string to be used for the symbol/choice.
+
+      This can be used e.g. to turn symbols/choices into links when generating
+      documentation, or for printing the value of each symbol/choice after it.
+
+      Note that quoted values are represented as constants symbols
+      (Symbol.is_constant == True).
+    """
+    if expr.__class__ is not tuple:
+        return sc_expr_str_fn(expr)
+
+    if expr[0] is AND:
+        return "{} && {}".format(_parenthesize(expr[1], OR, sc_expr_str_fn),
+                                 _parenthesize(expr[2], OR, sc_expr_str_fn))
+
+    if expr[0] is OR:
+        # This turns A && B || C && D into "(A && B) || (C && D)", which is
+        # redundant, but more readable
+        return "{} || {}".format(_parenthesize(expr[1], AND, sc_expr_str_fn),
+                                 _parenthesize(expr[2], AND, sc_expr_str_fn))
+
+    if expr[0] is NOT:
+        if expr[1].__class__ is tuple:
+            return "!({})".format(expr_str(expr[1], sc_expr_str_fn))
+        return "!" + sc_expr_str_fn(expr[1])  # Symbol
+
+    # Relation
+    #
+    # Relation operands are always symbols (quoted strings are constant
+    # symbols)
+    return "{} {} {}".format(sc_expr_str_fn(expr[1]), REL_TO_STR[expr[0]],
+                             sc_expr_str_fn(expr[2]))
+
+
+def expr_items(expr):
+    """
+    Returns a set() of all items (symbols and choices) that appear in the
+    expression 'expr'.
+
+    Passing subexpressions of expressions to this function works as expected.
+    """
+    res = set()
+
+    def rec(subexpr):
+        if subexpr.__class__ is tuple:
+            # AND, OR, NOT, or relation
+
+            rec(subexpr[1])
+
+            # NOTs only have a single operand
+            if subexpr[0] is not NOT:
+                rec(subexpr[2])
+
+        else:
+            # Symbol or choice
+            res.add(subexpr)
+
+    rec(expr)
+    return res
+
+
+def split_expr(expr, op):
+    """
+    Returns a list containing the top-level AND or OR operands in the
+    expression 'expr', in the same (left-to-right) order as they appear in
+    the expression.
+
+    This can be handy e.g. for splitting (weak) reverse dependencies
+    from 'select' and 'imply' into individual selects/implies.
+
+    op:
+      Either AND to get AND operands, or OR to get OR operands.
+
+      (Having this as an operand might be more future-safe than having two
+      hardcoded functions.)
+
+
+    Pseudo-code examples:
+
+      split_expr( A                    , OR  )  ->  [A]
+      split_expr( A && B               , OR  )  ->  [A && B]
+      split_expr( A || B               , OR  )  ->  [A, B]
+      split_expr( A || B               , AND )  ->  [A || B]
+      split_expr( A || B || (C && D)   , OR  )  ->  [A, B, C && D]
+
+      # Second || is not at the top level
+      split_expr( A || (B && (C || D)) , OR )  ->  [A, B && (C || D)]
+
+      # Parentheses don't matter as long as we stay at the top level (don't
+      # encounter any non-'op' nodes)
+      split_expr( (A || B) || C        , OR )  ->  [A, B, C]
+      split_expr( A || (B || C)        , OR )  ->  [A, B, C]
+    """
+    res = []
+
+    def rec(subexpr):
+        if subexpr.__class__ is tuple and subexpr[0] is op:
+            rec(subexpr[1])
+            rec(subexpr[2])
+        else:
+            res.append(subexpr)
+
+    rec(expr)
+    return res
+
+
+def escape(s):
+    r"""
+    Escapes the string 's' in the same fashion as is done for display in
+    Kconfig format and when writing strings to a .config file. " and \ are
+    replaced by \" and \\, respectively.
+    """
+    # \ must be escaped before " to avoid double escaping
+    return s.replace("\\", r"\\").replace('"', r'\"')
+
+
+def unescape(s):
+    r"""
+    Unescapes the string 's'. \ followed by any character is replaced with just
+    that character. Used internally when reading .config files.
+    """
+    return _unescape_sub(r"\1", s)
+
+# unescape() helper
+_unescape_sub = re.compile(r"\\(.)").sub
+
+
+def standard_kconfig(description=None):
+    """
+    Argument parsing helper for tools that take a single optional Kconfig file
+    argument (default: Kconfig). Returns the Kconfig instance for the parsed
+    configuration. Uses argparse internally.
+
+    Exits with sys.exit() (which raises SystemExit) on errors.
+
+    description (default: None):
+      The 'description' passed to argparse.ArgumentParser().
+      argparse.RawDescriptionHelpFormatter is used, so formatting is preserved.
+    """
+    import argparse
+
+    parser = argparse.ArgumentParser(
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        description=description)
+
+    parser.add_argument(
+        "kconfig",
+        metavar="KCONFIG",
+        default="Kconfig",
+        nargs="?",
+        help="Top-level Kconfig file (default: Kconfig)")
+
+    return Kconfig(parser.parse_args().kconfig, suppress_traceback=True)
+
+
+def standard_config_filename():
+    """
+    Helper for tools. Returns the value of KCONFIG_CONFIG (which specifies the
+    .config file to load/save) if it is set, and ".config" otherwise.
+
+    Calling load_config() with filename=None might give the behavior you want,
+    without having to use this function.
+    """
+    return os.getenv("KCONFIG_CONFIG", ".config")
+
+
+def load_allconfig(kconf, filename):
+    """
+    Use Kconfig.load_allconfig() instead, which was added in Kconfiglib 13.4.0.
+    Supported for backwards compatibility. Might be removed at some point after
+    a long period of deprecation warnings.
+    """
+    allconfig = os.getenv("KCONFIG_ALLCONFIG")
+    if allconfig is None:
+        return
+
+    def std_msg(e):
+        # "Upcasts" a _KconfigIOError to an IOError, removing the custom
+        # __str__() message. The standard message is better here.
+        #
+        # This might also convert an OSError to an IOError in obscure cases,
+        # but it's probably not a big deal. The distinction is shaky (see
+        # PEP-3151).
+        return IOError(e.errno, e.strerror, e.filename)
+
+    old_warn_assign_override = kconf.warn_assign_override
+    old_warn_assign_redun = kconf.warn_assign_redun
+    kconf.warn_assign_override = kconf.warn_assign_redun = False
+
+    if allconfig in ("", "1"):
+        try:
+            print(kconf.load_config(filename, False))
+        except EnvironmentError as e1:
+            try:
+                print(kconf.load_config("all.config", False))
+            except EnvironmentError as e2:
+                sys.exit("error: KCONFIG_ALLCONFIG is set, but neither {} "
+                         "nor all.config could be opened: {}, {}"
+                         .format(filename, std_msg(e1), std_msg(e2)))
+    else:
+        try:
+            print(kconf.load_config(allconfig, False))
+        except EnvironmentError as e:
+            sys.exit("error: KCONFIG_ALLCONFIG is set to '{}', which "
+                     "could not be opened: {}"
+                     .format(allconfig, std_msg(e)))
+
+    kconf.warn_assign_override = old_warn_assign_override
+    kconf.warn_assign_redun = old_warn_assign_redun
+
+
+#
+# Internal functions
+#
+
+
+def _visibility(sc):
+    # Symbols and Choices have a "visibility" that acts as an upper bound on
+    # the values a user can set for them, corresponding to the visibility in
+    # e.g. 'make menuconfig'. This function calculates the visibility for the
+    # Symbol or Choice 'sc' -- the logic is nearly identical.
+
+    vis = 0
+
+    for node in sc.nodes:
+        if node.prompt:
+            vis = max(vis, expr_value(node.prompt[1]))
+
+    if sc.__class__ is Symbol and sc.choice:
+        if sc.choice.orig_type is TRISTATE and \
+           sc.orig_type is not TRISTATE and sc.choice.tri_value != 2:
+            # Non-tristate choice symbols are only visible in y mode
+            return 0
+
+        if sc.orig_type is TRISTATE and vis == 1 and sc.choice.tri_value == 2:
+            # Choice symbols with m visibility are not visible in y mode
+            return 0
+
+    # Promote m to y if we're dealing with a non-tristate (possibly due to
+    # modules being disabled)
+    if vis == 1 and sc.type is not TRISTATE:
+        return 2
+
+    return vis
+
+
+def _depend_on(sc, expr):
+    # Adds 'sc' (symbol or choice) as a "dependee" to all symbols in 'expr'.
+    # Constant symbols in 'expr' are skipped as they can never change value
+    # anyway.
+
+    if expr.__class__ is tuple:
+        # AND, OR, NOT, or relation
+
+        _depend_on(sc, expr[1])
+
+        # NOTs only have a single operand
+        if expr[0] is not NOT:
+            _depend_on(sc, expr[2])
+
+    elif not expr.is_constant:
+        # Non-constant symbol, or choice
+        expr._dependents.add(sc)
+
+
+def _parenthesize(expr, type_, sc_expr_str_fn):
+    # expr_str() helper. Adds parentheses around expressions of type 'type_'.
+
+    if expr.__class__ is tuple and expr[0] is type_:
+        return "({})".format(expr_str(expr, sc_expr_str_fn))
+    return expr_str(expr, sc_expr_str_fn)
+
+
+def _ordered_unique(lst):
+    # Returns 'lst' with any duplicates removed, preserving order. This hacky
+    # version seems to be a common idiom. It relies on short-circuit evaluation
+    # and set.add() returning None, which is falsy.
+
+    seen = set()
+    seen_add = seen.add
+    return [x for x in lst if x not in seen and not seen_add(x)]
+
+
+def _is_base_n(s, n):
+    try:
+        int(s, n)
+        return True
+    except ValueError:
+        return False
+
+
+def _strcmp(s1, s2):
+    # strcmp()-alike that returns -1, 0, or 1
+
+    return (s1 > s2) - (s1 < s2)
+
+
+def _sym_to_num(sym):
+    # expr_value() helper for converting a symbol to a number. Raises
+    # ValueError for symbols that can't be converted.
+
+    # For BOOL and TRISTATE, n/m/y count as 0/1/2. This mirrors 9059a3493ef
+    # ("kconfig: fix relational operators for bool and tristate symbols") in
+    # the C implementation.
+    return sym.tri_value if sym.orig_type in _BOOL_TRISTATE else \
+           int(sym.str_value, _TYPE_TO_BASE[sym.orig_type])
+
+
+def _touch_dep_file(path, sym_name):
+    # If sym_name is MY_SYM_NAME, touches my/sym/name.h. See the sync_deps()
+    # docstring.
+
+    sym_path = path + os.sep + sym_name.lower().replace("_", os.sep) + ".h"
+    sym_path_dir = dirname(sym_path)
+    if not exists(sym_path_dir):
+        os.makedirs(sym_path_dir, 0o755)
+
+    # A kind of truncating touch, mirroring the C tools
+    os.close(os.open(
+        sym_path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o644))
+
+
+def _save_old(path):
+    # See write_config()
+
+    def copy(src, dst):
+        # Import as needed, to save some startup time
+        import shutil
+        shutil.copyfile(src, dst)
+
+    if islink(path):
+        # Preserve symlinks
+        copy_fn = copy
+    elif hasattr(os, "replace"):
+        # Python 3 (3.3+) only. Best choice when available, because it
+        # removes <filename>.old on both *nix and Windows.
+        copy_fn = os.replace
+    elif os.name == "posix":
+        # Removes <filename>.old on POSIX systems
+        copy_fn = os.rename
+    else:
+        # Fall back on copying
+        copy_fn = copy
+
+    try:
+        copy_fn(path, path + ".old")
+    except Exception:
+        # Ignore errors from 'path' missing as well as other errors.
+        # <filename>.old file is usually more of a nice-to-have, and not worth
+        # erroring out over e.g. if <filename>.old happens to be a directory or
+        # <filename> is something like /dev/null.
+        pass
+
+
+def _locs(sc):
+    # Symbol/Choice.name_and_loc helper. Returns the "(defined at ...)" part of
+    # the string. 'sc' is a Symbol or Choice.
+
+    if sc.nodes:
+        return "(defined at {})".format(
+            ", ".join("{0.filename}:{0.linenr}".format(node)
+                      for node in sc.nodes))
+
+    return "(undefined)"
+
+
+# Menu manipulation
+
+
+def _expr_depends_on(expr, sym):
+    # Reimplementation of expr_depends_symbol() from mconf.c. Used to determine
+    # if a submenu should be implicitly created. This also influences which
+    # items inside choice statements are considered choice items.
+
+    if expr.__class__ is not tuple:
+        return expr is sym
+
+    if expr[0] in _EQUAL_UNEQUAL:
+        # Check for one of the following:
+        # sym = m/y, m/y = sym, sym != n, n != sym
+
+        left, right = expr[1:]
+
+        if right is sym:
+            left, right = right, left
+        elif left is not sym:
+            return False
+
+        return (expr[0] is EQUAL and right is sym.kconfig.m or
+                                     right is sym.kconfig.y) or \
+               (expr[0] is UNEQUAL and right is sym.kconfig.n)
+
+    return expr[0] is AND and \
+           (_expr_depends_on(expr[1], sym) or
+            _expr_depends_on(expr[2], sym))
+
+
+def _auto_menu_dep(node1, node2):
+    # Returns True if node2 has an "automatic menu dependency" on node1. If
+    # node2 has a prompt, we check its condition. Otherwise, we look directly
+    # at node2.dep.
+
+    return _expr_depends_on(node2.prompt[1] if node2.prompt else node2.dep,
+                            node1.item)
+
+
+def _flatten(node):
+    # "Flattens" menu nodes without prompts (e.g. 'if' nodes and non-visible
+    # symbols with children from automatic menu creation) so that their
+    # children appear after them instead. This gives a clean menu structure
+    # with no unexpected "jumps" in the indentation.
+    #
+    # Do not flatten promptless choices (which can appear "legitimately" if a
+    # named choice is defined in multiple locations to add on symbols). It
+    # looks confusing, and the menuconfig already shows all choice symbols if
+    # you enter the choice at some location with a prompt.
+
+    while node:
+        if node.list and not node.prompt and \
+           node.item.__class__ is not Choice:
+
+            last_node = node.list
+            while 1:
+                last_node.parent = node.parent
+                if not last_node.next:
+                    break
+                last_node = last_node.next
+
+            last_node.next = node.next
+            node.next = node.list
+            node.list = None
+
+        node = node.next
+
+
+def _remove_ifs(node):
+    # Removes 'if' nodes (which can be recognized by MenuNode.item being None),
+    # which are assumed to already have been flattened. The C implementation
+    # doesn't bother to do this, but we expose the menu tree directly, and it
+    # makes it nicer to work with.
+
+    cur = node.list
+    while cur and not cur.item:
+        cur = cur.next
+
+    node.list = cur
+
+    while cur:
+        next = cur.next
+        while next and not next.item:
+            next = next.next
+
+        # Equivalent to
+        #
+        #   cur.next = next
+        #   cur = next
+        #
+        # due to tricky Python semantics. The order matters.
+        cur.next = cur = next
+
+
+def _finalize_choice(node):
+    # Finalizes a choice, marking each symbol whose menu node has the choice as
+    # the parent as a choice symbol, and automatically determining types if not
+    # specified.
+
+    choice = node.item
+
+    cur = node.list
+    while cur:
+        if cur.item.__class__ is Symbol:
+            cur.item.choice = choice
+            choice.syms.append(cur.item)
+        cur = cur.next
+
+    # If no type is specified for the choice, its type is that of
+    # the first choice item with a specified type
+    if not choice.orig_type:
+        for item in choice.syms:
+            if item.orig_type:
+                choice.orig_type = item.orig_type
+                break
+
+    # Each choice item of UNKNOWN type gets the type of the choice
+    for sym in choice.syms:
+        if not sym.orig_type:
+            sym.orig_type = choice.orig_type
+
+
+def _check_dep_loop_sym(sym, ignore_choice):
+    # Detects dependency loops using depth-first search on the dependency graph
+    # (which is calculated earlier in Kconfig._build_dep()).
+    #
+    # Algorithm:
+    #
+    #  1. Symbols/choices start out with _visited = 0, meaning unvisited.
+    #
+    #  2. When a symbol/choice is first visited, _visited is set to 1, meaning
+    #     "visited, potentially part of a dependency loop". The recursive
+    #     search then continues from the symbol/choice.
+    #
+    #  3. If we run into a symbol/choice X with _visited already set to 1,
+    #     there's a dependency loop. The loop is found on the call stack by
+    #     recording symbols while returning ("on the way back") until X is seen
+    #     again.
+    #
+    #  4. Once a symbol/choice and all its dependencies (or dependents in this
+    #     case) have been checked recursively without detecting any loops, its
+    #     _visited is set to 2, meaning "visited, not part of a dependency
+    #     loop".
+    #
+    #     This saves work if we run into the symbol/choice again in later calls
+    #     to _check_dep_loop_sym(). We just return immediately.
+    #
+    # Choices complicate things, as every choice symbol depends on every other
+    # choice symbol in a sense. When a choice is "entered" via a choice symbol
+    # X, we visit all choice symbols from the choice except X, and prevent
+    # immediately revisiting the choice with a flag (ignore_choice).
+    #
+    # Maybe there's a better way to handle this (different flags or the
+    # like...)
+
+    if not sym._visited:
+        # sym._visited == 0, unvisited
+
+        sym._visited = 1
+
+        for dep in sym._dependents:
+            # Choices show up in Symbol._dependents when the choice has the
+            # symbol in a 'prompt' or 'default' condition (e.g.
+            # 'default ... if SYM').
+            #
+            # Since we aren't entering the choice via a choice symbol, all
+            # choice symbols need to be checked, hence the None.
+            loop = _check_dep_loop_choice(dep, None) \
+                   if dep.__class__ is Choice \
+                   else _check_dep_loop_sym(dep, False)
+
+            if loop:
+                # Dependency loop found
+                return _found_dep_loop(loop, sym)
+
+        if sym.choice and not ignore_choice:
+            loop = _check_dep_loop_choice(sym.choice, sym)
+            if loop:
+                # Dependency loop found
+                return _found_dep_loop(loop, sym)
+
+        # The symbol is not part of a dependency loop
+        sym._visited = 2
+
+        # No dependency loop found
+        return None
+
+    if sym._visited == 2:
+        # The symbol was checked earlier and is already known to not be part of
+        # a dependency loop
+        return None
+
+    # sym._visited == 1, found a dependency loop. Return the symbol as the
+    # first element in it.
+    return (sym,)
+
+
+def _check_dep_loop_choice(choice, skip):
+    if not choice._visited:
+        # choice._visited == 0, unvisited
+
+        choice._visited = 1
+
+        # Check for loops involving choice symbols. If we came here via a
+        # choice symbol, skip that one, as we'd get a false positive
+        # '<sym FOO> -> <choice> -> <sym FOO>' loop otherwise.
+        for sym in choice.syms:
+            if sym is not skip:
+                # Prevent the choice from being immediately re-entered via the
+                # "is a choice symbol" path by passing True
+                loop = _check_dep_loop_sym(sym, True)
+                if loop:
+                    # Dependency loop found
+                    return _found_dep_loop(loop, choice)
+
+        # The choice is not part of a dependency loop
+        choice._visited = 2
+
+        # No dependency loop found
+        return None
+
+    if choice._visited == 2:
+        # The choice was checked earlier and is already known to not be part of
+        # a dependency loop
+        return None
+
+    # choice._visited == 1, found a dependency loop. Return the choice as the
+    # first element in it.
+    return (choice,)
+
+
+def _found_dep_loop(loop, cur):
+    # Called "on the way back" when we know we have a loop
+
+    # Is the symbol/choice 'cur' where the loop started?
+    if cur is not loop[0]:
+        # Nope, it's just a part of the loop
+        return loop + (cur,)
+
+    # Yep, we have the entire loop. Throw an exception that shows it.
+
+    msg = "\nDependency loop\n" \
+            "===============\n\n"
+
+    for item in loop:
+        if item is not loop[0]:
+            msg += "...depends on "
+            if item.__class__ is Symbol and item.choice:
+                msg += "the choice symbol "
+
+        msg += "{}, with definition...\n\n{}\n\n" \
+               .format(item.name_and_loc, item)
+
+        # Small wart: Since we reuse the already calculated
+        # Symbol/Choice._dependents sets for recursive dependency detection, we
+        # lose information on whether a dependency came from a 'select'/'imply'
+        # condition or e.g. a 'depends on'.
+        #
+        # This might cause selecting symbols to "disappear". For example,
+        # a symbol B having 'select A if C' gives a direct dependency from A to
+        # C, since it corresponds to a reverse dependency of B && C.
+        #
+        # Always print reverse dependencies for symbols that have them to make
+        # sure information isn't lost. I wonder if there's some neat way to
+        # improve this.
+
+        if item.__class__ is Symbol:
+            if item.rev_dep is not item.kconfig.n:
+                msg += "(select-related dependencies: {})\n\n" \
+                       .format(expr_str(item.rev_dep))
+
+            if item.weak_rev_dep is not item.kconfig.n:
+                msg += "(imply-related dependencies: {})\n\n" \
+                       .format(expr_str(item.rev_dep))
+
+    msg += "...depends again on " + loop[0].name_and_loc
+
+    raise KconfigError(msg)
+
+
+def _decoding_error(e, filename, macro_linenr=None):
+    # Gives the filename and context for UnicodeDecodeError's, which are a pain
+    # to debug otherwise. 'e' is the UnicodeDecodeError object.
+    #
+    # If the decoding error is for the output of a $(shell,...) command,
+    # macro_linenr holds the line number where it was run (the exact line
+    # number isn't available for decoding errors in files).
+
+    raise KconfigError(
+        "\n"
+        "Malformed {} in {}\n"
+        "Context: {}\n"
+        "Problematic data: {}\n"
+        "Reason: {}".format(
+            e.encoding,
+            "'{}'".format(filename) if macro_linenr is None else
+                "output from macro at {}:{}".format(filename, macro_linenr),
+            e.object[max(e.start - 40, 0):e.end + 40],
+            e.object[e.start:e.end],
+            e.reason))
+
+
+def _warn_verbose_deprecated(fn_name):
+    sys.stderr.write(
+        "Deprecation warning: {0}()'s 'verbose' argument has no effect. Since "
+        "Kconfiglib 12.0.0, the message is returned from {0}() instead, "
+        "and is always generated. Do e.g. print(kconf.{0}()) if you want to "
+        "want to show a message like \"Loaded configuration '.config'\" on "
+        "stdout. The old API required ugly hacks to reuse messages in "
+        "configuration interfaces.\n".format(fn_name))
+
+
+# Predefined preprocessor functions
+
+
+def _filename_fn(kconf, _):
+    return kconf.filename
+
+
+def _lineno_fn(kconf, _):
+    return str(kconf.linenr)
+
+
+def _info_fn(kconf, _, msg):
+    print("{}:{}: {}".format(kconf.filename, kconf.linenr, msg))
+
+    return ""
+
+
+def _warning_if_fn(kconf, _, cond, msg):
+    if cond == "y":
+        kconf._warn(msg, kconf.filename, kconf.linenr)
+
+    return ""
+
+
+def _error_if_fn(kconf, _, cond, msg):
+    if cond == "y":
+        raise KconfigError("{}:{}: {}".format(
+            kconf.filename, kconf.linenr, msg))
+
+    return ""
+
+
+def _shell_fn(kconf, _, command):
+    import subprocess  # Only import as needed, to save some startup time
+
+    stdout, stderr = subprocess.Popen(
+        command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
+    ).communicate()
+
+    if not _IS_PY2:
+        try:
+            stdout = stdout.decode(kconf._encoding)
+            stderr = stderr.decode(kconf._encoding)
+        except UnicodeDecodeError as e:
+            _decoding_error(e, kconf.filename, kconf.linenr)
+
+    if stderr:
+        kconf._warn("'{}' wrote to stderr: {}".format(
+                        command, "\n".join(stderr.splitlines())),
+                    kconf.filename, kconf.linenr)
+
+    # Universal newlines with splitlines() (to prevent e.g. stray \r's in
+    # command output on Windows), trailing newline removal, and
+    # newline-to-space conversion.
+    #
+    # On Python 3 versions before 3.6, it's not possible to specify the
+    # encoding when passing universal_newlines=True to Popen() (the 'encoding'
+    # parameter was added in 3.6), so we do this manual version instead.
+    return "\n".join(stdout.splitlines()).rstrip("\n").replace("\n", " ")
+
+#
+# Global constants
+#
+
+TRI_TO_STR = {
+    0: "n",
+    1: "m",
+    2: "y",
+}
+
+STR_TO_TRI = {
+    "n": 0,
+    "m": 1,
+    "y": 2,
+}
+
+# Constant representing that there's no cached choice selection. This is
+# distinct from a cached None (no selection). Any object that's not None or a
+# Symbol will do. We test this with 'is'.
+_NO_CACHED_SELECTION = 0
+
+# Are we running on Python 2?
+_IS_PY2 = sys.version_info[0] < 3
+
+try:
+    _UNAME_RELEASE = os.uname()[2]
+except AttributeError:
+    # Only import as needed, to save some startup time
+    import platform
+    _UNAME_RELEASE = platform.uname()[2]
+
+# The token and type constants below are safe to test with 'is', which is a bit
+# faster (~30% faster on my machine, and a few % faster for total parsing
+# time), even without assuming Python's small integer optimization (which
+# caches small integer objects). The constants end up pointing to unique
+# integer objects, and since we consistently refer to them via the names below,
+# we always get the same object.
+#
+# Client code should use == though.
+
+# Tokens, with values 1, 2, ... . Avoiding 0 simplifies some checks by making
+# all tokens except empty strings truthy.
+(
+    _T_ALLNOCONFIG_Y,
+    _T_AND,
+    _T_BOOL,
+    _T_CHOICE,
+    _T_CLOSE_PAREN,
+    _T_COMMENT,
+    _T_CONFIG,
+    _T_DEFAULT,
+    _T_DEFCONFIG_LIST,
+    _T_DEF_BOOL,
+    _T_DEF_HEX,
+    _T_DEF_INT,
+    _T_DEF_STRING,
+    _T_DEF_TRISTATE,
+    _T_DEPENDS,
+    _T_ENDCHOICE,
+    _T_ENDIF,
+    _T_ENDMENU,
+    _T_ENV,
+    _T_EQUAL,
+    _T_GREATER,
+    _T_GREATER_EQUAL,
+    _T_HELP,
+    _T_HEX,
+    _T_IF,
+    _T_IMPLY,
+    _T_INT,
+    _T_LESS,
+    _T_LESS_EQUAL,
+    _T_MAINMENU,
+    _T_MENU,
+    _T_MENUCONFIG,
+    _T_MODULES,
+    _T_NOT,
+    _T_ON,
+    _T_OPEN_PAREN,
+    _T_OPTION,
+    _T_OPTIONAL,
+    _T_OR,
+    _T_ORSOURCE,
+    _T_OSOURCE,
+    _T_PROMPT,
+    _T_RANGE,
+    _T_RSOURCE,
+    _T_SELECT,
+    _T_SOURCE,
+    _T_STRING,
+    _T_TRISTATE,
+    _T_UNEQUAL,
+    _T_VISIBLE,
+) = range(1, 51)
+
+# Keyword to token map, with the get() method assigned directly as a small
+# optimization
+_get_keyword = {
+    "---help---":     _T_HELP,
+    "allnoconfig_y":  _T_ALLNOCONFIG_Y,
+    "bool":           _T_BOOL,
+    "boolean":        _T_BOOL,
+    "choice":         _T_CHOICE,
+    "comment":        _T_COMMENT,
+    "config":         _T_CONFIG,
+    "def_bool":       _T_DEF_BOOL,
+    "def_hex":        _T_DEF_HEX,
+    "def_int":        _T_DEF_INT,
+    "def_string":     _T_DEF_STRING,
+    "def_tristate":   _T_DEF_TRISTATE,
+    "default":        _T_DEFAULT,
+    "defconfig_list": _T_DEFCONFIG_LIST,
+    "depends":        _T_DEPENDS,
+    "endchoice":      _T_ENDCHOICE,
+    "endif":          _T_ENDIF,
+    "endmenu":        _T_ENDMENU,
+    "env":            _T_ENV,
+    "grsource":       _T_ORSOURCE,  # Backwards compatibility
+    "gsource":        _T_OSOURCE,   # Backwards compatibility
+    "help":           _T_HELP,
+    "hex":            _T_HEX,
+    "if":             _T_IF,
+    "imply":          _T_IMPLY,
+    "int":            _T_INT,
+    "mainmenu":       _T_MAINMENU,
+    "menu":           _T_MENU,
+    "menuconfig":     _T_MENUCONFIG,
+    "modules":        _T_MODULES,
+    "on":             _T_ON,
+    "option":         _T_OPTION,
+    "optional":       _T_OPTIONAL,
+    "orsource":       _T_ORSOURCE,
+    "osource":        _T_OSOURCE,
+    "prompt":         _T_PROMPT,
+    "range":          _T_RANGE,
+    "rsource":        _T_RSOURCE,
+    "select":         _T_SELECT,
+    "source":         _T_SOURCE,
+    "string":         _T_STRING,
+    "tristate":       _T_TRISTATE,
+    "visible":        _T_VISIBLE,
+}.get
+
+# The constants below match the value of the corresponding tokens to remove the
+# need for conversion
+
+# Node types
+MENU    = _T_MENU
+COMMENT = _T_COMMENT
+
+# Expression types
+AND           = _T_AND
+OR            = _T_OR
+NOT           = _T_NOT
+EQUAL         = _T_EQUAL
+UNEQUAL       = _T_UNEQUAL
+LESS          = _T_LESS
+LESS_EQUAL    = _T_LESS_EQUAL
+GREATER       = _T_GREATER
+GREATER_EQUAL = _T_GREATER_EQUAL
+
+REL_TO_STR = {
+    EQUAL:         "=",
+    UNEQUAL:       "!=",
+    LESS:          "<",
+    LESS_EQUAL:    "<=",
+    GREATER:       ">",
+    GREATER_EQUAL: ">=",
+}
+
+# Symbol/choice types. UNKNOWN is 0 (falsy) to simplify some checks.
+# Client code shouldn't rely on it though, as it was non-zero in
+# older versions.
+UNKNOWN  = 0
+BOOL     = _T_BOOL
+TRISTATE = _T_TRISTATE
+STRING   = _T_STRING
+INT      = _T_INT
+HEX      = _T_HEX
+
+TYPE_TO_STR = {
+    UNKNOWN:  "unknown",
+    BOOL:     "bool",
+    TRISTATE: "tristate",
+    STRING:   "string",
+    INT:      "int",
+    HEX:      "hex",
+}
+
+# Used in comparisons. 0 means the base is inferred from the format of the
+# string.
+_TYPE_TO_BASE = {
+    HEX:      16,
+    INT:      10,
+    STRING:   0,
+    UNKNOWN:  0,
+}
+
+# def_bool -> BOOL, etc.
+_DEF_TOKEN_TO_TYPE = {
+    _T_DEF_BOOL:     BOOL,
+    _T_DEF_HEX:      HEX,
+    _T_DEF_INT:      INT,
+    _T_DEF_STRING:   STRING,
+    _T_DEF_TRISTATE: TRISTATE,
+}
+
+# Tokens after which strings are expected. This is used to tell strings from
+# constant symbol references during tokenization, both of which are enclosed in
+# quotes.
+#
+# Identifier-like lexemes ("missing quotes") are also treated as strings after
+# these tokens. _T_CHOICE is included to avoid symbols being registered for
+# named choices.
+_STRING_LEX = frozenset({
+    _T_BOOL,
+    _T_CHOICE,
+    _T_COMMENT,
+    _T_HEX,
+    _T_INT,
+    _T_MAINMENU,
+    _T_MENU,
+    _T_ORSOURCE,
+    _T_OSOURCE,
+    _T_PROMPT,
+    _T_RSOURCE,
+    _T_SOURCE,
+    _T_STRING,
+    _T_TRISTATE,
+})
+
+# Various sets for quick membership tests. Gives a single global lookup and
+# avoids creating temporary dicts/tuples.
+
+_TYPE_TOKENS = frozenset({
+    _T_BOOL,
+    _T_TRISTATE,
+    _T_INT,
+    _T_HEX,
+    _T_STRING,
+})
+
+_SOURCE_TOKENS = frozenset({
+    _T_SOURCE,
+    _T_RSOURCE,
+    _T_OSOURCE,
+    _T_ORSOURCE,
+})
+
+_REL_SOURCE_TOKENS = frozenset({
+    _T_RSOURCE,
+    _T_ORSOURCE,
+})
+
+# Obligatory (non-optional) sources
+_OBL_SOURCE_TOKENS = frozenset({
+    _T_SOURCE,
+    _T_RSOURCE,
+})
+
+_BOOL_TRISTATE = frozenset({
+    BOOL,
+    TRISTATE,
+})
+
+_BOOL_TRISTATE_UNKNOWN = frozenset({
+    BOOL,
+    TRISTATE,
+    UNKNOWN,
+})
+
+_INT_HEX = frozenset({
+    INT,
+    HEX,
+})
+
+_SYMBOL_CHOICE = frozenset({
+    Symbol,
+    Choice,
+})
+
+_MENU_COMMENT = frozenset({
+    MENU,
+    COMMENT,
+})
+
+_EQUAL_UNEQUAL = frozenset({
+    EQUAL,
+    UNEQUAL,
+})
+
+_RELATIONS = frozenset({
+    EQUAL,
+    UNEQUAL,
+    LESS,
+    LESS_EQUAL,
+    GREATER,
+    GREATER_EQUAL,
+})
+
+# Helper functions for getting compiled regular expressions, with the needed
+# matching function returned directly as a small optimization.
+#
+# Use ASCII regex matching on Python 3. It's already the default on Python 2.
+
+
+def _re_match(regex):
+    return re.compile(regex, 0 if _IS_PY2 else re.ASCII).match
+
+
+def _re_search(regex):
+    return re.compile(regex, 0 if _IS_PY2 else re.ASCII).search
+
+
+# Various regular expressions used during parsing
+
+# The initial token on a line. Also eats leading and trailing whitespace, so
+# that we can jump straight to the next token (or to the end of the line if
+# there is only one token).
+#
+# This regex will also fail to match for empty lines and comment lines.
+#
+# '$' is included to detect preprocessor variable assignments with macro
+# expansions in the left-hand side.
+_command_match = _re_match(r"\s*([A-Za-z0-9_$-]+)\s*")
+
+# An identifier/keyword after the first token. Also eats trailing whitespace.
+# '$' is included to detect identifiers containing macro expansions.
+_id_keyword_match = _re_match(r"([A-Za-z0-9_$/.-]+)\s*")
+
+# A fragment in the left-hand side of a preprocessor variable assignment. These
+# are the portions between macro expansions ($(foo)). Macros are supported in
+# the LHS (variable name).
+_assignment_lhs_fragment_match = _re_match("[A-Za-z0-9_-]*")
+
+# The assignment operator and value (right-hand side) in a preprocessor
+# variable assignment
+_assignment_rhs_match = _re_match(r"\s*(=|:=|\+=)\s*(.*)")
+
+# Special characters/strings while expanding a macro ('(', ')', ',', and '$(')
+_macro_special_search = _re_search(r"\(|\)|,|\$\(")
+
+# Special characters/strings while expanding a string (quotes, '\', and '$(')
+_string_special_search = _re_search(r'"|\'|\\|\$\(')
+
+# Special characters/strings while expanding a symbol name. Also includes
+# end-of-line, in case the macro is the last thing on the line.
+_name_special_search = _re_search(r'[^A-Za-z0-9_$/.-]|\$\(|$')
+
+# A valid right-hand side for an assignment to a string symbol in a .config
+# file, including escaped characters. Extracts the contents.
+_conf_string_match = _re_match(r'"((?:[^\\"]|\\.)*)"')
diff --git a/scripts/Kconfiglib/menuconfig.py b/scripts/Kconfiglib/menuconfig.py
new file mode 100755
index 0000000..7e765d3
--- /dev/null
+++ b/scripts/Kconfiglib/menuconfig.py
@@ -0,0 +1,3278 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018-2019, Nordic Semiconductor ASA and Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Overview
+========
+
+A curses-based Python 2/3 menuconfig implementation. The interface should feel
+familiar to people used to mconf ('make menuconfig').
+
+Supports the same keys as mconf, and also supports a set of keybindings
+inspired by Vi:
+
+  J/K     : Down/Up
+  L       : Enter menu/Toggle item
+  H       : Leave menu
+  Ctrl-D/U: Page Down/Page Up
+  G/End   : Jump to end of list
+  g/Home  : Jump to beginning of list
+
+[Space] toggles values if possible, and enters menus otherwise. [Enter] works
+the other way around.
+
+The mconf feature where pressing a key jumps to a menu entry with that
+character in it in the current menu isn't supported. A jump-to feature for
+jumping directly to any symbol (including invisible symbols), choice, menu or
+comment (as in a Kconfig 'comment "Foo"') is available instead.
+
+A few different modes are available:
+
+  F: Toggle show-help mode, which shows the help text of the currently selected
+  item in the window at the bottom of the menu display. This is handy when
+  browsing through options.
+
+  C: Toggle show-name mode, which shows the symbol name before each symbol menu
+  entry
+
+  A: Toggle show-all mode, which shows all items, including currently invisible
+  items and items that lack a prompt. Invisible items are drawn in a different
+  style to make them stand out.
+
+
+Running
+=======
+
+menuconfig.py can be run either as a standalone executable or by calling the
+menuconfig() function with an existing Kconfig instance. The second option is a
+bit inflexible in that it will still load and save .config, etc.
+
+When run in standalone mode, the top-level Kconfig file to load can be passed
+as a command-line argument. With no argument, it defaults to "Kconfig".
+
+The KCONFIG_CONFIG environment variable specifies the .config file to load (if
+it exists) and save. If KCONFIG_CONFIG is unset, ".config" is used.
+
+When overwriting a configuration file, the old version is saved to
+<filename>.old (e.g. .config.old).
+
+$srctree is supported through Kconfiglib.
+
+
+Color schemes
+=============
+
+It is possible to customize the color scheme by setting the MENUCONFIG_STYLE
+environment variable. For example, setting it to 'aquatic' will enable an
+alternative, less yellow, more 'make menuconfig'-like color scheme, contributed
+by Mitja Horvat (pinkfluid).
+
+This is the current list of built-in styles:
+    - default       classic Kconfiglib theme with a yellow accent
+    - monochrome    colorless theme (uses only bold and standout) attributes,
+                    this style is used if the terminal doesn't support colors
+    - aquatic       blue-tinted style loosely resembling the lxdialog theme
+
+It is possible to customize the current style by changing colors of UI
+elements on the screen. This is the list of elements that can be stylized:
+
+    - path          Top row in the main display, with the menu path
+    - separator     Separator lines between windows. Also used for the top line
+                    in the symbol information display.
+    - list          List of items, e.g. the main display
+    - selection     Style for the selected item
+    - inv-list      Like list, but for invisible items. Used in show-all mode.
+    - inv-selection Like selection, but for invisible items. Used in show-all
+                    mode.
+    - help          Help text windows at the bottom of various fullscreen
+                    dialogs
+    - show-help     Window showing the help text in show-help mode
+    - frame         Frame around dialog boxes
+    - body          Body of dialog boxes
+    - edit          Edit box in pop-up dialogs
+    - jump-edit     Edit box in jump-to dialog
+    - text          Symbol information text
+
+The color definition is a comma separated list of attributes:
+
+    - fg:COLOR      Set the foreground/background colors. COLOR can be one of
+      * or *        the basic 16 colors (black, red, green, yellow, blue,
+    - bg:COLOR      magenta, cyan, white and brighter versions, for example,
+                    brightred). On terminals that support more than 8 colors,
+                    you can also directly put in a color number, e.g. fg:123
+                    (hexadecimal and octal constants are accepted as well).
+                    Colors outside the range -1..curses.COLORS-1 (which is
+                    terminal-dependent) are ignored (with a warning). The COLOR
+                    can be also specified using a RGB value in the HTML
+                    notation, for example #RRGGBB. If the terminal supports
+                    color changing, the color is rendered accurately.
+                    Otherwise, the visually nearest color is used.
+
+                    If the background or foreground color of an element is not
+                    specified, it defaults to -1, representing the default
+                    terminal foreground or background color.
+
+                    Note: On some terminals a bright version of the color
+                    implies bold.
+    - bold          Use bold text
+    - underline     Use underline text
+    - standout      Standout text attribute (reverse color)
+
+More often than not, some UI elements share the same color definition. In such
+cases the right value may specify an UI element from which the color definition
+will be copied. For example, "separator=help" will apply the current color
+definition for "help" to "separator".
+
+A keyword without the '=' is assumed to be a style template. The template name
+is looked up in the built-in styles list and the style definition is expanded
+in-place. With this, built-in styles can be used as basis for new styles.
+
+For example, take the aquatic theme and give it a red selection bar:
+
+MENUCONFIG_STYLE="aquatic selection=fg:white,bg:red"
+
+If there's an error in the style definition or if a missing style is assigned
+to, the assignment will be ignored, along with a warning being printed on
+stderr.
+
+The 'default' theme is always implicitly parsed first, so the following two
+settings have the same effect:
+
+    MENUCONFIG_STYLE="selection=fg:white,bg:red"
+    MENUCONFIG_STYLE="default selection=fg:white,bg:red"
+
+If the terminal doesn't support colors, the 'monochrome' theme is used, and
+MENUCONFIG_STYLE is ignored. The assumption is that the environment is broken
+somehow, and that the important thing is to get something usable.
+
+
+Other features
+==============
+
+  - Seamless terminal resizing
+
+  - No dependencies on *nix, as the 'curses' module is in the Python standard
+    library
+
+  - Unicode text entry
+
+  - Improved information screen compared to mconf:
+
+      * Expressions are split up by their top-level &&/|| operands to improve
+        readability
+
+      * Undefined symbols in expressions are pointed out
+
+      * Menus and comments have information displays
+
+      * Kconfig definitions are printed
+
+      * The include path is shown, listing the locations of the 'source'
+        statements that included the Kconfig file of the symbol (or other
+        item)
+
+
+Limitations
+===========
+
+Doesn't work out of the box on Windows, but can be made to work with
+
+    pip install windows-curses
+
+See the https://github.com/zephyrproject-rtos/windows-curses repository.
+"""
+from __future__ import print_function
+
+import os
+import sys
+
+_IS_WINDOWS = os.name == "nt"  # Are we running on Windows?
+
+try:
+    import curses
+except ImportError as e:
+    if not _IS_WINDOWS:
+        raise
+    sys.exit("""\
+menuconfig failed to import the standard Python 'curses' library. Try
+installing a package like windows-curses
+(https://github.com/zephyrproject-rtos/windows-curses) by running this command
+in cmd.exe:
+
+    pip install windows-curses
+
+Starting with Kconfiglib 13.0.0, windows-curses is no longer automatically
+installed when installing Kconfiglib via pip on Windows (because it breaks
+installation on MSYS2).
+
+Exception:
+{}: {}""".format(type(e).__name__, e))
+
+import errno
+import locale
+import re
+import textwrap
+
+from kconfiglib import Symbol, Choice, MENU, COMMENT, MenuNode, \
+                       BOOL, TRISTATE, STRING, INT, HEX, \
+                       AND, OR, \
+                       expr_str, expr_value, split_expr, \
+                       standard_sc_expr_str, \
+                       TRI_TO_STR, TYPE_TO_STR, \
+                       standard_kconfig, standard_config_filename
+
+
+#
+# Configuration variables
+#
+
+# If True, try to change LC_CTYPE to a UTF-8 locale if it is set to the C
+# locale (which implies ASCII). This fixes curses Unicode I/O issues on systems
+# with bad defaults. ncurses configures itself from the locale settings.
+#
+# Related PEP: https://www.python.org/dev/peps/pep-0538/
+_CHANGE_C_LC_CTYPE_TO_UTF8 = True
+
+# How many steps an implicit submenu will be indented. Implicit submenus are
+# created when an item depends on the symbol before it. Note that symbols
+# defined with 'menuconfig' create a separate menu instead of indenting.
+_SUBMENU_INDENT = 4
+
+# Number of steps for Page Up/Down to jump
+_PG_JUMP = 6
+
+# Height of the help window in show-help mode
+_SHOW_HELP_HEIGHT = 8
+
+# How far the cursor needs to be from the edge of the window before it starts
+# to scroll. Used for the main menu display, the information display, the
+# search display, and for text boxes.
+_SCROLL_OFFSET = 5
+
+# Minimum width of dialogs that ask for text input
+_INPUT_DIALOG_MIN_WIDTH = 30
+
+# Number of arrows pointing up/down to draw when a window is scrolled
+_N_SCROLL_ARROWS = 14
+
+# Lines of help text shown at the bottom of the "main" display
+_MAIN_HELP_LINES = """
+[Space/Enter] Toggle/enter  [ESC] Leave menu           [S] Save
+[O] Load                    [?] Symbol info            [/] Jump to symbol
+[F] Toggle show-help mode   [C] Toggle show-name mode  [A] Toggle show-all mode
+[Q] Quit (prompts for save) [D] Save minimal config (advanced)
+"""[1:-1].split("\n")
+
+# Lines of help text shown at the bottom of the information dialog
+_INFO_HELP_LINES = """
+[ESC/q] Return to menu      [/] Jump to symbol
+"""[1:-1].split("\n")
+
+# Lines of help text shown at the bottom of the search dialog
+_JUMP_TO_HELP_LINES = """
+Type text to narrow the search. Regexes are supported (via Python's 're'
+module). The up/down cursor keys step in the list. [Enter] jumps to the
+selected symbol. [ESC] aborts the search. Type multiple space-separated
+strings/regexes to find entries that match all of them. Type Ctrl-F to
+view the help of the selected item without leaving the dialog.
+"""[1:-1].split("\n")
+
+#
+# Styling
+#
+
+_STYLES = {
+    "default": """
+    path=fg:black,bg:white,bold
+    separator=fg:black,bg:yellow,bold
+    list=fg:black,bg:white
+    selection=fg:white,bg:blue,bold
+    inv-list=fg:red,bg:white
+    inv-selection=fg:red,bg:blue
+    help=path
+    show-help=list
+    frame=fg:black,bg:yellow,bold
+    body=fg:white,bg:black
+    edit=fg:white,bg:blue
+    jump-edit=edit
+    text=list
+    """,
+
+    # This style is forced on terminals that do no support colors
+    "monochrome": """
+    path=bold
+    separator=bold,standout
+    list=
+    selection=bold,standout
+    inv-list=bold
+    inv-selection=bold,standout
+    help=bold
+    show-help=
+    frame=bold,standout
+    body=
+    edit=standout
+    jump-edit=
+    text=
+    """,
+
+    # Blue-tinted style loosely resembling lxdialog
+    "aquatic": """
+    path=fg:white,bg:blue
+    separator=fg:white,bg:cyan
+    help=path
+    frame=fg:white,bg:cyan
+    body=fg:white,bg:blue
+    edit=fg:black,bg:white
+    """
+}
+
+_NAMED_COLORS = {
+    # Basic colors
+    "black":         curses.COLOR_BLACK,
+    "red":           curses.COLOR_RED,
+    "green":         curses.COLOR_GREEN,
+    "yellow":        curses.COLOR_YELLOW,
+    "blue":          curses.COLOR_BLUE,
+    "magenta":       curses.COLOR_MAGENTA,
+    "cyan":          curses.COLOR_CYAN,
+    "white":         curses.COLOR_WHITE,
+
+    # Bright versions
+    "brightblack":   curses.COLOR_BLACK + 8,
+    "brightred":     curses.COLOR_RED + 8,
+    "brightgreen":   curses.COLOR_GREEN + 8,
+    "brightyellow":  curses.COLOR_YELLOW + 8,
+    "brightblue":    curses.COLOR_BLUE + 8,
+    "brightmagenta": curses.COLOR_MAGENTA + 8,
+    "brightcyan":    curses.COLOR_CYAN + 8,
+    "brightwhite":   curses.COLOR_WHITE + 8,
+
+    # Aliases
+    "purple":        curses.COLOR_MAGENTA,
+    "brightpurple":  curses.COLOR_MAGENTA + 8,
+}
+
+
+def _rgb_to_6cube(rgb):
+    # Converts an 888 RGB color to a 3-tuple (nice in that it's hashable)
+    # representing the closest xterm 256-color 6x6x6 color cube color.
+    #
+    # The xterm 256-color extension uses a RGB color palette with components in
+    # the range 0-5 (a 6x6x6 cube). The catch is that the mapping is nonlinear.
+    # Index 0 in the 6x6x6 cube is mapped to 0, index 1 to 95, then 135, 175,
+    # etc., in increments of 40. See the links below:
+    #
+    #   https://commons.wikimedia.org/wiki/File:Xterm_256color_chart.svg
+    #   https://github.com/tmux/tmux/blob/master/colour.c
+
+    # 48 is the middle ground between 0 and 95.
+    return tuple(0 if x < 48 else int(round(max(1, (x - 55)/40))) for x in rgb)
+
+
+def _6cube_to_rgb(r6g6b6):
+    # Returns the 888 RGB color for a 666 xterm color cube index
+
+    return tuple(0 if x == 0 else 40*x + 55 for x in r6g6b6)
+
+
+def _rgb_to_gray(rgb):
+    # Converts an 888 RGB color to the index of an xterm 256-color grayscale
+    # color with approx. the same perceived brightness
+
+    # Calculate the luminance (gray intensity) of the color. See
+    #   https://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color
+    # and
+    #   https://www.w3.org/TR/AERT/#color-contrast
+    luma = 0.299*rgb[0] + 0.587*rgb[1] + 0.114*rgb[2]
+
+    # Closest index in the grayscale palette, which starts at RGB 0x080808,
+    # with stepping 0x0A0A0A
+    index = int(round((luma - 8)/10))
+
+    # Clamp the index to 0-23, corresponding to 232-255
+    return max(0, min(index, 23))
+
+
+def _gray_to_rgb(index):
+    # Convert a grayscale index to its closet single RGB component
+
+    return 3*(10*index + 8,)  # Returns a 3-tuple
+
+
+# Obscure Python: We never pass a value for rgb2index, and it keeps pointing to
+# the same dict. This avoids a global.
+def _alloc_rgb(rgb, rgb2index={}):
+    # Initialize a new entry in the xterm palette to the given RGB color,
+    # returning its index. If the color has already been initialized, the index
+    # of the existing entry is returned.
+    #
+    # ncurses is palette-based, so we need to overwrite palette entries to make
+    # new colors.
+    #
+    # The colors from 0 to 15 are user-defined, and there's no way to query
+    # their RGB values, so we better leave them untouched. Also leave any
+    # hypothetical colors above 255 untouched (though we're unlikely to
+    # allocate that many colors anyway).
+
+    if rgb in rgb2index:
+        return rgb2index[rgb]
+
+    # Many terminals allow the user to customize the first 16 colors. Avoid
+    # changing their values.
+    color_index = 16 + len(rgb2index)
+    if color_index >= 256:
+        _warn("Unable to allocate new RGB color ", rgb, ". Too many colors "
+              "allocated.")
+        return 0
+
+    # Map each RGB component from the range 0-255 to the range 0-1000, which is
+    # what curses uses
+    curses.init_color(color_index, *(int(round(1000*x/255)) for x in rgb))
+    rgb2index[rgb] = color_index
+
+    return color_index
+
+
+def _color_from_num(num):
+    # Returns the index of a color that looks like color 'num' in the xterm
+    # 256-color palette (but that might not be 'num', if we're redefining
+    # colors)
+
+    # - _alloc_rgb() won't touch the first 16 colors or any (hypothetical)
+    #   colors above 255, so we can always return them as-is
+    #
+    # - If the terminal doesn't support changing color definitions, or if
+    #   curses.COLORS < 256, _alloc_rgb() won't touch any color, and all colors
+    #   can be returned as-is
+    if num < 16 or num > 255 or not curses.can_change_color() or \
+       curses.COLORS < 256:
+        return num
+
+    # _alloc_rgb() might redefine colors, so emulate the xterm 256-color
+    # palette by allocating new colors instead of returning color numbers
+    # directly
+
+    if num < 232:
+        num -= 16
+        return _alloc_rgb(_6cube_to_rgb(((num//36)%6, (num//6)%6, num%6)))
+
+    return _alloc_rgb(_gray_to_rgb(num - 232))
+
+
+def _color_from_rgb(rgb):
+    # Returns the index of a color matching the 888 RGB color 'rgb'. The
+    # returned color might be an ~exact match or an approximation, depending on
+    # terminal capabilities.
+
+    # Calculates the Euclidean distance between two RGB colors
+    def dist(r1, r2): return sum((x - y)**2 for x, y in zip(r1, r2))
+
+    if curses.COLORS >= 256:
+        # Assume we're dealing with xterm's 256-color extension
+
+        if curses.can_change_color():
+            # Best case -- the terminal supports changing palette entries via
+            # curses.init_color(). Initialize an unused palette entry and
+            # return it.
+            return _alloc_rgb(rgb)
+
+        # Second best case -- pick between the xterm 256-color extension colors
+
+        # Closest 6-cube "color" color
+        c6 = _rgb_to_6cube(rgb)
+        # Closest gray color
+        gray = _rgb_to_gray(rgb)
+
+        if dist(rgb, _6cube_to_rgb(c6)) < dist(rgb, _gray_to_rgb(gray)):
+            # Use the "color" color from the 6x6x6 color palette. Calculate the
+            # color number from the 6-cube index triplet.
+            return 16 + 36*c6[0] + 6*c6[1] + c6[2]
+
+        # Use the color from the gray palette
+        return 232 + gray
+
+    # Terminal not in xterm 256-color mode. This is probably the best we can
+    # do, or is it? Submit patches. :)
+    min_dist = float('inf')
+    best = -1
+    for color in range(curses.COLORS):
+        # ncurses uses the range 0..1000. Scale that down to 0..255.
+        d = dist(rgb, tuple(int(round(255*c/1000))
+                            for c in curses.color_content(color)))
+        if d < min_dist:
+            min_dist = d
+            best = color
+
+    return best
+
+
+def _parse_style(style_str, parsing_default):
+    # Parses a string with '<element>=<style>' assignments. Anything not
+    # containing '=' is assumed to be a reference to a built-in style, which is
+    # treated as if all the assignments from the style were inserted at that
+    # point in the string.
+    #
+    # The parsing_default flag is set to True when we're implicitly parsing the
+    # 'default'/'monochrome' style, to prevent warnings.
+
+    for sline in style_str.split():
+        # Words without a "=" character represents a style template
+        if "=" in sline:
+            key, data = sline.split("=", 1)
+
+            # The 'default' style template is assumed to define all keys. We
+            # run _style_to_curses() for non-existing keys as well, so that we
+            # print warnings for errors to the right of '=' for those too.
+            if key not in _style and not parsing_default:
+                _warn("Ignoring non-existent style", key)
+
+            # If data is a reference to another key, copy its style
+            if data in _style:
+                _style[key] = _style[data]
+            else:
+                _style[key] = _style_to_curses(data)
+
+        elif sline in _STYLES:
+            # Recursively parse style template. Ignore styles that don't exist,
+            # for backwards/forwards compatibility.
+            _parse_style(_STYLES[sline], parsing_default)
+
+        else:
+            _warn("Ignoring non-existent style template", sline)
+
+# Dictionary mapping element types to the curses attributes used to display
+# them
+_style = {}
+
+
+def _style_to_curses(style_def):
+    # Parses a style definition string (<element>=<style>), returning
+    # a (fg_color, bg_color, attributes) tuple.
+
+    def parse_color(color_def):
+        color_def = color_def.split(":", 1)[1]
+
+        # HTML format, #RRGGBB
+        if re.match("#[A-Fa-f0-9]{6}", color_def):
+            return _color_from_rgb((
+                int(color_def[1:3], 16),
+                int(color_def[3:5], 16),
+                int(color_def[5:7], 16)))
+
+        if color_def in _NAMED_COLORS:
+            color_num = _color_from_num(_NAMED_COLORS[color_def])
+        else:
+            try:
+                color_num = _color_from_num(int(color_def, 0))
+            except ValueError:
+                _warn("Ignoring color", color_def, "that's neither "
+                      "predefined nor a number")
+                return -1
+
+        if not -1 <= color_num < curses.COLORS:
+            _warn("Ignoring color {}, which is outside the range "
+                  "-1..curses.COLORS-1 (-1..{})"
+                  .format(color_def, curses.COLORS - 1))
+            return -1
+
+        return color_num
+
+    fg_color = -1
+    bg_color = -1
+    attrs = 0
+
+    if style_def:
+        for field in style_def.split(","):
+            if field.startswith("fg:"):
+                fg_color = parse_color(field)
+            elif field.startswith("bg:"):
+                bg_color = parse_color(field)
+            elif field == "bold":
+                # A_BOLD tends to produce faint and hard-to-read text on the
+                # Windows console, especially with the old color scheme, before
+                # the introduction of
+                # https://blogs.msdn.microsoft.com/commandline/2017/08/02/updating-the-windows-console-colors/
+                attrs |= curses.A_NORMAL if _IS_WINDOWS else curses.A_BOLD
+            elif field == "standout":
+                attrs |= curses.A_STANDOUT
+            elif field == "underline":
+                attrs |= curses.A_UNDERLINE
+            else:
+                _warn("Ignoring unknown style attribute", field)
+
+    return _style_attr(fg_color, bg_color, attrs)
+
+
+def _init_styles():
+    if curses.has_colors():
+        try:
+            curses.use_default_colors()
+        except curses.error:
+            # Ignore errors on funky terminals that support colors but not
+            # using default colors. Worst it can do is break transparency and
+            # the like. Ran across this with the MSYS2/winpty setup in
+            # https://github.com/msys2/MINGW-packages/issues/5823, though there
+            # seems to be a lot of general brokenness there.
+            pass
+
+        # Use the 'default' theme as the base, and add any user-defined style
+        # settings from the environment
+        _parse_style("default", True)
+        if "MENUCONFIG_STYLE" in os.environ:
+            _parse_style(os.environ["MENUCONFIG_STYLE"], False)
+    else:
+        # Force the 'monochrome' theme if the terminal doesn't support colors.
+        # MENUCONFIG_STYLE is likely to mess things up here (though any colors
+        # would be ignored), so ignore it.
+        _parse_style("monochrome", True)
+
+
+# color_attribs holds the color pairs we've already created, indexed by a
+# (<foreground color>, <background color>) tuple.
+#
+# Obscure Python: We never pass a value for color_attribs, and it keeps
+# pointing to the same dict. This avoids a global.
+def _style_attr(fg_color, bg_color, attribs, color_attribs={}):
+    # Returns an attribute with the specified foreground and background color
+    # and the attributes in 'attribs'. Reuses color pairs already created if
+    # possible, and creates a new color pair otherwise.
+    #
+    # Returns 'attribs' if colors aren't supported.
+
+    if not curses.has_colors():
+        return attribs
+
+    if (fg_color, bg_color) not in color_attribs:
+        # Create new color pair. Color pair number 0 is hardcoded and cannot be
+        # changed, hence the +1s.
+        curses.init_pair(len(color_attribs) + 1, fg_color, bg_color)
+        color_attribs[(fg_color, bg_color)] = \
+            curses.color_pair(len(color_attribs) + 1)
+
+    return color_attribs[(fg_color, bg_color)] | attribs
+
+
+#
+# Main application
+#
+
+
+def _main():
+    menuconfig(standard_kconfig(__doc__))
+
+
+def menuconfig(kconf):
+    """
+    Launches the configuration interface, returning after the user exits.
+
+    kconf:
+      Kconfig instance to be configured
+    """
+    global _kconf
+    global _conf_filename
+    global _conf_changed
+    global _minconf_filename
+    global _show_all
+
+    _kconf = kconf
+
+    # Filename to save configuration to
+    _conf_filename = standard_config_filename()
+
+    # Load existing configuration and set _conf_changed True if it is outdated
+    _conf_changed = _load_config()
+
+    # Filename to save minimal configuration to
+    _minconf_filename = "defconfig"
+
+    # Any visible items in the top menu?
+    _show_all = False
+    if not _shown_nodes(kconf.top_node):
+        # Nothing visible. Start in show-all mode and try again.
+        _show_all = True
+        if not _shown_nodes(kconf.top_node):
+            # Give up. The implementation relies on always having a selected
+            # node.
+            print("Empty configuration -- nothing to configure.\n"
+                  "Check that environment variables are set properly.")
+            return
+
+    # Disable warnings. They get mangled in curses mode, and we deal with
+    # errors ourselves.
+    kconf.warn = False
+
+    # Make curses use the locale settings specified in the environment
+    locale.setlocale(locale.LC_ALL, "")
+
+    # Try to fix Unicode issues on systems with bad defaults
+    if _CHANGE_C_LC_CTYPE_TO_UTF8:
+        _change_c_lc_ctype_to_utf8()
+
+    # Get rid of the delay between pressing ESC and jumping to the parent menu,
+    # unless the user has set ESCDELAY (see ncurses(3)). This makes the UI much
+    # smoother to work with.
+    #
+    # Note: This is strictly pretty iffy, since escape codes for e.g. cursor
+    # keys start with ESC, but I've never seen it cause problems in practice
+    # (probably because it's unlikely that the escape code for a key would get
+    # split up across read()s, at least with a terminal emulator). Please
+    # report if you run into issues. Some suitable small default value could be
+    # used here instead in that case. Maybe it's silly to not put in the
+    # smallest imperceptible delay here already, though I don't like guessing.
+    #
+    # (From a quick glance at the ncurses source code, ESCDELAY might only be
+    # relevant for mouse events there, so maybe escapes are assumed to arrive
+    # in one piece already...)
+    os.environ.setdefault("ESCDELAY", "0")
+
+    # Enter curses mode. _menuconfig() returns a string to print on exit, after
+    # curses has been de-initialized.
+    print(curses.wrapper(_menuconfig))
+
+
+def _load_config():
+    # Loads any existing .config file. See the Kconfig.load_config() docstring.
+    #
+    # Returns True if .config is missing or outdated. We always prompt for
+    # saving the configuration in that case.
+
+    print(_kconf.load_config())
+    if not os.path.exists(_conf_filename):
+        # No .config
+        return True
+
+    return _needs_save()
+
+
+def _needs_save():
+    # Returns True if a just-loaded .config file is outdated (would get
+    # modified when saving)
+
+    if _kconf.missing_syms:
+        # Assignments to undefined symbols in the .config
+        return True
+
+    for sym in _kconf.unique_defined_syms:
+        if sym.user_value is None:
+            if sym.config_string:
+                # Unwritten symbol
+                return True
+        elif sym.orig_type in (BOOL, TRISTATE):
+            if sym.tri_value != sym.user_value:
+                # Written bool/tristate symbol, new value
+                return True
+        elif sym.str_value != sym.user_value:
+            # Written string/int/hex symbol, new value
+            return True
+
+    # No need to prompt for save
+    return False
+
+
+# Global variables used below:
+#
+#   _stdscr:
+#     stdscr from curses
+#
+#   _cur_menu:
+#     Menu node of the menu (or menuconfig symbol, or choice) currently being
+#     shown
+#
+#   _shown:
+#     List of items in _cur_menu that are shown (ignoring scrolling). In
+#     show-all mode, this list contains all items in _cur_menu. Otherwise, it
+#     contains just the visible items.
+#
+#   _sel_node_i:
+#     Index in _shown of the currently selected node
+#
+#   _menu_scroll:
+#     Index in _shown of the top row of the main display
+#
+#   _parent_screen_rows:
+#     List/stack of the row numbers that the selections in the parent menus
+#     appeared on. This is used to prevent the scrolling from jumping around
+#     when going in and out of menus.
+#
+#   _show_help/_show_name/_show_all:
+#     If True, the corresponding mode is on. See the module docstring.
+#
+#   _conf_filename:
+#     File to save the configuration to
+#
+#   _minconf_filename:
+#     File to save minimal configurations to
+#
+#   _conf_changed:
+#     True if the configuration has been changed. If False, we don't bother
+#     showing the save-and-quit dialog.
+#
+#     We reset this to False whenever the configuration is saved explicitly
+#     from the save dialog.
+
+
+def _menuconfig(stdscr):
+    # Logic for the main display, with the list of symbols, etc.
+
+    global _stdscr
+    global _conf_filename
+    global _conf_changed
+    global _minconf_filename
+    global _show_help
+    global _show_name
+
+    _stdscr = stdscr
+
+    _init()
+
+    while True:
+        _draw_main()
+        curses.doupdate()
+
+
+        c = _getch_compat(_menu_win)
+
+        if c == curses.KEY_RESIZE:
+            _resize_main()
+
+        elif c in (curses.KEY_DOWN, "j", "J"):
+            _select_next_menu_entry()
+
+        elif c in (curses.KEY_UP, "k", "K"):
+            _select_prev_menu_entry()
+
+        elif c in (curses.KEY_NPAGE, "\x04"):  # Page Down/Ctrl-D
+            # Keep it simple. This way we get sane behavior for small windows,
+            # etc., for free.
+            for _ in range(_PG_JUMP):
+                _select_next_menu_entry()
+
+        elif c in (curses.KEY_PPAGE, "\x15"):  # Page Up/Ctrl-U
+            for _ in range(_PG_JUMP):
+                _select_prev_menu_entry()
+
+        elif c in (curses.KEY_END, "G"):
+            _select_last_menu_entry()
+
+        elif c in (curses.KEY_HOME, "g"):
+            _select_first_menu_entry()
+
+        elif c == " ":
+            # Toggle the node if possible
+            sel_node = _shown[_sel_node_i]
+            if not _change_node(sel_node):
+                _enter_menu(sel_node)
+
+        elif c in (curses.KEY_RIGHT, "\n", "l", "L"):
+            # Enter the node if possible
+            sel_node = _shown[_sel_node_i]
+            if not _enter_menu(sel_node):
+                _change_node(sel_node)
+
+        elif c in ("n", "N"):
+            _set_sel_node_tri_val(0)
+
+        elif c in ("m", "M"):
+            _set_sel_node_tri_val(1)
+
+        elif c in ("y", "Y"):
+            _set_sel_node_tri_val(2)
+
+        elif c in (curses.KEY_LEFT, curses.KEY_BACKSPACE, _ERASE_CHAR,
+                   "\x1B", "h", "H"):  # \x1B = ESC
+
+            if c == "\x1B" and _cur_menu is _kconf.top_node:
+                res = _quit_dialog()
+                if res:
+                    return res
+            else:
+                _leave_menu()
+
+        elif c in ("o", "O"):
+            _load_dialog()
+
+        elif c in ("s", "S"):
+            filename = _save_dialog(_kconf.write_config, _conf_filename,
+                                    "configuration")
+            if filename:
+                _conf_filename = filename
+                _conf_changed = False
+
+        elif c in ("d", "D"):
+            filename = _save_dialog(_kconf.write_min_config, _minconf_filename,
+                                    "minimal configuration")
+            if filename:
+                _minconf_filename = filename
+
+        elif c == "/":
+            _jump_to_dialog()
+            # The terminal might have been resized while the fullscreen jump-to
+            # dialog was open
+            _resize_main()
+
+        elif c == "?":
+            _info_dialog(_shown[_sel_node_i], False)
+            # The terminal might have been resized while the fullscreen info
+            # dialog was open
+            _resize_main()
+
+        elif c in ("f", "F"):
+            _show_help = not _show_help
+            _set_style(_help_win, "show-help" if _show_help else "help")
+            _resize_main()
+
+        elif c in ("c", "C"):
+            _show_name = not _show_name
+
+        elif c in ("a", "A"):
+            _toggle_show_all()
+
+        elif c in ("q", "Q"):
+            res = _quit_dialog()
+            if res:
+                return res
+
+
+def _quit_dialog():
+    if not _conf_changed:
+        return "No changes to save (for '{}')".format(_conf_filename)
+
+    while True:
+        c = _key_dialog(
+            "Quit",
+            " Save configuration?\n"
+            "\n"
+            "(Y)es  (N)o  (C)ancel",
+            "ync")
+
+        if c is None or c == "c":
+            return None
+
+        if c == "y":
+            # Returns a message to print
+            msg = _try_save(_kconf.write_config, _conf_filename, "configuration")
+            if msg:
+                return msg
+
+        elif c == "n":
+            return "Configuration ({}) was not saved".format(_conf_filename)
+
+
+def _init():
+    # Initializes the main display with the list of symbols, etc. Also does
+    # misc. global initialization that needs to happen after initializing
+    # curses.
+
+    global _ERASE_CHAR
+
+    global _path_win
+    global _top_sep_win
+    global _menu_win
+    global _bot_sep_win
+    global _help_win
+
+    global _parent_screen_rows
+    global _cur_menu
+    global _shown
+    global _sel_node_i
+    global _menu_scroll
+
+    global _show_help
+    global _show_name
+
+    # Looking for this in addition to KEY_BACKSPACE (which is unreliable) makes
+    # backspace work with TERM=vt100. That makes it likely to work in sane
+    # environments.
+    _ERASE_CHAR = curses.erasechar()
+    if sys.version_info[0] >= 3:
+        # erasechar() returns a one-byte bytes object on Python 3. This sets
+        # _ERASE_CHAR to a blank string if it can't be decoded, which should be
+        # harmless.
+        _ERASE_CHAR = _ERASE_CHAR.decode("utf-8", "ignore")
+
+    _init_styles()
+
+    # Hide the cursor
+    _safe_curs_set(0)
+
+    # Initialize windows
+
+    # Top row, with menu path
+    _path_win = _styled_win("path")
+
+    # Separator below menu path, with title and arrows pointing up
+    _top_sep_win = _styled_win("separator")
+
+    # List of menu entries with symbols, etc.
+    _menu_win = _styled_win("list")
+    _menu_win.keypad(True)
+
+    # Row below menu list, with arrows pointing down
+    _bot_sep_win = _styled_win("separator")
+
+    # Help window with keys at the bottom. Shows help texts in show-help mode.
+    _help_win = _styled_win("help")
+
+    # The rows we'd like the nodes in the parent menus to appear on. This
+    # prevents the scroll from jumping around when going in and out of menus.
+    _parent_screen_rows = []
+
+    # Initial state
+
+    _cur_menu = _kconf.top_node
+    _shown = _shown_nodes(_cur_menu)
+    _sel_node_i = _menu_scroll = 0
+
+    _show_help = _show_name = False
+
+    # Give windows their initial size
+    _resize_main()
+
+
+def _resize_main():
+    # Resizes the main display, with the list of symbols, etc., to fill the
+    # terminal
+
+    global _menu_scroll
+
+    screen_height, screen_width = _stdscr.getmaxyx()
+
+    _path_win.resize(1, screen_width)
+    _top_sep_win.resize(1, screen_width)
+    _bot_sep_win.resize(1, screen_width)
+
+    help_win_height = _SHOW_HELP_HEIGHT if _show_help else \
+        len(_MAIN_HELP_LINES)
+
+    menu_win_height = screen_height - help_win_height - 3
+
+    if menu_win_height >= 1:
+        _menu_win.resize(menu_win_height, screen_width)
+        _help_win.resize(help_win_height, screen_width)
+
+        _top_sep_win.mvwin(1, 0)
+        _menu_win.mvwin(2, 0)
+        _bot_sep_win.mvwin(2 + menu_win_height, 0)
+        _help_win.mvwin(2 + menu_win_height + 1, 0)
+    else:
+        # Degenerate case. Give up on nice rendering and just prevent errors.
+
+        menu_win_height = 1
+
+        _menu_win.resize(1, screen_width)
+        _help_win.resize(1, screen_width)
+
+        for win in _top_sep_win, _menu_win, _bot_sep_win, _help_win:
+            win.mvwin(0, 0)
+
+    # Adjust the scroll so that the selected node is still within the window,
+    # if needed
+    if _sel_node_i - _menu_scroll >= menu_win_height:
+        _menu_scroll = _sel_node_i - menu_win_height + 1
+
+
+def _height(win):
+    # Returns the height of 'win'
+
+    return win.getmaxyx()[0]
+
+
+def _width(win):
+    # Returns the width of 'win'
+
+    return win.getmaxyx()[1]
+
+
+def _enter_menu(menu):
+    # Makes 'menu' the currently displayed menu. In addition to actual 'menu's,
+    # "menu" here includes choices and symbols defined with the 'menuconfig'
+    # keyword.
+    #
+    # Returns False if 'menu' can't be entered.
+
+    global _cur_menu
+    global _shown
+    global _sel_node_i
+    global _menu_scroll
+
+    if not menu.is_menuconfig:
+        return False  # Not a menu
+
+    shown_sub = _shown_nodes(menu)
+    # Never enter empty menus. We depend on having a current node.
+    if not shown_sub:
+        return False
+
+    # Remember where the current node appears on the screen, so we can try
+    # to get it to appear in the same place when we leave the menu
+    _parent_screen_rows.append(_sel_node_i - _menu_scroll)
+
+    # Jump into menu
+    _cur_menu = menu
+    _shown = shown_sub
+    _sel_node_i = _menu_scroll = 0
+
+    if isinstance(menu.item, Choice):
+        _select_selected_choice_sym()
+
+    return True
+
+
+def _select_selected_choice_sym():
+    # Puts the cursor on the currently selected (y-valued) choice symbol, if
+    # any. Does nothing if if the choice has no selection (is not visible/in y
+    # mode).
+
+    global _sel_node_i
+
+    choice = _cur_menu.item
+    if choice.selection:
+        # Search through all menu nodes to handle choice symbols being defined
+        # in multiple locations
+        for node in choice.selection.nodes:
+            if node in _shown:
+                _sel_node_i = _shown.index(node)
+                _center_vertically()
+                return
+
+
+def _jump_to(node):
+    # Jumps directly to the menu node 'node'
+
+    global _cur_menu
+    global _shown
+    global _sel_node_i
+    global _menu_scroll
+    global _show_all
+    global _parent_screen_rows
+
+    # Clear remembered menu locations. We might not even have been in the
+    # parent menus before.
+    _parent_screen_rows = []
+
+    old_show_all = _show_all
+    jump_into = (isinstance(node.item, Choice) or node.item == MENU) and \
+                node.list
+
+    # If we're jumping to a non-empty choice or menu, jump to the first entry
+    # in it instead of jumping to its menu node
+    if jump_into:
+        _cur_menu = node
+        node = node.list
+    else:
+        _cur_menu = _parent_menu(node)
+
+    _shown = _shown_nodes(_cur_menu)
+    if node not in _shown:
+        # The node wouldn't be shown. Turn on show-all to show it.
+        _show_all = True
+        _shown = _shown_nodes(_cur_menu)
+
+    _sel_node_i = _shown.index(node)
+
+    if jump_into and not old_show_all and _show_all:
+        # If we're jumping into a choice or menu and were forced to turn on
+        # show-all because the first entry wasn't visible, try turning it off.
+        # That will land us at the first visible node if there are visible
+        # nodes, and is a no-op otherwise.
+        _toggle_show_all()
+
+    _center_vertically()
+
+    # If we're jumping to a non-empty choice, jump to the selected symbol, if
+    # any
+    if jump_into and isinstance(_cur_menu.item, Choice):
+        _select_selected_choice_sym()
+
+
+def _leave_menu():
+    # Jumps to the parent menu of the current menu. Does nothing if we're in
+    # the top menu.
+
+    global _cur_menu
+    global _shown
+    global _sel_node_i
+    global _menu_scroll
+
+    if _cur_menu is _kconf.top_node:
+        return
+
+    # Jump to parent menu
+    parent = _parent_menu(_cur_menu)
+    _shown = _shown_nodes(parent)
+    _sel_node_i = _shown.index(_cur_menu)
+    _cur_menu = parent
+
+    # Try to make the menu entry appear on the same row on the screen as it did
+    # before we entered the menu.
+
+    if _parent_screen_rows:
+        # The terminal might have shrunk since we were last in the parent menu
+        screen_row = min(_parent_screen_rows.pop(), _height(_menu_win) - 1)
+        _menu_scroll = max(_sel_node_i - screen_row, 0)
+    else:
+        # No saved parent menu locations, meaning we jumped directly to some
+        # node earlier
+        _center_vertically()
+
+
+def _select_next_menu_entry():
+    # Selects the menu entry after the current one, adjusting the scroll if
+    # necessary. Does nothing if we're already at the last menu entry.
+
+    global _sel_node_i
+    global _menu_scroll
+
+    if _sel_node_i < len(_shown) - 1:
+        # Jump to the next node
+        _sel_node_i += 1
+
+        # If the new node is sufficiently close to the edge of the menu window
+        # (as determined by _SCROLL_OFFSET), increase the scroll by one. This
+        # gives nice and non-jumpy behavior even when
+        # _SCROLL_OFFSET >= _height(_menu_win).
+        if _sel_node_i >= _menu_scroll + _height(_menu_win) - _SCROLL_OFFSET \
+           and _menu_scroll < _max_scroll(_shown, _menu_win):
+
+            _menu_scroll += 1
+
+
+def _select_prev_menu_entry():
+    # Selects the menu entry before the current one, adjusting the scroll if
+    # necessary. Does nothing if we're already at the first menu entry.
+
+    global _sel_node_i
+    global _menu_scroll
+
+    if _sel_node_i > 0:
+        # Jump to the previous node
+        _sel_node_i -= 1
+
+        # See _select_next_menu_entry()
+        if _sel_node_i < _menu_scroll + _SCROLL_OFFSET:
+            _menu_scroll = max(_menu_scroll - 1, 0)
+
+
+def _select_last_menu_entry():
+    # Selects the last menu entry in the current menu
+
+    global _sel_node_i
+    global _menu_scroll
+
+    _sel_node_i = len(_shown) - 1
+    _menu_scroll = _max_scroll(_shown, _menu_win)
+
+
+def _select_first_menu_entry():
+    # Selects the first menu entry in the current menu
+
+    global _sel_node_i
+    global _menu_scroll
+
+    _sel_node_i = _menu_scroll = 0
+
+
+def _toggle_show_all():
+    # Toggles show-all mode on/off. If turning it off would give no visible
+    # items in the current menu, it is left on.
+
+    global _show_all
+    global _shown
+    global _sel_node_i
+    global _menu_scroll
+
+    # Row on the screen the cursor is on. Preferably we want the same row to
+    # stay highlighted.
+    old_row = _sel_node_i - _menu_scroll
+
+    _show_all = not _show_all
+    # List of new nodes to be shown after toggling _show_all
+    new_shown = _shown_nodes(_cur_menu)
+
+    # Find a good node to select. The selected node might disappear if show-all
+    # mode is turned off.
+
+    # Select the previously selected node itself if it is still visible. If
+    # there are visible nodes before it, select the closest one.
+    for node in _shown[_sel_node_i::-1]:
+        if node in new_shown:
+            _sel_node_i = new_shown.index(node)
+            break
+    else:
+        # No visible nodes before the previously selected node. Select the
+        # closest visible node after it instead.
+        for node in _shown[_sel_node_i + 1:]:
+            if node in new_shown:
+                _sel_node_i = new_shown.index(node)
+                break
+        else:
+            # No visible nodes at all, meaning show-all was turned off inside
+            # an invisible menu. Don't allow that, as the implementation relies
+            # on always having a selected node.
+            _show_all = True
+            return
+
+    _shown = new_shown
+
+    # Try to make the cursor stay on the same row in the menu window. This
+    # might be impossible if too many nodes have disappeared above the node.
+    _menu_scroll = max(_sel_node_i - old_row, 0)
+
+
+def _center_vertically():
+    # Centers the selected node vertically, if possible
+
+    global _menu_scroll
+
+    _menu_scroll = min(max(_sel_node_i - _height(_menu_win)//2, 0),
+                       _max_scroll(_shown, _menu_win))
+
+
+def _draw_main():
+    # Draws the "main" display, with the list of symbols, the header, and the
+    # footer.
+    #
+    # This could be optimized to only update the windows that have actually
+    # changed, but keep it simple for now and let curses sort it out.
+
+    term_width = _width(_stdscr)
+
+    #
+    # Update the separator row below the menu path
+    #
+
+    _top_sep_win.erase()
+
+    # Draw arrows pointing up if the symbol window is scrolled down. Draw them
+    # before drawing the title, so the title ends up on top for small windows.
+    if _menu_scroll > 0:
+        _safe_hline(_top_sep_win, 0, 4, curses.ACS_UARROW, _N_SCROLL_ARROWS)
+
+    # Add the 'mainmenu' text as the title, centered at the top
+    _safe_addstr(_top_sep_win,
+                 0, max((term_width - len(_kconf.mainmenu_text))//2, 0),
+                 _kconf.mainmenu_text)
+
+    _top_sep_win.noutrefresh()
+
+    # Note: The menu path at the top is deliberately updated last. See below.
+
+    #
+    # Update the symbol window
+    #
+
+    _menu_win.erase()
+
+    # Draw the _shown nodes starting from index _menu_scroll up to either as
+    # many as fit in the window, or to the end of _shown
+    for i in range(_menu_scroll,
+                   min(_menu_scroll + _height(_menu_win), len(_shown))):
+
+        node = _shown[i]
+
+        # The 'not _show_all' test avoids showing invisible items in red
+        # outside show-all mode, which could look confusing/broken. Invisible
+        # symbols show up outside show-all mode if an invisible symbol has
+        # visible children in an implicit (indented) menu.
+        if _visible(node) or not _show_all:
+            style = _style["selection" if i == _sel_node_i else "list"]
+        else:
+            style = _style["inv-selection" if i == _sel_node_i else "inv-list"]
+
+        _safe_addstr(_menu_win, i - _menu_scroll, 0, _node_str(node), style)
+
+    _menu_win.noutrefresh()
+
+    #
+    # Update the bottom separator window
+    #
+
+    _bot_sep_win.erase()
+
+    # Draw arrows pointing down if the symbol window is scrolled up
+    if _menu_scroll < _max_scroll(_shown, _menu_win):
+        _safe_hline(_bot_sep_win, 0, 4, curses.ACS_DARROW, _N_SCROLL_ARROWS)
+
+    # Indicate when show-name/show-help/show-all mode is enabled
+    enabled_modes = []
+    if _show_help:
+        enabled_modes.append("show-help (toggle with [F])")
+    if _show_name:
+        enabled_modes.append("show-name")
+    if _show_all:
+        enabled_modes.append("show-all")
+    if enabled_modes:
+        s = " and ".join(enabled_modes) + " mode enabled"
+        _safe_addstr(_bot_sep_win, 0, max(term_width - len(s) - 2, 0), s)
+
+    _bot_sep_win.noutrefresh()
+
+    #
+    # Update the help window, which shows either key bindings or help texts
+    #
+
+    _help_win.erase()
+
+    if _show_help:
+        node = _shown[_sel_node_i]
+        if isinstance(node.item, (Symbol, Choice)) and node.help:
+            help_lines = textwrap.wrap(node.help, _width(_help_win))
+            for i in range(min(_height(_help_win), len(help_lines))):
+                _safe_addstr(_help_win, i, 0, help_lines[i])
+        else:
+            _safe_addstr(_help_win, 0, 0, "(no help)")
+    else:
+        for i, line in enumerate(_MAIN_HELP_LINES):
+            _safe_addstr(_help_win, i, 0, line)
+
+    _help_win.noutrefresh()
+
+    #
+    # Update the top row with the menu path.
+    #
+    # Doing this last leaves the cursor on the top row, which avoids some minor
+    # annoying jumpiness in gnome-terminal when reducing the height of the
+    # terminal. It seems to happen whenever the row with the cursor on it
+    # disappears.
+    #
+
+    _path_win.erase()
+
+    # Draw the menu path ("(Top) -> Menu -> Submenu -> ...")
+
+    menu_prompts = []
+
+    menu = _cur_menu
+    while menu is not _kconf.top_node:
+        # Promptless choices can be entered in show-all mode. Use
+        # standard_sc_expr_str() for them, so they show up as
+        # '<choice (name if any)>'.
+        menu_prompts.append(menu.prompt[0] if menu.prompt else
+                            standard_sc_expr_str(menu.item))
+        menu = menu.parent
+    menu_prompts.append("(Top)")
+    menu_prompts.reverse()
+
+    # Hack: We can't put ACS_RARROW directly in the string. Temporarily
+    # represent it with NULL.
+    menu_path_str = " \0 ".join(menu_prompts)
+
+    # Scroll the menu path to the right if needed to make the current menu's
+    # title visible
+    if len(menu_path_str) > term_width:
+        menu_path_str = menu_path_str[len(menu_path_str) - term_width:]
+
+    # Print the path with the arrows reinserted
+    split_path = menu_path_str.split("\0")
+    _safe_addstr(_path_win, split_path[0])
+    for s in split_path[1:]:
+        _safe_addch(_path_win, curses.ACS_RARROW)
+        _safe_addstr(_path_win, s)
+
+    _path_win.noutrefresh()
+
+
+def _parent_menu(node):
+    # Returns the menu node of the menu that contains 'node'. In addition to
+    # proper 'menu's, this might also be a 'menuconfig' symbol or a 'choice'.
+    # "Menu" here means a menu in the interface.
+
+    menu = node.parent
+    while not menu.is_menuconfig:
+        menu = menu.parent
+    return menu
+
+
+def _shown_nodes(menu):
+    # Returns the list of menu nodes from 'menu' (see _parent_menu()) that
+    # would be shown when entering it
+
+    def rec(node):
+        res = []
+
+        while node:
+            if _visible(node) or _show_all:
+                res.append(node)
+                if node.list and not node.is_menuconfig:
+                    # Nodes from implicit menu created from dependencies. Will
+                    # be shown indented. Note that is_menuconfig is True for
+                    # menus and choices as well as 'menuconfig' symbols.
+                    res += rec(node.list)
+
+            elif node.list and isinstance(node.item, Symbol):
+                # Show invisible symbols if they have visible children. This
+                # can happen for an m/y-valued symbol with an optional prompt
+                # ('prompt "foo" is COND') that is currently disabled. Note
+                # that it applies to both 'config' and 'menuconfig' symbols.
+                shown_children = rec(node.list)
+                if shown_children:
+                    res.append(node)
+                    if not node.is_menuconfig:
+                        res += shown_children
+
+            node = node.next
+
+        return res
+
+    if isinstance(menu.item, Choice):
+        # For named choices defined in multiple locations, entering the choice
+        # at a particular menu node would normally only show the choice symbols
+        # defined there (because that's what the MenuNode tree looks like).
+        #
+        # That might look confusing, and makes extending choices by defining
+        # them in multiple locations less useful. Instead, gather all the child
+        # menu nodes for all the choices whenever a choice is entered. That
+        # makes all choice symbols visible at all locations.
+        #
+        # Choices can contain non-symbol items (people do all sorts of weird
+        # stuff with them), hence the generality here. We really need to
+        # preserve the menu tree at each choice location.
+        #
+        # Note: Named choices are pretty broken in the C tools, and this is
+        # super obscure, so you probably won't find much that relies on this.
+        # This whole 'if' could be deleted if you don't care about defining
+        # choices in multiple locations to add symbols (which will still work,
+        # just with things being displayed in a way that might be unexpected).
+
+        # Do some additional work to avoid listing choice symbols twice if all
+        # or part of the choice is copied in multiple locations (e.g. by
+        # including some Kconfig file multiple times). We give the prompts at
+        # the current location precedence.
+        seen_syms = {node.item for node in rec(menu.list)
+                     if isinstance(node.item, Symbol)}
+        res = []
+        for choice_node in menu.item.nodes:
+            for node in rec(choice_node.list):
+                # 'choice_node is menu' checks if we're dealing with the
+                # current location
+                if node.item not in seen_syms or choice_node is menu:
+                    res.append(node)
+                    if isinstance(node.item, Symbol):
+                        seen_syms.add(node.item)
+        return res
+
+    return rec(menu.list)
+
+
+def _visible(node):
+    # Returns True if the node should appear in the menu (outside show-all
+    # mode)
+
+    return node.prompt and expr_value(node.prompt[1]) and not \
+        (node.item == MENU and not expr_value(node.visibility))
+
+
+def _change_node(node):
+    # Changes the value of the menu node 'node' if it is a symbol. Bools and
+    # tristates are toggled, while other symbol types pop up a text entry
+    # dialog.
+    #
+    # Returns False if the value of 'node' can't be changed.
+
+    if not _changeable(node):
+        return False
+
+    # sc = symbol/choice
+    sc = node.item
+
+    if sc.orig_type in (INT, HEX, STRING):
+        s = sc.str_value
+
+        while True:
+            s = _input_dialog(
+                "{} ({})".format(node.prompt[0], TYPE_TO_STR[sc.orig_type]),
+                s, _range_info(sc))
+
+            if s is None:
+                break
+
+            if sc.orig_type in (INT, HEX):
+                s = s.strip()
+
+                # 'make menuconfig' does this too. Hex values not starting with
+                # '0x' are accepted when loading .config files though.
+                if sc.orig_type == HEX and not s.startswith(("0x", "0X")):
+                    s = "0x" + s
+
+            if _check_valid(sc, s):
+                _set_val(sc, s)
+                break
+
+    elif len(sc.assignable) == 1:
+        # Handles choice symbols for choices in y mode, which are a special
+        # case: .assignable can be (2,) while .tri_value is 0.
+        _set_val(sc, sc.assignable[0])
+
+    else:
+        # Set the symbol to the value after the current value in
+        # sc.assignable, with wrapping
+        val_index = sc.assignable.index(sc.tri_value)
+        _set_val(sc, sc.assignable[(val_index + 1) % len(sc.assignable)])
+
+
+    if _is_y_mode_choice_sym(sc) and not node.list:
+        # Immediately jump to the parent menu after making a choice selection,
+        # like 'make menuconfig' does, except if the menu node has children
+        # (which can happen if a symbol 'depends on' a choice symbol that
+        # immediately precedes it).
+        _leave_menu()
+
+
+    return True
+
+
+def _changeable(node):
+    # Returns True if the value if 'node' can be changed
+
+    sc = node.item
+
+    if not isinstance(sc, (Symbol, Choice)):
+        return False
+
+    # This will hit for invisible symbols, which appear in show-all mode and
+    # when an invisible symbol has visible children (which can happen e.g. for
+    # symbols with optional prompts)
+    if not (node.prompt and expr_value(node.prompt[1])):
+        return False
+
+    return sc.orig_type in (STRING, INT, HEX) or len(sc.assignable) > 1 \
+        or _is_y_mode_choice_sym(sc)
+
+
+def _set_sel_node_tri_val(tri_val):
+    # Sets the value of the currently selected menu entry to 'tri_val', if that
+    # value can be assigned
+
+    sc = _shown[_sel_node_i].item
+    if isinstance(sc, (Symbol, Choice)) and tri_val in sc.assignable:
+        _set_val(sc, tri_val)
+
+
+def _set_val(sc, val):
+    # Wrapper around Symbol/Choice.set_value() for updating the menu state and
+    # _conf_changed
+
+    global _conf_changed
+
+    # Use the string representation of tristate values. This makes the format
+    # consistent for all symbol types.
+    if val in TRI_TO_STR:
+        val = TRI_TO_STR[val]
+
+    if val != sc.str_value:
+        sc.set_value(val)
+        _conf_changed = True
+
+        # Changing the value of the symbol might have changed what items in the
+        # current menu are visible. Recalculate the state.
+        _update_menu()
+
+
+def _update_menu():
+    # Updates the current menu after the value of a symbol or choice has been
+    # changed. Changing a value might change which items in the menu are
+    # visible.
+    #
+    # If possible, preserves the location of the cursor on the screen when
+    # items are added/removed above the selected item.
+
+    global _shown
+    global _sel_node_i
+    global _menu_scroll
+
+    # Row on the screen the cursor was on
+    old_row = _sel_node_i - _menu_scroll
+
+    sel_node = _shown[_sel_node_i]
+
+    # New visible nodes
+    _shown = _shown_nodes(_cur_menu)
+
+    # New index of selected node
+    _sel_node_i = _shown.index(sel_node)
+
+    # Try to make the cursor stay on the same row in the menu window. This
+    # might be impossible if too many nodes have disappeared above the node.
+    _menu_scroll = max(_sel_node_i - old_row, 0)
+
+
+def _input_dialog(title, initial_text, info_text=None):
+    # Pops up a dialog that prompts the user for a string
+    #
+    # title:
+    #   Title to display at the top of the dialog window's border
+    #
+    # initial_text:
+    #   Initial text to prefill the input field with
+    #
+    # info_text:
+    #   String to show next to the input field. If None, just the input field
+    #   is shown.
+
+    win = _styled_win("body")
+    win.keypad(True)
+
+    info_lines = info_text.split("\n") if info_text else []
+
+    # Give the input dialog its initial size
+    _resize_input_dialog(win, title, info_lines)
+
+    _safe_curs_set(2)
+
+    # Input field text
+    s = initial_text
+
+    # Cursor position
+    i = len(initial_text)
+
+    def edit_width():
+        return _width(win) - 4
+
+    # Horizontal scroll offset
+    hscroll = max(i - edit_width() + 1, 0)
+
+    while True:
+        # Draw the "main" display with the menu, etc., so that resizing still
+        # works properly. This is like a stack of windows, only hardcoded for
+        # now.
+        _draw_main()
+        _draw_input_dialog(win, title, info_lines, s, i, hscroll)
+        curses.doupdate()
+
+
+        c = _getch_compat(win)
+
+        if c == curses.KEY_RESIZE:
+            # Resize the main display too. The dialog floats above it.
+            _resize_main()
+            _resize_input_dialog(win, title, info_lines)
+
+        elif c == "\n":
+            _safe_curs_set(0)
+            return s
+
+        elif c == "\x1B":  # \x1B = ESC
+            _safe_curs_set(0)
+            return None
+
+        else:
+            s, i, hscroll = _edit_text(c, s, i, hscroll, edit_width())
+
+
+def _resize_input_dialog(win, title, info_lines):
+    # Resizes the input dialog to a size appropriate for the terminal size
+
+    screen_height, screen_width = _stdscr.getmaxyx()
+
+    win_height = 5
+    if info_lines:
+        win_height += len(info_lines) + 1
+    win_height = min(win_height, screen_height)
+
+    win_width = max(_INPUT_DIALOG_MIN_WIDTH,
+                    len(title) + 4,
+                    *(len(line) + 4 for line in info_lines))
+    win_width = min(win_width, screen_width)
+
+    win.resize(win_height, win_width)
+    win.mvwin((screen_height - win_height)//2,
+              (screen_width - win_width)//2)
+
+
+def _draw_input_dialog(win, title, info_lines, s, i, hscroll):
+    edit_width = _width(win) - 4
+
+    win.erase()
+
+    # Note: Perhaps having a separate window for the input field would be nicer
+    visible_s = s[hscroll:hscroll + edit_width]
+    _safe_addstr(win, 2, 2, visible_s + " "*(edit_width - len(visible_s)),
+                 _style["edit"])
+
+    for linenr, line in enumerate(info_lines):
+        _safe_addstr(win, 4 + linenr, 2, line)
+
+    # Draw the frame last so that it overwrites the body text for small windows
+    _draw_frame(win, title)
+
+    _safe_move(win, 2, 2 + i - hscroll)
+
+    win.noutrefresh()
+
+
+def _load_dialog():
+    # Dialog for loading a new configuration
+
+    global _conf_changed
+    global _conf_filename
+    global _show_all
+
+    if _conf_changed:
+        c = _key_dialog(
+            "Load",
+            "You have unsaved changes. Load new\n"
+            "configuration anyway?\n"
+            "\n"
+            "         (O)K  (C)ancel",
+            "oc")
+
+        if c is None or c == "c":
+            return
+
+    filename = _conf_filename
+    while True:
+        filename = _input_dialog("File to load", filename, _load_save_info())
+        if filename is None:
+            return
+
+        filename = os.path.expanduser(filename)
+
+        if _try_load(filename):
+            _conf_filename = filename
+            _conf_changed = _needs_save()
+
+            # Turn on show-all mode if the selected node is not visible after
+            # loading the new configuration. _shown still holds the old state.
+            if _shown[_sel_node_i] not in _shown_nodes(_cur_menu):
+                _show_all = True
+
+            _update_menu()
+
+            # The message dialog indirectly updates the menu display, so _msg()
+            # must be called after the new state has been initialized
+            _msg("Success", "Loaded " + filename)
+            return
+
+
+def _try_load(filename):
+    # Tries to load a configuration file. Pops up an error and returns False on
+    # failure.
+    #
+    # filename:
+    #   Configuration file to load
+
+    try:
+        _kconf.load_config(filename)
+        return True
+    except EnvironmentError as e:
+        _error("Error loading '{}'\n\n{} (errno: {})"
+               .format(filename, e.strerror, errno.errorcode[e.errno]))
+        return False
+
+
+def _save_dialog(save_fn, default_filename, description):
+    # Dialog for saving the current configuration
+    #
+    # save_fn:
+    #   Function to call with 'filename' to save the file
+    #
+    # default_filename:
+    #   Prefilled filename in the input field
+    #
+    # description:
+    #   String describing the thing being saved
+    #
+    # Return value:
+    #   The path to the saved file, or None if no file was saved
+
+    filename = default_filename
+    while True:
+        filename = _input_dialog("Filename to save {} to".format(description),
+                                 filename, _load_save_info())
+        if filename is None:
+            return None
+
+        filename = os.path.expanduser(filename)
+
+        msg = _try_save(save_fn, filename, description)
+        if msg:
+            _msg("Success", msg)
+            return filename
+
+
+def _try_save(save_fn, filename, description):
+    # Tries to save a configuration file. Returns a message to print on
+    # success.
+    #
+    # save_fn:
+    #   Function to call with 'filename' to save the file
+    #
+    # description:
+    #   String describing the thing being saved
+    #
+    # Return value:
+    #   A message to print on success, and None on failure
+
+    try:
+        # save_fn() returns a message to print
+        return save_fn(filename)
+    except EnvironmentError as e:
+        _error("Error saving {} to '{}'\n\n{} (errno: {})"
+               .format(description, e.filename, e.strerror,
+                       errno.errorcode[e.errno]))
+        return None
+
+
+def _key_dialog(title, text, keys):
+    # Pops up a dialog that can be closed by pressing a key
+    #
+    # title:
+    #   Title to display at the top of the dialog window's border
+    #
+    # text:
+    #   Text to show in the dialog
+    #
+    # keys:
+    #   List of keys that will close the dialog. Other keys (besides ESC) are
+    #   ignored. The caller is responsible for providing a hint about which
+    #   keys can be pressed in 'text'.
+    #
+    # Return value:
+    #   The key that was pressed to close the dialog. Uppercase characters are
+    #   converted to lowercase. ESC will always close the dialog, and returns
+    #   None.
+
+    win = _styled_win("body")
+    win.keypad(True)
+
+    _resize_key_dialog(win, text)
+
+    while True:
+        # See _input_dialog()
+        _draw_main()
+        _draw_key_dialog(win, title, text)
+        curses.doupdate()
+
+
+        c = _getch_compat(win)
+
+        if c == curses.KEY_RESIZE:
+            # Resize the main display too. The dialog floats above it.
+            _resize_main()
+            _resize_key_dialog(win, text)
+
+        elif c == "\x1B":  # \x1B = ESC
+            return None
+
+        elif isinstance(c, str):
+            c = c.lower()
+            if c in keys:
+                return c
+
+
+def _resize_key_dialog(win, text):
+    # Resizes the key dialog to a size appropriate for the terminal size
+
+    screen_height, screen_width = _stdscr.getmaxyx()
+
+    lines = text.split("\n")
+
+    win_height = min(len(lines) + 4, screen_height)
+    win_width = min(max(len(line) for line in lines) + 4, screen_width)
+
+    win.resize(win_height, win_width)
+    win.mvwin((screen_height - win_height)//2,
+              (screen_width - win_width)//2)
+
+
+def _draw_key_dialog(win, title, text):
+    win.erase()
+
+    for i, line in enumerate(text.split("\n")):
+        _safe_addstr(win, 2 + i, 2, line)
+
+    # Draw the frame last so that it overwrites the body text for small windows
+    _draw_frame(win, title)
+
+    win.noutrefresh()
+
+
+def _draw_frame(win, title):
+    # Draw a frame around the inner edges of 'win', with 'title' at the top
+
+    win_height, win_width = win.getmaxyx()
+
+    win.attron(_style["frame"])
+
+    # Draw top/bottom edge
+    _safe_hline(win,              0, 0, " ", win_width)
+    _safe_hline(win, win_height - 1, 0, " ", win_width)
+
+    # Draw left/right edge
+    _safe_vline(win, 0,             0, " ", win_height)
+    _safe_vline(win, 0, win_width - 1, " ", win_height)
+
+    # Draw title
+    _safe_addstr(win, 0, max((win_width - len(title))//2, 0), title)
+
+    win.attroff(_style["frame"])
+
+
+def _jump_to_dialog():
+    # Implements the jump-to dialog, where symbols can be looked up via
+    # incremental search and jumped to.
+    #
+    # Returns True if the user jumped to a symbol, and False if the dialog was
+    # canceled.
+
+    s = ""  # Search text
+    prev_s = None  # Previous search text
+    s_i = 0  # Search text cursor position
+    hscroll = 0  # Horizontal scroll offset
+
+    sel_node_i = 0  # Index of selected row
+    scroll = 0  # Index in 'matches' of the top row of the list
+
+    # Edit box at the top
+    edit_box = _styled_win("jump-edit")
+    edit_box.keypad(True)
+
+    # List of matches
+    matches_win = _styled_win("list")
+
+    # Bottom separator, with arrows pointing down
+    bot_sep_win = _styled_win("separator")
+
+    # Help window with instructions at the bottom
+    help_win = _styled_win("help")
+
+    # Give windows their initial size
+    _resize_jump_to_dialog(edit_box, matches_win, bot_sep_win, help_win,
+                           sel_node_i, scroll)
+
+    _safe_curs_set(2)
+
+    # Logic duplication with _select_{next,prev}_menu_entry(), except we do a
+    # functional variant that returns the new (sel_node_i, scroll) values to
+    # avoid 'nonlocal'. TODO: Can this be factored out in some nice way?
+
+    def select_next_match():
+        if sel_node_i == len(matches) - 1:
+            return sel_node_i, scroll
+
+        if sel_node_i + 1 >= scroll + _height(matches_win) - _SCROLL_OFFSET \
+           and scroll < _max_scroll(matches, matches_win):
+
+            return sel_node_i + 1, scroll + 1
+
+        return sel_node_i + 1, scroll
+
+    def select_prev_match():
+        if sel_node_i == 0:
+            return sel_node_i, scroll
+
+        if sel_node_i - 1 < scroll + _SCROLL_OFFSET:
+            return sel_node_i - 1, max(scroll - 1, 0)
+
+        return sel_node_i - 1, scroll
+
+    while True:
+        if s != prev_s:
+            # The search text changed. Find new matching nodes.
+
+            prev_s = s
+
+            try:
+                # We could use re.IGNORECASE here instead of lower(), but this
+                # is noticeably less jerky while inputting regexes like
+                # '.*debug$' (though the '.*' is redundant there). Those
+                # probably have bad interactions with re.search(), which
+                # matches anywhere in the string.
+                #
+                # It's not horrible either way. Just a bit smoother.
+                regex_searches = [re.compile(regex).search
+                                  for regex in s.lower().split()]
+
+                # No exception thrown, so the regexes are okay
+                bad_re = None
+
+                # List of matching nodes
+                matches = []
+                add_match = matches.append
+
+                # Search symbols and choices
+
+                for node in _sorted_sc_nodes():
+                    # Symbol/choice
+                    sc = node.item
+
+                    for search in regex_searches:
+                        # Both the name and the prompt might be missing, since
+                        # we're searching both symbols and choices
+
+                        # Does the regex match either the symbol name or the
+                        # prompt (if any)?
+                        if not (sc.name and search(sc.name.lower()) or
+                                node.prompt and search(node.prompt[0].lower())):
+
+                            # Give up on the first regex that doesn't match, to
+                            # speed things up a bit when multiple regexes are
+                            # entered
+                            break
+
+                    else:
+                        add_match(node)
+
+                # Search menus and comments
+
+                for node in _sorted_menu_comment_nodes():
+                    for search in regex_searches:
+                        if not search(node.prompt[0].lower()):
+                            break
+                    else:
+                        add_match(node)
+
+            except re.error as e:
+                # Bad regex. Remember the error message so we can show it.
+                bad_re = "Bad regular expression"
+                # re.error.msg was added in Python 3.5
+                if hasattr(e, "msg"):
+                    bad_re += ": " + e.msg
+
+                matches = []
+
+            # Reset scroll and jump to the top of the list of matches
+            sel_node_i = scroll = 0
+
+        _draw_jump_to_dialog(edit_box, matches_win, bot_sep_win, help_win,
+                             s, s_i, hscroll,
+                             bad_re, matches, sel_node_i, scroll)
+        curses.doupdate()
+
+
+        c = _getch_compat(edit_box)
+
+        if c == "\n":
+            if matches:
+                _jump_to(matches[sel_node_i])
+                _safe_curs_set(0)
+                return True
+
+        elif c == "\x1B":  # \x1B = ESC
+            _safe_curs_set(0)
+            return False
+
+        elif c == curses.KEY_RESIZE:
+            # We adjust the scroll so that the selected node stays visible in
+            # the list when the terminal is resized, hence the 'scroll'
+            # assignment
+            scroll = _resize_jump_to_dialog(
+                edit_box, matches_win, bot_sep_win, help_win,
+                sel_node_i, scroll)
+
+        elif c == "\x06":  # \x06 = Ctrl-F
+            if matches:
+                _safe_curs_set(0)
+                _info_dialog(matches[sel_node_i], True)
+                _safe_curs_set(2)
+
+                scroll = _resize_jump_to_dialog(
+                    edit_box, matches_win, bot_sep_win, help_win,
+                    sel_node_i, scroll)
+
+        elif c == curses.KEY_DOWN:
+            sel_node_i, scroll = select_next_match()
+
+        elif c == curses.KEY_UP:
+            sel_node_i, scroll = select_prev_match()
+
+        elif c in (curses.KEY_NPAGE, "\x04"):  # Page Down/Ctrl-D
+            # Keep it simple. This way we get sane behavior for small windows,
+            # etc., for free.
+            for _ in range(_PG_JUMP):
+                sel_node_i, scroll = select_next_match()
+
+        # Page Up (no Ctrl-U, as it's already used by the edit box)
+        elif c == curses.KEY_PPAGE:
+            for _ in range(_PG_JUMP):
+                sel_node_i, scroll = select_prev_match()
+
+        elif c == curses.KEY_END:
+            sel_node_i = len(matches) - 1
+            scroll = _max_scroll(matches, matches_win)
+
+        elif c == curses.KEY_HOME:
+            sel_node_i = scroll = 0
+
+        else:
+            s, s_i, hscroll = _edit_text(c, s, s_i, hscroll,
+                                         _width(edit_box) - 2)
+
+
+# Obscure Python: We never pass a value for cached_nodes, and it keeps pointing
+# to the same list. This avoids a global.
+def _sorted_sc_nodes(cached_nodes=[]):
+    # Returns a sorted list of symbol and choice nodes to search. The symbol
+    # nodes appear first, sorted by name, and then the choice nodes, sorted by
+    # prompt and (secondarily) name.
+
+    if not cached_nodes:
+        # Add symbol nodes
+        for sym in sorted(_kconf.unique_defined_syms,
+                          key=lambda sym: sym.name):
+            # += is in-place for lists
+            cached_nodes += sym.nodes
+
+        # Add choice nodes
+
+        choices = sorted(_kconf.unique_choices,
+                         key=lambda choice: choice.name or "")
+
+        cached_nodes += sorted(
+            [node for choice in choices for node in choice.nodes],
+            key=lambda node: node.prompt[0] if node.prompt else "")
+
+    return cached_nodes
+
+
+def _sorted_menu_comment_nodes(cached_nodes=[]):
+    # Returns a list of menu and comment nodes to search, sorted by prompt,
+    # with the menus first
+
+    if not cached_nodes:
+        def prompt_text(mc):
+            return mc.prompt[0]
+
+        cached_nodes += sorted(_kconf.menus, key=prompt_text)
+        cached_nodes += sorted(_kconf.comments, key=prompt_text)
+
+    return cached_nodes
+
+
+def _resize_jump_to_dialog(edit_box, matches_win, bot_sep_win, help_win,
+                           sel_node_i, scroll):
+    # Resizes the jump-to dialog to fill the terminal.
+    #
+    # Returns the new scroll index. We adjust the scroll if needed so that the
+    # selected node stays visible.
+
+    screen_height, screen_width = _stdscr.getmaxyx()
+
+    bot_sep_win.resize(1, screen_width)
+
+    help_win_height = len(_JUMP_TO_HELP_LINES)
+    matches_win_height = screen_height - help_win_height - 4
+
+    if matches_win_height >= 1:
+        edit_box.resize(3, screen_width)
+        matches_win.resize(matches_win_height, screen_width)
+        help_win.resize(help_win_height, screen_width)
+
+        matches_win.mvwin(3, 0)
+        bot_sep_win.mvwin(3 + matches_win_height, 0)
+        help_win.mvwin(3 + matches_win_height + 1, 0)
+    else:
+        # Degenerate case. Give up on nice rendering and just prevent errors.
+
+        matches_win_height = 1
+
+        edit_box.resize(screen_height, screen_width)
+        matches_win.resize(1, screen_width)
+        help_win.resize(1, screen_width)
+
+        for win in matches_win, bot_sep_win, help_win:
+            win.mvwin(0, 0)
+
+    # Adjust the scroll so that the selected row is still within the window, if
+    # needed
+    if sel_node_i - scroll >= matches_win_height:
+        return sel_node_i - matches_win_height + 1
+    return scroll
+
+
+def _draw_jump_to_dialog(edit_box, matches_win, bot_sep_win, help_win,
+                         s, s_i, hscroll,
+                         bad_re, matches, sel_node_i, scroll):
+
+    edit_width = _width(edit_box) - 2
+
+    #
+    # Update list of matches
+    #
+
+    matches_win.erase()
+
+    if matches:
+        for i in range(scroll,
+                       min(scroll + _height(matches_win), len(matches))):
+
+            node = matches[i]
+
+            if isinstance(node.item, (Symbol, Choice)):
+                node_str = _name_and_val_str(node.item)
+                if node.prompt:
+                    node_str += ' "{}"'.format(node.prompt[0])
+            elif node.item == MENU:
+                node_str = 'menu "{}"'.format(node.prompt[0])
+            else:  # node.item == COMMENT
+                node_str = 'comment "{}"'.format(node.prompt[0])
+
+            _safe_addstr(matches_win, i - scroll, 0, node_str,
+                         _style["selection" if i == sel_node_i else "list"])
+
+    else:
+        # bad_re holds the error message from the re.error exception on errors
+        _safe_addstr(matches_win, 0, 0, bad_re or "No matches")
+
+    matches_win.noutrefresh()
+
+    #
+    # Update bottom separator line
+    #
+
+    bot_sep_win.erase()
+
+    # Draw arrows pointing down if the symbol list is scrolled up
+    if scroll < _max_scroll(matches, matches_win):
+        _safe_hline(bot_sep_win, 0, 4, curses.ACS_DARROW, _N_SCROLL_ARROWS)
+
+    bot_sep_win.noutrefresh()
+
+    #
+    # Update help window at bottom
+    #
+
+    help_win.erase()
+
+    for i, line in enumerate(_JUMP_TO_HELP_LINES):
+        _safe_addstr(help_win, i, 0, line)
+
+    help_win.noutrefresh()
+
+    #
+    # Update edit box. We do this last since it makes it handy to position the
+    # cursor.
+    #
+
+    edit_box.erase()
+
+    _draw_frame(edit_box, "Jump to symbol/choice/menu/comment")
+
+    # Draw arrows pointing up if the symbol list is scrolled down
+    if scroll > 0:
+        # TODO: Bit ugly that _style["frame"] is repeated here
+        _safe_hline(edit_box, 2, 4, curses.ACS_UARROW, _N_SCROLL_ARROWS,
+                    _style["frame"])
+
+    visible_s = s[hscroll:hscroll + edit_width]
+    _safe_addstr(edit_box, 1, 1, visible_s)
+
+    _safe_move(edit_box, 1, 1 + s_i - hscroll)
+
+    edit_box.noutrefresh()
+
+
+def _info_dialog(node, from_jump_to_dialog):
+    # Shows a fullscreen window with information about 'node'.
+    #
+    # If 'from_jump_to_dialog' is True, the information dialog was opened from
+    # within the jump-to-dialog. In this case, we make '/' from within the
+    # information dialog just return, to avoid a confusing recursive invocation
+    # of the jump-to-dialog.
+
+    # Top row, with title and arrows point up
+    top_line_win = _styled_win("separator")
+
+    # Text display
+    text_win = _styled_win("text")
+    text_win.keypad(True)
+
+    # Bottom separator, with arrows pointing down
+    bot_sep_win = _styled_win("separator")
+
+    # Help window with keys at the bottom
+    help_win = _styled_win("help")
+
+    # Give windows their initial size
+    _resize_info_dialog(top_line_win, text_win, bot_sep_win, help_win)
+
+
+    # Get lines of help text
+    lines = _info_str(node).split("\n")
+
+    # Index of first row in 'lines' to show
+    scroll = 0
+
+    while True:
+        _draw_info_dialog(node, lines, scroll, top_line_win, text_win,
+                          bot_sep_win, help_win)
+        curses.doupdate()
+
+
+        c = _getch_compat(text_win)
+
+        if c == curses.KEY_RESIZE:
+            _resize_info_dialog(top_line_win, text_win, bot_sep_win, help_win)
+
+        elif c in (curses.KEY_DOWN, "j", "J"):
+            if scroll < _max_scroll(lines, text_win):
+                scroll += 1
+
+        elif c in (curses.KEY_NPAGE, "\x04"):  # Page Down/Ctrl-D
+            scroll = min(scroll + _PG_JUMP, _max_scroll(lines, text_win))
+
+        elif c in (curses.KEY_PPAGE, "\x15"):  # Page Up/Ctrl-U
+            scroll = max(scroll - _PG_JUMP, 0)
+
+        elif c in (curses.KEY_END, "G"):
+            scroll = _max_scroll(lines, text_win)
+
+        elif c in (curses.KEY_HOME, "g"):
+            scroll = 0
+
+        elif c in (curses.KEY_UP, "k", "K"):
+            if scroll > 0:
+                scroll -= 1
+
+        elif c == "/":
+            # Support starting a search from within the information dialog
+
+            if from_jump_to_dialog:
+                return  # Avoid recursion
+
+            if _jump_to_dialog():
+                return  # Jumped to a symbol. Cancel the information dialog.
+
+            # Stay in the information dialog if the jump-to dialog was
+            # canceled. Resize it in case the terminal was resized while the
+            # fullscreen jump-to dialog was open.
+            _resize_info_dialog(top_line_win, text_win, bot_sep_win, help_win)
+
+        elif c in (curses.KEY_LEFT, curses.KEY_BACKSPACE, _ERASE_CHAR,
+                   "\x1B",  # \x1B = ESC
+                   "q", "Q", "h", "H"):
+
+            return
+
+
+def _resize_info_dialog(top_line_win, text_win, bot_sep_win, help_win):
+    # Resizes the info dialog to fill the terminal
+
+    screen_height, screen_width = _stdscr.getmaxyx()
+
+    top_line_win.resize(1, screen_width)
+    bot_sep_win.resize(1, screen_width)
+
+    help_win_height = len(_INFO_HELP_LINES)
+    text_win_height = screen_height - help_win_height - 2
+
+    if text_win_height >= 1:
+        text_win.resize(text_win_height, screen_width)
+        help_win.resize(help_win_height, screen_width)
+
+        text_win.mvwin(1, 0)
+        bot_sep_win.mvwin(1 + text_win_height, 0)
+        help_win.mvwin(1 + text_win_height + 1, 0)
+    else:
+        # Degenerate case. Give up on nice rendering and just prevent errors.
+
+        text_win.resize(1, screen_width)
+        help_win.resize(1, screen_width)
+
+        for win in text_win, bot_sep_win, help_win:
+            win.mvwin(0, 0)
+
+
+def _draw_info_dialog(node, lines, scroll, top_line_win, text_win,
+                      bot_sep_win, help_win):
+
+    text_win_height, text_win_width = text_win.getmaxyx()
+
+    # Note: The top row is deliberately updated last. See _draw_main().
+
+    #
+    # Update text display
+    #
+
+    text_win.erase()
+
+    for i, line in enumerate(lines[scroll:scroll + text_win_height]):
+        _safe_addstr(text_win, i, 0, line)
+
+    text_win.noutrefresh()
+
+    #
+    # Update bottom separator line
+    #
+
+    bot_sep_win.erase()
+
+    # Draw arrows pointing down if the symbol window is scrolled up
+    if scroll < _max_scroll(lines, text_win):
+        _safe_hline(bot_sep_win, 0, 4, curses.ACS_DARROW, _N_SCROLL_ARROWS)
+
+    bot_sep_win.noutrefresh()
+
+    #
+    # Update help window at bottom
+    #
+
+    help_win.erase()
+
+    for i, line in enumerate(_INFO_HELP_LINES):
+        _safe_addstr(help_win, i, 0, line)
+
+    help_win.noutrefresh()
+
+    #
+    # Update top row
+    #
+
+    top_line_win.erase()
+
+    # Draw arrows pointing up if the information window is scrolled down. Draw
+    # them before drawing the title, so the title ends up on top for small
+    # windows.
+    if scroll > 0:
+        _safe_hline(top_line_win, 0, 4, curses.ACS_UARROW, _N_SCROLL_ARROWS)
+
+    title = ("Symbol" if isinstance(node.item, Symbol) else
+             "Choice" if isinstance(node.item, Choice) else
+             "Menu"   if node.item == MENU else
+             "Comment") + " information"
+    _safe_addstr(top_line_win, 0, max((text_win_width - len(title))//2, 0),
+                 title)
+
+    top_line_win.noutrefresh()
+
+
+def _info_str(node):
+    # Returns information about the menu node 'node' as a string.
+    #
+    # The helper functions are responsible for adding newlines. This allows
+    # them to return "" if they don't want to add any output.
+
+    if isinstance(node.item, Symbol):
+        sym = node.item
+
+        return (
+            _name_info(sym) +
+            _prompt_info(sym) +
+            "Type: {}\n".format(TYPE_TO_STR[sym.type]) +
+            _value_info(sym) +
+            _help_info(sym) +
+            _direct_dep_info(sym) +
+            _defaults_info(sym) +
+            _select_imply_info(sym) +
+            _kconfig_def_info(sym)
+        )
+
+    if isinstance(node.item, Choice):
+        choice = node.item
+
+        return (
+            _name_info(choice) +
+            _prompt_info(choice) +
+            "Type: {}\n".format(TYPE_TO_STR[choice.type]) +
+            'Mode: {}\n'.format(choice.str_value) +
+            _help_info(choice) +
+            _choice_syms_info(choice) +
+            _direct_dep_info(choice) +
+            _defaults_info(choice) +
+            _kconfig_def_info(choice)
+        )
+
+    return _kconfig_def_info(node)  # node.item in (MENU, COMMENT)
+
+
+def _name_info(sc):
+    # Returns a string with the name of the symbol/choice. Names are optional
+    # for choices.
+
+    return "Name: {}\n".format(sc.name) if sc.name else ""
+
+
+def _prompt_info(sc):
+    # Returns a string listing the prompts of 'sc' (Symbol or Choice)
+
+    s = ""
+
+    for node in sc.nodes:
+        if node.prompt:
+            s += "Prompt: {}\n".format(node.prompt[0])
+
+    return s
+
+
+def _value_info(sym):
+    # Returns a string showing 'sym's value
+
+    # Only put quotes around the value for string symbols
+    return "Value: {}\n".format(
+        '"{}"'.format(sym.str_value)
+        if sym.orig_type == STRING
+        else sym.str_value)
+
+
+def _choice_syms_info(choice):
+    # Returns a string listing the choice symbols in 'choice'. Adds
+    # "(selected)" next to the selected one.
+
+    s = "Choice symbols:\n"
+
+    for sym in choice.syms:
+        s += "  - " + sym.name
+        if sym is choice.selection:
+            s += " (selected)"
+        s += "\n"
+
+    return s + "\n"
+
+
+def _help_info(sc):
+    # Returns a string with the help text(s) of 'sc' (Symbol or Choice).
+    # Symbols and choices defined in multiple locations can have multiple help
+    # texts.
+
+    s = "\n"
+
+    for node in sc.nodes:
+        if node.help is not None:
+            s += "Help:\n\n{}\n\n".format(_indent(node.help, 2))
+
+    return s
+
+
+def _direct_dep_info(sc):
+    # Returns a string describing the direct dependencies of 'sc' (Symbol or
+    # Choice). The direct dependencies are the OR of the dependencies from each
+    # definition location. The dependencies at each definition location come
+    # from 'depends on' and dependencies inherited from parent items.
+
+    return "" if sc.direct_dep is _kconf.y else \
+        'Direct dependencies (={}):\n{}\n' \
+        .format(TRI_TO_STR[expr_value(sc.direct_dep)],
+                _split_expr_info(sc.direct_dep, 2))
+
+
+def _defaults_info(sc):
+    # Returns a string describing the defaults of 'sc' (Symbol or Choice)
+
+    if not sc.defaults:
+        return ""
+
+    s = "Default"
+    if len(sc.defaults) > 1:
+        s += "s"
+    s += ":\n"
+
+    for val, cond in sc.orig_defaults:
+        s += "  - "
+        if isinstance(sc, Symbol):
+            s += _expr_str(val)
+
+            # Skip the tristate value hint if the expression is just a single
+            # symbol. _expr_str() already shows its value as a string.
+            #
+            # This also avoids showing the tristate value for string/int/hex
+            # defaults, which wouldn't make any sense.
+            if isinstance(val, tuple):
+                s += '  (={})'.format(TRI_TO_STR[expr_value(val)])
+        else:
+            # Don't print the value next to the symbol name for choice
+            # defaults, as it looks a bit confusing
+            s += val.name
+        s += "\n"
+
+        if cond is not _kconf.y:
+            s += "    Condition (={}):\n{}" \
+                 .format(TRI_TO_STR[expr_value(cond)],
+                         _split_expr_info(cond, 4))
+
+    return s + "\n"
+
+
+def _split_expr_info(expr, indent):
+    # Returns a string with 'expr' split into its top-level && or || operands,
+    # with one operand per line, together with the operand's value. This is
+    # usually enough to get something readable for long expressions. A fancier
+    # recursive thingy would be possible too.
+    #
+    # indent:
+    #   Number of leading spaces to add before the split expression.
+
+    if len(split_expr(expr, AND)) > 1:
+        split_op = AND
+        op_str = "&&"
+    else:
+        split_op = OR
+        op_str = "||"
+
+    s = ""
+    for i, term in enumerate(split_expr(expr, split_op)):
+        s += "{}{} {}".format(indent*" ",
+                              "  " if i == 0 else op_str,
+                              _expr_str(term))
+
+        # Don't bother showing the value hint if the expression is just a
+        # single symbol. _expr_str() already shows its value.
+        if isinstance(term, tuple):
+            s += "  (={})".format(TRI_TO_STR[expr_value(term)])
+
+        s += "\n"
+
+    return s
+
+
+def _select_imply_info(sym):
+    # Returns a string with information about which symbols 'select' or 'imply'
+    # 'sym'. The selecting/implying symbols are grouped according to which
+    # value they select/imply 'sym' to (n/m/y).
+
+    def sis(expr, val, title):
+        # sis = selects/implies
+        sis = [si for si in split_expr(expr, OR) if expr_value(si) == val]
+        if not sis:
+            return ""
+
+        res = title
+        for si in sis:
+            res += "  - {}\n".format(split_expr(si, AND)[0].name)
+        return res + "\n"
+
+    s = ""
+
+    if sym.rev_dep is not _kconf.n:
+        s += sis(sym.rev_dep, 2,
+                 "Symbols currently y-selecting this symbol:\n")
+        s += sis(sym.rev_dep, 1,
+                 "Symbols currently m-selecting this symbol:\n")
+        s += sis(sym.rev_dep, 0,
+                 "Symbols currently n-selecting this symbol (no effect):\n")
+
+    if sym.weak_rev_dep is not _kconf.n:
+        s += sis(sym.weak_rev_dep, 2,
+                 "Symbols currently y-implying this symbol:\n")
+        s += sis(sym.weak_rev_dep, 1,
+                 "Symbols currently m-implying this symbol:\n")
+        s += sis(sym.weak_rev_dep, 0,
+                 "Symbols currently n-implying this symbol (no effect):\n")
+
+    return s
+
+
+def _kconfig_def_info(item):
+    # Returns a string with the definition of 'item' in Kconfig syntax,
+    # together with the definition location(s) and their include and menu paths
+
+    nodes = [item] if isinstance(item, MenuNode) else item.nodes
+
+    s = "Kconfig definition{}, with parent deps. propagated to 'depends on'\n" \
+        .format("s" if len(nodes) > 1 else "")
+    s += (len(s) - 1)*"="
+
+    for node in nodes:
+        s += "\n\n" \
+             "At {}:{}\n" \
+             "{}" \
+             "Menu path: {}\n\n" \
+             "{}" \
+             .format(node.filename, node.linenr,
+                     _include_path_info(node),
+                     _menu_path_info(node),
+                     _indent(node.custom_str(_name_and_val_str), 2))
+
+    return s
+
+
+def _include_path_info(node):
+    if not node.include_path:
+        # In the top-level Kconfig file
+        return ""
+
+    return "Included via {}\n".format(
+        " -> ".join("{}:{}".format(filename, linenr)
+                    for filename, linenr in node.include_path))
+
+
+def _menu_path_info(node):
+    # Returns a string describing the menu path leading up to 'node'
+
+    path = ""
+
+    while node.parent is not _kconf.top_node:
+        node = node.parent
+
+        # Promptless choices might appear among the parents. Use
+        # standard_sc_expr_str() for them, so that they show up as
+        # '<choice (name if any)>'.
+        path = " -> " + (node.prompt[0] if node.prompt else
+                         standard_sc_expr_str(node.item)) + path
+
+    return "(Top)" + path
+
+
+def _indent(s, n):
+    # Returns 's' with each line indented 'n' spaces. textwrap.indent() is not
+    # available in Python 2 (it's 3.3+).
+
+    return "\n".join(n*" " + line for line in s.split("\n"))
+
+
+def _name_and_val_str(sc):
+    # Custom symbol/choice printer that shows symbol values after symbols
+
+    # Show the values of non-constant (non-quoted) symbols that don't look like
+    # numbers. Things like 123 are actually symbol references, and only work as
+    # expected due to undefined symbols getting their name as their value.
+    # Showing the symbol value for those isn't helpful though.
+    if isinstance(sc, Symbol) and not sc.is_constant and not _is_num(sc.name):
+        if not sc.nodes:
+            # Undefined symbol reference
+            return "{}(undefined/n)".format(sc.name)
+
+        return '{}(={})'.format(sc.name, sc.str_value)
+
+    # For other items, use the standard format
+    return standard_sc_expr_str(sc)
+
+
+def _expr_str(expr):
+    # Custom expression printer that shows symbol values
+    return expr_str(expr, _name_and_val_str)
+
+
+def _styled_win(style):
+    # Returns a new curses window with style 'style' and space as the fill
+    # character. The initial dimensions are (1, 1), so the window needs to be
+    # sized and positioned separately.
+
+    win = curses.newwin(1, 1)
+    _set_style(win, style)
+    return win
+
+
+def _set_style(win, style):
+    # Changes the style of an existing window
+
+    win.bkgdset(" ", _style[style])
+
+
+def _max_scroll(lst, win):
+    # Assuming 'lst' is a list of items to be displayed in 'win',
+    # returns the maximum number of steps 'win' can be scrolled down.
+    # We stop scrolling when the bottom item is visible.
+
+    return max(0, len(lst) - _height(win))
+
+
+def _edit_text(c, s, i, hscroll, width):
+    # Implements text editing commands for edit boxes. Takes a character (which
+    # could also be e.g. curses.KEY_LEFT) and the edit box state, and returns
+    # the new state after the character has been processed.
+    #
+    # c:
+    #   Character from user
+    #
+    # s:
+    #   Current contents of string
+    #
+    # i:
+    #   Current cursor index in string
+    #
+    # hscroll:
+    #   Index in s of the leftmost character in the edit box, for horizontal
+    #   scrolling
+    #
+    # width:
+    #   Width in characters of the edit box
+    #
+    # Return value:
+    #   An (s, i, hscroll) tuple for the new state
+
+    if c == curses.KEY_LEFT:
+        if i > 0:
+            i -= 1
+
+    elif c == curses.KEY_RIGHT:
+        if i < len(s):
+            i += 1
+
+    elif c in (curses.KEY_HOME, "\x01"):  # \x01 = CTRL-A
+        i = 0
+
+    elif c in (curses.KEY_END, "\x05"):  # \x05 = CTRL-E
+        i = len(s)
+
+    elif c in (curses.KEY_BACKSPACE, _ERASE_CHAR):
+        if i > 0:
+            s = s[:i-1] + s[i:]
+            i -= 1
+
+    elif c == curses.KEY_DC:
+        s = s[:i] + s[i+1:]
+
+    elif c == "\x17":  # \x17 = CTRL-W
+        # The \W removes characters like ',' one at a time
+        new_i = re.search(r"(?:\w*|\W)\s*$", s[:i]).start()
+        s = s[:new_i] + s[i:]
+        i = new_i
+
+    elif c == "\x0B":  # \x0B = CTRL-K
+        s = s[:i]
+
+    elif c == "\x15":  # \x15 = CTRL-U
+        s = s[i:]
+        i = 0
+
+    elif isinstance(c, str):
+        # Insert character
+        s = s[:i] + c + s[i:]
+        i += 1
+
+    # Adjust the horizontal scroll so that the cursor never touches the left or
+    # right edges of the edit box, except when it's at the beginning or the end
+    # of the string
+    if i < hscroll + _SCROLL_OFFSET:
+        hscroll = max(i - _SCROLL_OFFSET, 0)
+    elif i >= hscroll + width - _SCROLL_OFFSET:
+        max_scroll = max(len(s) - width + 1, 0)
+        hscroll = min(i - width + _SCROLL_OFFSET + 1, max_scroll)
+
+    return s, i, hscroll
+
+
+def _load_save_info():
+    # Returns an information string for load/save dialog boxes
+
+    return "(Relative to {})\n\nRefer to your home directory with ~" \
+           .format(os.path.join(os.getcwd(), ""))
+
+
+def _msg(title, text):
+    # Pops up a message dialog that can be dismissed with Space/Enter/ESC
+
+    _key_dialog(title, text, " \n")
+
+
+def _error(text):
+    # Pops up an error dialog that can be dismissed with Space/Enter/ESC
+
+    _msg("Error", text)
+
+
+def _node_str(node):
+    # Returns the complete menu entry text for a menu node.
+    #
+    # Example return value: "[*] Support for X"
+
+    # Calculate the indent to print the item with by checking how many levels
+    # above it the closest 'menuconfig' item is (this includes menus and
+    # choices as well as menuconfig symbols)
+    indent = 0
+    parent = node.parent
+    while not parent.is_menuconfig:
+        indent += _SUBMENU_INDENT
+        parent = parent.parent
+
+    # This approach gives nice alignment for empty string symbols ("()  Foo")
+    s = "{:{}}".format(_value_str(node), 3 + indent)
+
+    if _should_show_name(node):
+        if isinstance(node.item, Symbol):
+            s += " <{}>".format(node.item.name)
+        else:
+            # For choices, use standard_sc_expr_str(). That way they show up as
+            # '<choice (name if any)>'.
+            s += " " + standard_sc_expr_str(node.item)
+
+    if node.prompt:
+        if node.item == COMMENT:
+            s += " *** {} ***".format(node.prompt[0])
+        else:
+            s += " " + node.prompt[0]
+
+        if isinstance(node.item, Symbol):
+            sym = node.item
+
+            # Print "(NEW)" next to symbols without a user value (from e.g. a
+            # .config), but skip it for choice symbols in choices in y mode,
+            # and for symbols of UNKNOWN type (which generate a warning though)
+            if sym.user_value is None and sym.orig_type and \
+               not (sym.choice and sym.choice.tri_value == 2):
+
+                s += " (NEW)"
+
+    if isinstance(node.item, Choice) and node.item.tri_value == 2:
+        # Print the prompt of the selected symbol after the choice for
+        # choices in y mode
+        sym = node.item.selection
+        if sym:
+            for sym_node in sym.nodes:
+                # Use the prompt used at this choice location, in case the
+                # choice symbol is defined in multiple locations
+                if sym_node.parent is node and sym_node.prompt:
+                    s += " ({})".format(sym_node.prompt[0])
+                    break
+            else:
+                # If the symbol isn't defined at this choice location, then
+                # just use whatever prompt we can find for it
+                for sym_node in sym.nodes:
+                    if sym_node.prompt:
+                        s += " ({})".format(sym_node.prompt[0])
+                        break
+
+    # Print "--->" next to nodes that have menus that can potentially be
+    # entered. Print "----" if the menu is empty. We don't allow those to be
+    # entered.
+    if node.is_menuconfig:
+        s += "  --->" if _shown_nodes(node) else "  ----"
+
+    return s
+
+
+def _should_show_name(node):
+    # Returns True if 'node' is a symbol or choice whose name should shown (if
+    # any, as names are optional for choices)
+
+    # The 'not node.prompt' case only hits in show-all mode, for promptless
+    # symbols and choices
+    return not node.prompt or \
+           (_show_name and isinstance(node.item, (Symbol, Choice)))
+
+
+def _value_str(node):
+    # Returns the value part ("[*]", "<M>", "(foo)" etc.) of a menu node
+
+    item = node.item
+
+    if item in (MENU, COMMENT):
+        return ""
+
+    # Wouldn't normally happen, and generates a warning
+    if not item.orig_type:
+        return ""
+
+    if item.orig_type in (STRING, INT, HEX):
+        return "({})".format(item.str_value)
+
+    # BOOL or TRISTATE
+
+    if _is_y_mode_choice_sym(item):
+        return "(X)" if item.choice.selection is item else "( )"
+
+    tri_val_str = (" ", "M", "*")[item.tri_value]
+
+    if len(item.assignable) <= 1:
+        # Pinned to a single value
+        return "" if isinstance(item, Choice) else "-{}-".format(tri_val_str)
+
+    if item.type == BOOL:
+        return "[{}]".format(tri_val_str)
+
+    # item.type == TRISTATE
+    if item.assignable == (1, 2):
+        return "{{{}}}".format(tri_val_str)  # {M}/{*}
+    return "<{}>".format(tri_val_str)
+
+
+def _is_y_mode_choice_sym(item):
+    # The choice mode is an upper bound on the visibility of choice symbols, so
+    # we can check the choice symbols' own visibility to see if the choice is
+    # in y mode
+    return isinstance(item, Symbol) and item.choice and item.visibility == 2
+
+
+def _check_valid(sym, s):
+    # Returns True if the string 's' is a well-formed value for 'sym'.
+    # Otherwise, displays an error and returns False.
+
+    if sym.orig_type not in (INT, HEX):
+        return True  # Anything goes for non-int/hex symbols
+
+    base = 10 if sym.orig_type == INT else 16
+    try:
+        int(s, base)
+    except ValueError:
+        _error("'{}' is a malformed {} value"
+               .format(s, TYPE_TO_STR[sym.orig_type]))
+        return False
+
+    for low_sym, high_sym, cond in sym.ranges:
+        if expr_value(cond):
+            low_s = low_sym.str_value
+            high_s = high_sym.str_value
+
+            if not int(low_s, base) <= int(s, base) <= int(high_s, base):
+                _error("{} is outside the range {}-{}"
+                       .format(s, low_s, high_s))
+                return False
+
+            break
+
+    return True
+
+
+def _range_info(sym):
+    # Returns a string with information about the valid range for the symbol
+    # 'sym', or None if 'sym' doesn't have a range
+
+    if sym.orig_type in (INT, HEX):
+        for low, high, cond in sym.ranges:
+            if expr_value(cond):
+                return "Range: {}-{}".format(low.str_value, high.str_value)
+
+    return None
+
+
+def _is_num(name):
+    # Heuristic to see if a symbol name looks like a number, for nicer output
+    # when printing expressions. Things like 16 are actually symbol names, only
+    # they get their name as their value when the symbol is undefined.
+
+    try:
+        int(name)
+    except ValueError:
+        if not name.startswith(("0x", "0X")):
+            return False
+
+        try:
+            int(name, 16)
+        except ValueError:
+            return False
+
+    return True
+
+
+def _getch_compat(win):
+    # Uses get_wch() if available (Python 3.3+) and getch() otherwise.
+    #
+    # Also falls back on getch() if get_wch() raises curses.error, to work
+    # around an issue when resizing the terminal on at least macOS Catalina.
+    # See https://github.com/ulfalizer/Kconfiglib/issues/84.
+    #
+    # Also handles a PDCurses resizing quirk.
+
+    try:
+        c = win.get_wch()
+    except (AttributeError, curses.error):
+        c = win.getch()
+        if 0 <= c <= 255:
+            c = chr(c)
+
+    # Decent resizing behavior on PDCurses requires calling resize_term(0, 0)
+    # after receiving KEY_RESIZE, while ncurses (usually) handles terminal
+    # resizing automatically in get(_w)ch() (see the end of the
+    # resizeterm(3NCURSES) man page).
+    #
+    # resize_term(0, 0) reliably fails and does nothing on ncurses, so this
+    # hack gives ncurses/PDCurses compatibility for resizing. I don't know
+    # whether it would cause trouble for other implementations.
+    if c == curses.KEY_RESIZE:
+        try:
+            curses.resize_term(0, 0)
+        except curses.error:
+            pass
+
+    return c
+
+
+def _warn(*args):
+    # Temporarily returns from curses to shell mode and prints a warning to
+    # stderr. The warning would get lost in curses mode.
+    curses.endwin()
+    print("menuconfig warning: ", end="", file=sys.stderr)
+    print(*args, file=sys.stderr)
+    curses.doupdate()
+
+
+# Ignore exceptions from some functions that might fail, e.g. for small
+# windows. They usually do reasonable things anyway.
+
+
+def _safe_curs_set(visibility):
+    try:
+        curses.curs_set(visibility)
+    except curses.error:
+        pass
+
+
+def _safe_addstr(win, *args):
+    # Clip the line to avoid wrapping to the next line, which looks glitchy.
+    # addchstr() would do it for us, but it's not available in the 'curses'
+    # module.
+
+    attr = None
+    if isinstance(args[0], str):
+        y, x = win.getyx()
+        s = args[0]
+        if len(args) == 2:
+            attr = args[1]
+    else:
+        y, x, s = args[:3]
+        if len(args) == 4:
+            attr = args[3]
+
+    maxlen = _width(win) - x
+    s = s.expandtabs()
+
+    try:
+        # The 'curses' module uses wattr_set() internally if you pass 'attr',
+        # overwriting the background style, so setting 'attr' to 0 in the first
+        # case won't do the right thing
+        if attr is None:
+            win.addnstr(y, x, s, maxlen)
+        else:
+            win.addnstr(y, x, s, maxlen, attr)
+    except curses.error:
+        pass
+
+
+def _safe_addch(win, *args):
+    try:
+        win.addch(*args)
+    except curses.error:
+        pass
+
+
+def _safe_hline(win, *args):
+    try:
+        win.hline(*args)
+    except curses.error:
+        pass
+
+
+def _safe_vline(win, *args):
+    try:
+        win.vline(*args)
+    except curses.error:
+        pass
+
+
+def _safe_move(win, *args):
+    try:
+        win.move(*args)
+    except curses.error:
+        pass
+
+
+def _change_c_lc_ctype_to_utf8():
+    # See _CHANGE_C_LC_CTYPE_TO_UTF8
+
+    if _IS_WINDOWS:
+        # Windows rarely has issues here, and the PEP 538 implementation avoids
+        # changing the locale on it. None of the UTF-8 locales below were
+        # supported from some quick testing either. Play it safe.
+        return
+
+    def try_set_locale(loc):
+        try:
+            locale.setlocale(locale.LC_CTYPE, loc)
+            return True
+        except locale.Error:
+            return False
+
+    # Is LC_CTYPE set to the C locale?
+    if locale.setlocale(locale.LC_CTYPE) == "C":
+        # This list was taken from the PEP 538 implementation in the CPython
+        # code, in Python/pylifecycle.c
+        for loc in "C.UTF-8", "C.utf8", "UTF-8":
+            if try_set_locale(loc):
+                # LC_CTYPE successfully changed
+                return
+
+
+if __name__ == "__main__":
+    _main()
diff --git a/scripts/Kconfiglib/oldconfig.py b/scripts/Kconfiglib/oldconfig.py
new file mode 100755
index 0000000..53434b2
--- /dev/null
+++ b/scripts/Kconfiglib/oldconfig.py
@@ -0,0 +1,246 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Implements oldconfig functionality.
+
+  1. Loads existing .config
+  2. Prompts for the value of all modifiable symbols/choices that
+     aren't already set in the .config
+  3. Writes an updated .config
+
+The default input/output filename is '.config'. A different filename can be
+passed in the KCONFIG_CONFIG environment variable.
+
+When overwriting a configuration file, the old version is saved to
+<filename>.old (e.g. .config.old).
+
+Entering '?' displays the help text of the symbol/choice, if any.
+
+Unlike 'make oldconfig', this script doesn't print menu titles and comments,
+but gives Kconfig definition locations. Printing menus and comments would be
+pretty easy to add: Look at the parents of each item, and print all menu
+prompts and comments unless they have already been printed (assuming you want
+to skip "irrelevant" menus).
+"""
+from __future__ import print_function
+
+import sys
+
+from kconfiglib import Symbol, Choice, BOOL, TRISTATE, HEX, standard_kconfig
+
+
+# Python 2/3 compatibility hack
+if sys.version_info[0] < 3:
+    input = raw_input
+
+
+def _main():
+    # Earlier symbols in Kconfig files might depend on later symbols and become
+    # visible if their values change. This flag is set to True if the value of
+    # any symbol changes, in which case we rerun the oldconfig to check for new
+    # visible symbols.
+    global conf_changed
+
+    kconf = standard_kconfig(__doc__)
+    print(kconf.load_config())
+
+    while True:
+        conf_changed = False
+
+        for node in kconf.node_iter():
+            oldconfig(node)
+
+        if not conf_changed:
+            break
+
+    print(kconf.write_config())
+
+
+def oldconfig(node):
+    """
+    Prompts the user for a value if node.item is a visible symbol/choice with
+    no user value.
+    """
+    # See main()
+    global conf_changed
+
+    # Only symbols and choices can be configured
+    if not isinstance(node.item, (Symbol, Choice)):
+        return
+
+    # Skip symbols and choices that aren't visible
+    if not node.item.visibility:
+        return
+
+    # Skip symbols and choices that don't have a prompt (at this location)
+    if not node.prompt:
+        return
+
+    if isinstance(node.item, Symbol):
+        sym = node.item
+
+        # Skip symbols that already have a user value
+        if sym.user_value is not None:
+            return
+
+        # Skip symbols that can only have a single value, due to selects
+        if len(sym.assignable) == 1:
+            return
+
+        # Skip symbols in choices in y mode. We ask once for the entire choice
+        # instead.
+        if sym.choice and sym.choice.tri_value == 2:
+            return
+
+        # Loop until the user enters a valid value or enters a blank string
+        # (for the default value)
+        while True:
+            val = input("{} ({}) [{}] ".format(
+                node.prompt[0], _name_and_loc_str(sym),
+                _default_value_str(sym)))
+
+            if val == "?":
+                _print_help(node)
+                continue
+
+            # Substitute a blank string with the default value the symbol
+            # would get
+            if not val:
+                val = sym.str_value
+
+            # Automatically add a "0x" prefix for hex symbols, like the
+            # menuconfig interface does. This isn't done when loading .config
+            # files, hence why set_value() doesn't do it automatically.
+            if sym.type == HEX and not val.startswith(("0x", "0X")):
+                val = "0x" + val
+
+            old_str_val = sym.str_value
+
+            # Kconfiglib itself will print a warning here if the value
+            # is invalid, so we don't need to bother
+            if sym.set_value(val):
+                # Valid value input. We're done with this node.
+
+                if sym.str_value != old_str_val:
+                    conf_changed = True
+
+                return
+
+    else:
+        choice = node.item
+
+        # Skip choices that already have a visible user selection...
+        if choice.user_selection and choice.user_selection.visibility == 2:
+            # ...unless there are new visible symbols in the choice. (We know
+            # they have y (2) visibility in that case, because m-visible
+            # symbols get demoted to n-visibility in y-mode choices, and the
+            # user-selected symbol had visibility y.)
+            for sym in choice.syms:
+                if sym is not choice.user_selection and sym.visibility and \
+                   sym.user_value is None:
+                    # New visible symbols in the choice
+                    break
+            else:
+                # No new visible symbols in the choice
+                return
+
+        # Get a list of available selections. The mode of the choice limits
+        # the visibility of the choice value symbols, so this will indirectly
+        # skip choices in n and m mode.
+        options = [sym for sym in choice.syms if sym.visibility == 2]
+
+        if not options:
+            # No y-visible choice value symbols
+            return
+
+        # Loop until the user enters a valid selection or a blank string (for
+        # the default selection)
+        while True:
+            print("{} ({})".format(node.prompt[0], _name_and_loc_str(choice)))
+
+            for i, sym in enumerate(options, 1):
+                print("{} {}. {} ({})".format(
+                    ">" if sym is choice.selection else " ",
+                    i,
+                    # Assume people don't define choice symbols with multiple
+                    # prompts. That generates a warning anyway.
+                    sym.nodes[0].prompt[0],
+                    sym.name))
+
+            sel_index = input("choice[1-{}]: ".format(len(options)))
+
+            if sel_index == "?":
+                _print_help(node)
+                continue
+
+            # Pick the default selection if the string is blank
+            if not sel_index:
+                choice.selection.set_value(2)
+                break
+
+            try:
+                sel_index = int(sel_index)
+            except ValueError:
+                print("Bad index", file=sys.stderr)
+                continue
+
+            if not 1 <= sel_index <= len(options):
+                print("Bad index", file=sys.stderr)
+                continue
+
+            # Valid selection
+
+            if options[sel_index - 1].tri_value != 2:
+                conf_changed = True
+
+            options[sel_index - 1].set_value(2)
+            break
+
+        # Give all of the non-selected visible choice symbols the user value n.
+        # This makes it so that the choice is no longer considered new once we
+        # do additional passes, if the reason that it was considered new was
+        # that it had new visible choice symbols.
+        #
+        # Only giving visible choice symbols the user value n means we will
+        # prompt for the choice again if later user selections make more new
+        # choice symbols visible, which is correct.
+        for sym in choice.syms:
+            if sym is not choice.user_selection and sym.visibility:
+                sym.set_value(0)
+
+
+def _name_and_loc_str(sc):
+    # Helper for printing the name of the symbol/choice 'sc' along with the
+    # location(s) in the Kconfig files where it is defined. Unnamed choices
+    # return "choice" instead of the name.
+
+    return "{}, defined at {}".format(
+        sc.name or "choice",
+        ", ".join("{}:{}".format(node.filename, node.linenr)
+                  for node in sc.nodes))
+
+
+def _print_help(node):
+    print("\n" + (node.help or "No help text\n"))
+
+
+def _default_value_str(sym):
+    # Returns the "m/M/y" string in e.g.
+    #
+    #   TRISTATE_SYM prompt (TRISTATE_SYM, defined at Kconfig:9) [n/M/y]:
+    #
+    # For string/int/hex, returns the default value as-is.
+
+    if sym.type in (BOOL, TRISTATE):
+        return "/".join(("NMY" if sym.tri_value == tri else "nmy")[tri]
+                        for tri in sym.assignable)
+
+    # string/int/hex
+    return sym.str_value
+
+
+if __name__ == "__main__":
+    _main()
diff --git a/scripts/Kconfiglib/olddefconfig.py b/scripts/Kconfiglib/olddefconfig.py
new file mode 100755
index 0000000..2dadfb4
--- /dev/null
+++ b/scripts/Kconfiglib/olddefconfig.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2018-2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Updates an old .config file or creates a new one, by filling in default values
+for all new symbols. This is the same as picking the default selection for all
+symbols in oldconfig, or entering the menuconfig interface and immediately
+saving.
+
+The default input/output filename is '.config'. A different filename can be
+passed in the KCONFIG_CONFIG environment variable.
+
+When overwriting a configuration file, the old version is saved to
+<filename>.old (e.g. .config.old).
+"""
+import kconfiglib
+
+
+def main():
+    kconf = kconfiglib.standard_kconfig(__doc__)
+    print(kconf.load_config())
+    print(kconf.write_config())
+
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/Kconfiglib/savedefconfig.py b/scripts/Kconfiglib/savedefconfig.py
new file mode 100755
index 0000000..0f36bde
--- /dev/null
+++ b/scripts/Kconfiglib/savedefconfig.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Saves a minimal configuration file that only lists symbols that differ in value
+from their defaults. Loading such a configuration file is equivalent to loading
+the "full" configuration file.
+
+Minimal configuration files are handy to start from when editing configuration
+files by hand.
+
+The default input configuration file is '.config'. A different input filename
+can be passed in the KCONFIG_CONFIG environment variable.
+
+Note: Minimal configurations can also be generated from within the menuconfig
+interface.
+"""
+import argparse
+
+import kconfiglib
+
+
+def main():
+    parser = argparse.ArgumentParser(
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        description=__doc__)
+
+    parser.add_argument(
+        "--kconfig",
+        default="Kconfig",
+        help="Top-level Kconfig file (default: Kconfig)")
+
+    parser.add_argument(
+        "--out",
+        metavar="MINIMAL_CONFIGURATION",
+        default="defconfig",
+        help="Output filename for minimal configuration (default: defconfig)")
+
+    args = parser.parse_args()
+
+    kconf = kconfiglib.Kconfig(args.kconfig, suppress_traceback=True)
+    print(kconf.load_config())
+    print(kconf.write_min_config(args.out))
+
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/Kconfiglib/setconfig.py b/scripts/Kconfiglib/setconfig.py
new file mode 100755
index 0000000..f9cf5cd
--- /dev/null
+++ b/scripts/Kconfiglib/setconfig.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python3
+
+# Copyright (c) 2019, Ulf Magnusson
+# SPDX-License-Identifier: ISC
+
+"""
+Simple utility for setting configuration values from the command line.
+
+Sample usage:
+
+  $ setconfig FOO_SUPPORT=y BAR_BITS=8
+
+Note: Symbol names should not be prefixed with 'CONFIG_'.
+
+The exit status on errors is 1.
+
+The default input/output configuration file is '.config'. A different filename
+can be passed in the KCONFIG_CONFIG environment variable.
+
+When overwriting a configuration file, the old version is saved to
+<filename>.old (e.g. .config.old).
+"""
+import argparse
+import sys
+
+import kconfiglib
+
+
+def main():
+    parser = argparse.ArgumentParser(
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        description=__doc__)
+
+    parser.add_argument(
+        "--kconfig",
+        default="Kconfig",
+        help="Top-level Kconfig file (default: Kconfig)")
+
+    parser.add_argument(
+        "--no-check-exists",
+        dest="check_exists",
+        action="store_false",
+        help="Ignore assignments to non-existent symbols instead of erroring "
+             "out")
+
+    parser.add_argument(
+        "--no-check-value",
+        dest="check_value",
+        action="store_false",
+        help="Ignore assignments that didn't \"take\" (where the symbol got a "
+             "different value, e.g. due to unsatisfied dependencies) instead "
+             "of erroring out")
+
+    parser.add_argument(
+        "assignments",
+        metavar="ASSIGNMENT",
+        nargs="*",
+        help="A 'NAME=value' assignment")
+
+    args = parser.parse_args()
+
+    kconf = kconfiglib.Kconfig(args.kconfig, suppress_traceback=True)
+    print(kconf.load_config())
+
+    for arg in args.assignments:
+        if "=" not in arg:
+            sys.exit("error: no '=' in assignment: '{}'".format(arg))
+        name, value = arg.split("=", 1)
+
+        if name not in kconf.syms:
+            if not args.check_exists:
+                continue
+            sys.exit("error: no symbol '{}' in configuration".format(name))
+
+        sym = kconf.syms[name]
+
+        if not sym.set_value(value):
+            sys.exit("error: '{}' is an invalid value for the {} symbol {}"
+                     .format(value, kconfiglib.TYPE_TO_STR[sym.orig_type],
+                             name))
+
+        if args.check_value and sym.str_value != value:
+            sys.exit("error: {} was assigned the value '{}', but got the "
+                     "value '{}'. Check the symbol's dependencies, and make "
+                     "sure that it has a prompt."
+                     .format(name, value, sym.str_value))
+
+    print(kconf.write_config())
+
+
+if __name__ == "__main__":
+    main()
-- 
2.34.1



From apatel at ventanamicro.com  Sat Jul 30 21:56:52 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Sun, 31 Jul 2022 10:26:52 +0530
Subject: [PATCH v4 14/15] platform: Remove redundant config.mk from all platforms
In-Reply-To: <20220731045653.165058-1-apatel@ventanamicro.com>
References: <20220731045653.165058-1-apatel@ventanamicro.com>
Message-ID: <20220731045653.165058-15-apatel@ventanamicro.com>

The options defined in config.mk can be specified in objects.mk of each
platform so let us remove config.mk from all platforms.

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 Makefile                           |  4 +-
 platform/andes/ae350/config.mk     | 36 --------------
 platform/andes/ae350/objects.mk    | 29 +++++++++++
 platform/fpga/ariane/config.mk     | 36 --------------
 platform/fpga/ariane/objects.mk    | 35 ++++++++++++++
 platform/fpga/openpiton/config.mk  | 35 --------------
 platform/fpga/openpiton/objects.mk | 35 ++++++++++++++
 platform/generic/config.mk         | 40 ----------------
 platform/generic/objects.mk        | 33 +++++++++++++
 platform/kendryte/k210/config.mk   | 19 --------
 platform/kendryte/k210/objects.mk  | 12 +++++
 platform/nuclei/ux600/config.mk    | 30 ------------
 platform/nuclei/ux600/objects.mk   | 22 +++++++++
 platform/template/config.mk        | 77 ------------------------------
 platform/template/objects.mk       | 72 ++++++++++++++++++++++++++++
 platform/template/platform.c       |  2 +-
 16 files changed, 240 insertions(+), 277 deletions(-)
 delete mode 100644 platform/andes/ae350/config.mk
 delete mode 100644 platform/fpga/ariane/config.mk
 delete mode 100644 platform/fpga/openpiton/config.mk
 delete mode 100644 platform/generic/config.mk
 delete mode 100644 platform/kendryte/k210/config.mk
 delete mode 100644 platform/nuclei/ux600/config.mk
 delete mode 100644 platform/template/config.mk

diff --git a/Makefile b/Makefile
index 820a137..e6406a8 100644
--- a/Makefile
+++ b/Makefile
@@ -204,7 +204,7 @@ firmware-object-mks=$(shell if [ -d $(firmware_dir) ]; then find $(firmware_dir)
 .PHONY: all
 all:
 
-# Include platform specific .config and config.mk
+# Include platform specific .config
 ifdef PLATFORM
 .PHONY: menuconfig
 menuconfig: $(platform_src_dir)/Kconfig $(src_dir)/Kconfig
@@ -228,8 +228,6 @@ $(KCONFIG_AUTOCMD): $(KCONFIG_CONFIG)
 
 include $(KCONFIG_CONFIG)
 include $(KCONFIG_AUTOCMD)
-
-include $(platform_src_dir)/config.mk
 endif
 
 # Include all object.mk files
diff --git a/platform/andes/ae350/config.mk b/platform/andes/ae350/config.mk
deleted file mode 100644
index f555ef5..0000000
--- a/platform/andes/ae350/config.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2019 Andes Technology Corporation
-#
-# Authors:
-#   Zong Li <zong at andestech.com>
-#   Nylon Chen <nylon7 at andestech.com>
-
-# Compiler flags
-platform-cppflags-y =
-platform-cflags-y =
-platform-asflags-y =
-platform-ldflags-y =
-
-# Blobs to build
-FW_TEXT_START=0x00000000
-
-FW_DYNAMIC=y
-
-FW_JUMP=y
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-  FW_JUMP_ADDR=0x400000
-else
-  FW_JUMP_ADDR=0x200000
-endif
-FW_JUMP_FDT_ADDR=0x2000000
-
-FW_PAYLOAD=y
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-  FW_PAYLOAD_OFFSET=0x400000
-else
-  FW_PAYLOAD_OFFSET=0x200000
-endif
-
-FW_PAYLOAD_FDT_ADDR=0x2000000
diff --git a/platform/andes/ae350/objects.mk b/platform/andes/ae350/objects.mk
index 5369677..80f0737 100644
--- a/platform/andes/ae350/objects.mk
+++ b/platform/andes/ae350/objects.mk
@@ -8,4 +8,33 @@
 #   Nylon Chen <nylon7 at andestech.com>
 #
 
+# Compiler flags
+platform-cppflags-y =
+platform-cflags-y =
+platform-asflags-y =
+platform-ldflags-y =
+
+# Objects to build
 platform-objs-y += cache.o platform.o plicsw.o plmt.o
+
+# Blobs to build
+FW_TEXT_START=0x00000000
+
+FW_DYNAMIC=y
+
+FW_JUMP=y
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+  FW_JUMP_ADDR=0x400000
+else
+  FW_JUMP_ADDR=0x200000
+endif
+FW_JUMP_FDT_ADDR=0x2000000
+
+FW_PAYLOAD=y
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+  FW_PAYLOAD_OFFSET=0x400000
+else
+  FW_PAYLOAD_OFFSET=0x200000
+endif
+
+FW_PAYLOAD_FDT_ADDR=0x2000000
diff --git a/platform/fpga/ariane/config.mk b/platform/fpga/ariane/config.mk
deleted file mode 100644
index 3556461..0000000
--- a/platform/fpga/ariane/config.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (C) 2019 FORTH-ICS/CARV
-#		Panagiotis Peristerakis <perister at ics.forth.gr>
-#
-
-#for more infos, check out /platform/template/config.mk
-
-PLATFORM_RISCV_XLEN = 64
-
-# Blobs to build
-FW_TEXT_START=0x80000000
-FW_JUMP=n
-
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
- # This needs to be 4MB aligned for 32-bit support
- FW_JUMP_ADDR=0x80400000
- else
- # This needs to be 2MB aligned for 64-bit support
- FW_JUMP_ADDR=0x80200000
- endif
-FW_JUMP_FDT_ADDR=0x82200000
-
-# Firmware with payload configuration.
-FW_PAYLOAD=y
-
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-# This needs to be 4MB aligned for 32-bit support
-  FW_PAYLOAD_OFFSET=0x400000
-else
-# This needs to be 2MB aligned for 64-bit support
-  FW_PAYLOAD_OFFSET=0x200000
-endif
-FW_PAYLOAD_FDT_ADDR=0x82200000
-FW_PAYLOAD_ALIGN=0x1000
diff --git a/platform/fpga/ariane/objects.mk b/platform/fpga/ariane/objects.mk
index 814e6da..83581ac 100644
--- a/platform/fpga/ariane/objects.mk
+++ b/platform/fpga/ariane/objects.mk
@@ -5,4 +5,39 @@
 #		Panagiotis Peristerakis <perister at ics.forth.gr>
 #
 
+# Compiler flags
+platform-cppflags-y =
+platform-cflags-y =
+platform-asflags-y =
+platform-ldflags-y =
+
+# Object to build
 platform-objs-y += platform.o
+
+PLATFORM_RISCV_XLEN = 64
+
+# Blobs to build
+FW_TEXT_START=0x80000000
+FW_JUMP=n
+
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+ # This needs to be 4MB aligned for 32-bit support
+ FW_JUMP_ADDR=0x80400000
+ else
+ # This needs to be 2MB aligned for 64-bit support
+ FW_JUMP_ADDR=0x80200000
+ endif
+FW_JUMP_FDT_ADDR=0x82200000
+
+# Firmware with payload configuration.
+FW_PAYLOAD=y
+
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+# This needs to be 4MB aligned for 32-bit support
+  FW_PAYLOAD_OFFSET=0x400000
+else
+# This needs to be 2MB aligned for 64-bit support
+  FW_PAYLOAD_OFFSET=0x200000
+endif
+FW_PAYLOAD_FDT_ADDR=0x82200000
+FW_PAYLOAD_ALIGN=0x1000
diff --git a/platform/fpga/openpiton/config.mk b/platform/fpga/openpiton/config.mk
deleted file mode 100644
index a969b25..0000000
--- a/platform/fpga/openpiton/config.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2020 Western Digital Corporation or its affiliates.
-#
-
-#for more infos, check out /platform/template/config.mk
-
-PLATFORM_RISCV_XLEN = 64
-
-# Blobs to build
-FW_TEXT_START=0x80000000
-FW_JUMP=n
-
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
- # This needs to be 4MB aligned for 32-bit support
- FW_JUMP_ADDR=0x80400000
- else
- # This needs to be 2MB aligned for 64-bit support
- FW_JUMP_ADDR=0x80200000
- endif
-FW_JUMP_FDT_ADDR=0x82200000
-
-# Firmware with payload configuration.
-FW_PAYLOAD=y
-
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-# This needs to be 4MB aligned for 32-bit support
-  FW_PAYLOAD_OFFSET=0x400000
-else
-# This needs to be 2MB aligned for 64-bit support
-  FW_PAYLOAD_OFFSET=0x200000
-endif
-FW_PAYLOAD_FDT_ADDR=0x82200000
-FW_PAYLOAD_ALIGN=0x1000
diff --git a/platform/fpga/openpiton/objects.mk b/platform/fpga/openpiton/objects.mk
index 30a3c4f..c8c345a 100644
--- a/platform/fpga/openpiton/objects.mk
+++ b/platform/fpga/openpiton/objects.mk
@@ -4,4 +4,39 @@
 # Copyright (c) 2020 Western Digital Corporation or its affiliates.
 #
 
+# Compiler flags
+platform-cppflags-y =
+platform-cflags-y =
+platform-asflags-y =
+platform-ldflags-y =
+
+# Objects to build
 platform-objs-y += platform.o
+
+PLATFORM_RISCV_XLEN = 64
+
+# Blobs to build
+FW_TEXT_START=0x80000000
+FW_JUMP=n
+
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+ # This needs to be 4MB aligned for 32-bit support
+ FW_JUMP_ADDR=0x80400000
+ else
+ # This needs to be 2MB aligned for 64-bit support
+ FW_JUMP_ADDR=0x80200000
+ endif
+FW_JUMP_FDT_ADDR=0x82200000
+
+# Firmware with payload configuration.
+FW_PAYLOAD=y
+
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+# This needs to be 4MB aligned for 32-bit support
+  FW_PAYLOAD_OFFSET=0x400000
+else
+# This needs to be 2MB aligned for 64-bit support
+  FW_PAYLOAD_OFFSET=0x200000
+endif
+FW_PAYLOAD_FDT_ADDR=0x82200000
+FW_PAYLOAD_ALIGN=0x1000
diff --git a/platform/generic/config.mk b/platform/generic/config.mk
deleted file mode 100644
index 8151974..0000000
--- a/platform/generic/config.mk
+++ /dev/null
@@ -1,40 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2020 Western Digital Corporation or its affiliates.
-#
-# Authors:
-#   Anup Patel <anup.patel at wdc.com>
-#
-
-# Compiler flags
-platform-cppflags-y =
-platform-cflags-y =
-platform-asflags-y =
-platform-ldflags-y =
-
-# Command for platform specific "make run"
-platform-runcmd = qemu-system-riscv$(PLATFORM_RISCV_XLEN) -M virt -m 256M \
-  -nographic -bios $(build_dir)/platform/generic/firmware/fw_payload.elf
-
-# Blobs to build
-FW_TEXT_START=0x80000000
-FW_DYNAMIC=y
-FW_JUMP=y
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-  # This needs to be 4MB aligned for 32-bit system
-  FW_JUMP_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x400000)))
-else
-  # This needs to be 2MB aligned for 64-bit system
-  FW_JUMP_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x200000)))
-endif
-FW_JUMP_FDT_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x2200000)))
-FW_PAYLOAD=y
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-  # This needs to be 4MB aligned for 32-bit system
-  FW_PAYLOAD_OFFSET=0x400000
-else
-  # This needs to be 2MB aligned for 64-bit system
-  FW_PAYLOAD_OFFSET=0x200000
-endif
-FW_PAYLOAD_FDT_ADDR=$(FW_JUMP_FDT_ADDR)
diff --git a/platform/generic/objects.mk b/platform/generic/objects.mk
index 4907754..136853e 100644
--- a/platform/generic/objects.mk
+++ b/platform/generic/objects.mk
@@ -7,5 +7,38 @@
 #   Anup Patel <anup.patel at wdc.com>
 #
 
+# Compiler flags
+platform-cppflags-y =
+platform-cflags-y =
+platform-asflags-y =
+platform-ldflags-y =
+
+# Command for platform specific "make run"
+platform-runcmd = qemu-system-riscv$(PLATFORM_RISCV_XLEN) -M virt -m 256M \
+  -nographic -bios $(build_dir)/platform/generic/firmware/fw_payload.elf
+
+# Objects to build
 platform-objs-y += platform.o
 platform-objs-y += platform_override_modules.o
+
+# Blobs to build
+FW_TEXT_START=0x80000000
+FW_DYNAMIC=y
+FW_JUMP=y
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+  # This needs to be 4MB aligned for 32-bit system
+  FW_JUMP_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x400000)))
+else
+  # This needs to be 2MB aligned for 64-bit system
+  FW_JUMP_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x200000)))
+endif
+FW_JUMP_FDT_ADDR=$(shell printf "0x%X" $$(($(FW_TEXT_START) + 0x2200000)))
+FW_PAYLOAD=y
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+  # This needs to be 4MB aligned for 32-bit system
+  FW_PAYLOAD_OFFSET=0x400000
+else
+  # This needs to be 2MB aligned for 64-bit system
+  FW_PAYLOAD_OFFSET=0x200000
+endif
+FW_PAYLOAD_FDT_ADDR=$(FW_JUMP_FDT_ADDR)
diff --git a/platform/kendryte/k210/config.mk b/platform/kendryte/k210/config.mk
deleted file mode 100644
index 8a9b814..0000000
--- a/platform/kendryte/k210/config.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2019 Western Digital Corporation or its affiliates.
-#
-# Authors:
-#   Damien Le Moal <damien.lemoal at wdc.com>
-#
-
-# Compiler flags
-platform-cppflags-y =
-platform-cflags-y =
-platform-asflags-y =
-platform-ldflags-y =
-
-# Blobs to build
-FW_TEXT_START=0x80000000
-FW_PAYLOAD=y
-FW_PAYLOAD_ALIGN=0x1000
diff --git a/platform/kendryte/k210/objects.mk b/platform/kendryte/k210/objects.mk
index b74da74..1bfb898 100644
--- a/platform/kendryte/k210/objects.mk
+++ b/platform/kendryte/k210/objects.mk
@@ -7,8 +7,20 @@
 #   Damien Le Moal <damien.lemoal at wdc.com>
 #
 
+# Compiler flags
+platform-cppflags-y =
+platform-cflags-y =
+platform-asflags-y =
+platform-ldflags-y =
+
+# Objects to build
 platform-objs-y += platform.o
 
 platform-objs-y += k210.o
 platform-varprefix-k210.o = dt_k210
 platform-padding-k210.o = 2048
+
+# Blobs to build
+FW_TEXT_START=0x80000000
+FW_PAYLOAD=y
+FW_PAYLOAD_ALIGN=0x1000
diff --git a/platform/nuclei/ux600/config.mk b/platform/nuclei/ux600/config.mk
deleted file mode 100644
index dddcc4e..0000000
--- a/platform/nuclei/ux600/config.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2020 Nuclei Corporation or its affiliates.
-#
-# Authors:
-#   lujun <lujun at nucleisys.com>
-#   hqfang <578567190 at qq.com>
-#
-
-# Compiler flags
-platform-cppflags-y =
-platform-cflags-y =
-platform-asflags-y =
-platform-ldflags-y =
-
-# Command for platform specific "make run"
-platform-runcmd = xl_spike \
-  $(build_dir)/platform/nuclei/ux600/firmware/fw_payload.elf
-
-# Blobs to build
-FW_TEXT_START=0xA0000000
-FW_DYNAMIC=y
-FW_JUMP=y
-
-FW_JUMP_ADDR=0xA0200000
-FW_JUMP_FDT_ADDR=0xA8000000
-FW_PAYLOAD=y
-FW_PAYLOAD_OFFSET=0x200000
-FW_PAYLOAD_FDT_ADDR=0xA8000000
diff --git a/platform/nuclei/ux600/objects.mk b/platform/nuclei/ux600/objects.mk
index 8c36c90..7c429e0 100644
--- a/platform/nuclei/ux600/objects.mk
+++ b/platform/nuclei/ux600/objects.mk
@@ -8,4 +8,26 @@
 #   hqfang <578567190 at qq.com>
 #
 
+# Compiler flags
+platform-cppflags-y =
+platform-cflags-y =
+platform-asflags-y =
+platform-ldflags-y =
+
+# Command for platform specific "make run"
+platform-runcmd = xl_spike \
+  $(build_dir)/platform/nuclei/ux600/firmware/fw_payload.elf
+
+# Objects to build
 platform-objs-y += platform.o
+
+# Blobs to build
+FW_TEXT_START=0xA0000000
+FW_DYNAMIC=y
+FW_JUMP=y
+
+FW_JUMP_ADDR=0xA0200000
+FW_JUMP_FDT_ADDR=0xA8000000
+FW_PAYLOAD=y
+FW_PAYLOAD_OFFSET=0x200000
+FW_PAYLOAD_FDT_ADDR=0xA8000000
diff --git a/platform/template/config.mk b/platform/template/config.mk
deleted file mode 100644
index f817342..0000000
--- a/platform/template/config.mk
+++ /dev/null
@@ -1,77 +0,0 @@
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2019 Western Digital Corporation or its affiliates.
-#
-
-# Compiler pre-processor flags
-platform-cppflags-y =
-
-# C Compiler and assembler flags.
-platform-cflags-y =
-platform-asflags-y =
-
-# Linker flags: additional libraries and object files that the platform
-# code needs can be added here
-platform-ldflags-y =
-
-#
-# Command for platform specific "make run"
-# Useful for development and debugging on plaftform simulator (such as QEMU)
-#
-# platform-runcmd = your_platform_run.sh
-
-#
-# Platform RISC-V XLEN, ABI, ISA and Code Model configuration.
-# These are optional parameters but platforms can optionaly provide it.
-# Some of these are guessed based on GCC compiler capabilities
-#
-# PLATFORM_RISCV_XLEN = 64
-# PLATFORM_RISCV_ABI = lp64
-# PLATFORM_RISCV_ISA = rv64imafdc
-# PLATFORM_RISCV_CODE_MODEL = medany
-
-# Firmware load address configuration. This is mandatory.
-FW_TEXT_START=0x80000000
-
-# Optional parameter for path to external FDT
-# FW_FDT_PATH="path to platform flattened device tree file"
-
-#
-# Dynamic firmware configuration.
-# Optional parameters are commented out. Uncomment and define these parameters
-# as needed.
-#
-FW_DYNAMIC=<y|n>
-
-#
-# Jump firmware configuration.
-# Optional parameters are commented out. Uncomment and define these parameters
-# as needed.
-#
-FW_JUMP=<y|n>
-# This needs to be 4MB aligned for 32-bit support
-# This needs to be 2MB aligned for 64-bit support
-# ifeq ($(PLATFORM_RISCV_XLEN), 32)
-# FW_JUMP_ADDR=0x80400000
-# else
-# FW_JUMP_ADDR=0x80200000
-# endif
-# FW_JUMP_FDT_ADDR=0x82200000
-
-#
-# Firmware with payload configuration.
-# Optional parameters are commented out. Uncomment and define these parameters
-# as needed.
-#
-FW_PAYLOAD=<y|n>
-# This needs to be 4MB aligned for 32-bit support
-# This needs to be 2MB aligned for 64-bit support
-ifeq ($(PLATFORM_RISCV_XLEN), 32)
-FW_PAYLOAD_OFFSET=0x400000
-else
-FW_PAYLOAD_OFFSET=0x200000
-endif
-# FW_PAYLOAD_ALIGN=0x1000
-# FW_PAYLOAD_PATH="path to next boot stage binary image file"
-# FW_PAYLOAD_FDT_ADDR=0x82200000
diff --git a/platform/template/objects.mk b/platform/template/objects.mk
index caaec2b..0025928 100644
--- a/platform/template/objects.mk
+++ b/platform/template/objects.mk
@@ -4,6 +4,33 @@
 # Copyright (c) 2019 Western Digital Corporation or its affiliates.
 #
 
+# Compiler pre-processor flags
+platform-cppflags-y =
+
+# C Compiler and assembler flags.
+platform-cflags-y =
+platform-asflags-y =
+
+# Linker flags: additional libraries and object files that the platform
+# code needs can be added here
+platform-ldflags-y =
+
+#
+# Command for platform specific "make run"
+# Useful for development and debugging on plaftform simulator (such as QEMU)
+#
+# platform-runcmd = your_platform_run.sh
+
+#
+# Platform RISC-V XLEN, ABI, ISA and Code Model configuration.
+# These are optional parameters but platforms can optionaly provide it.
+# Some of these are guessed based on GCC compiler capabilities
+#
+# PLATFORM_RISCV_XLEN = 64
+# PLATFORM_RISCV_ABI = lp64
+# PLATFORM_RISCV_ISA = rv64imafdc
+# PLATFORM_RISCV_CODE_MODEL = medany
+
 # Space separated list of object file names to be compiled for the platform
 platform-objs-y += platform.o
 
@@ -13,3 +40,48 @@ platform-objs-y += platform.o
 # source file be in the form <dt file name>.dts
 #
 # platform-objs-y += <dt file name>.o
+
+# Firmware load address configuration. This is mandatory.
+FW_TEXT_START=0x80000000
+
+# Optional parameter for path to external FDT
+# FW_FDT_PATH="path to platform flattened device tree file"
+
+#
+# Dynamic firmware configuration.
+# Optional parameters are commented out. Uncomment and define these parameters
+# as needed.
+#
+FW_DYNAMIC=<y|n>
+
+#
+# Jump firmware configuration.
+# Optional parameters are commented out. Uncomment and define these parameters
+# as needed.
+#
+FW_JUMP=<y|n>
+# This needs to be 4MB aligned for 32-bit support
+# This needs to be 2MB aligned for 64-bit support
+# ifeq ($(PLATFORM_RISCV_XLEN), 32)
+# FW_JUMP_ADDR=0x80400000
+# else
+# FW_JUMP_ADDR=0x80200000
+# endif
+# FW_JUMP_FDT_ADDR=0x82200000
+
+#
+# Firmware with payload configuration.
+# Optional parameters are commented out. Uncomment and define these parameters
+# as needed.
+#
+FW_PAYLOAD=<y|n>
+# This needs to be 4MB aligned for 32-bit support
+# This needs to be 2MB aligned for 64-bit support
+ifeq ($(PLATFORM_RISCV_XLEN), 32)
+FW_PAYLOAD_OFFSET=0x400000
+else
+FW_PAYLOAD_OFFSET=0x200000
+endif
+# FW_PAYLOAD_ALIGN=0x1000
+# FW_PAYLOAD_PATH="path to next boot stage binary image file"
+# FW_PAYLOAD_FDT_ADDR=0x82200000
diff --git a/platform/template/platform.c b/platform/template/platform.c
index 5524847..f3802da 100644
--- a/platform/template/platform.c
+++ b/platform/template/platform.c
@@ -11,7 +11,7 @@
 
 /*
  * Include these files as needed.
- * See config.mk PLATFORM_xxx configuration parameters.
+ * See objects.mk PLATFORM_xxx configuration parameters.
  */
 #include <sbi_utils/ipi/aclint_mswi.h>
 #include <sbi_utils/irqchip/plic.h>
-- 
2.34.1



From apatel at ventanamicro.com  Sat Jul 30 21:56:53 2022
From: apatel at ventanamicro.com (Anup Patel)
Date: Sun, 31 Jul 2022 10:26:53 +0530
Subject: [PATCH v4 15/15] docs: Update documentation for kconfig support
In-Reply-To: <20220731045653.165058-1-apatel@ventanamicro.com>
References: <20220731045653.165058-1-apatel@ventanamicro.com>
Message-ID: <20220731045653.165058-16-apatel@ventanamicro.com>

We update all documentation files to:
1) Remove references to platform specific config.mk file since it is
   has been removed.
2) Add details about platform specific configs/defconfig and Kconfig
   files mandatory for each platform.
3) Add required packages in top-level README.md

Signed-off-by: Anup Patel <apatel at ventanamicro.com>
---
 README.md                   | 25 +++++++++++++++++++++++--
 docs/firmware/fw_dynamic.md |  2 +-
 docs/firmware/fw_jump.md    |  4 ++--
 docs/firmware/fw_payload.md |  4 ++--
 docs/platform/platform.md   |  4 ++--
 docs/platform_guide.md      |  9 +++++----
 6 files changed, 35 insertions(+), 13 deletions(-)

diff --git a/README.md b/README.md
index 54fabe4..67d491d 100644
--- a/README.md
+++ b/README.md
@@ -92,8 +92,8 @@ N.B. Any S-mode boot loader (i.e. U-Boot) doesn't need to support HSM extension,
 as it doesn't need to boot all the harts. The operating system should be
 capable enough to bring up all other non-booting harts using HSM extension.
 
-Required Toolchain
-------------------
+Required Toolchain and Packages
+-------------------------------
 
 OpenSBI can be compiled natively or cross-compiled on a x86 host. For
 cross-compilation, you can build your own toolchain, download a prebuilt one
@@ -115,6 +115,14 @@ triple is used (e.g. *-target riscv64-unknown-elf*).
 Please note that only a 64-bit version of the toolchain is available in
 the Bootlin toolchain repository for now.
 
+In addition to a toolchain, OpenSBI also requires following packages on
+the host:
+
+1. device-tree-compiler: The device tree compiler for compiling device
+   tree sources (DTS files).
+2. python3: The python 3.0 (or higher) language support for various
+   scripts.
+
 Building and Installing the OpenSBI Platform-Independent Library
 ----------------------------------------------------------------
 
@@ -196,6 +204,19 @@ top-level make command line. These options, such as *PLATFORM_<xyz>* or
 *docs/platform/<platform_name>.md* files and
 *docs/firmware/<firmware_name>.md* files.
 
+All OpenSBI platforms support Kconfig style build-time configuration. Users
+can change the build-time configuration of a platform using a graphical
+interface as follows:
+```
+make PLATFORM=<platform_subdir> menuconfig
+```
+
+Alternately, an OpenSBI platform can have multiple default configurations
+and users can select a custom default configuration as follows:
+```
+make PLATFORM=<platform_subdir> PLATFORM_DEFCONFIG=<platform_custom_defconfig>
+```
+
 Building 32-bit / 64-bit OpenSBI Images
 ---------------------------------------
 By default, building OpenSBI generates 32-bit or 64-bit images based on the
diff --git a/docs/firmware/fw_dynamic.md b/docs/firmware/fw_dynamic.md
index 7b9b192..e251488 100644
--- a/docs/firmware/fw_dynamic.md
+++ b/docs/firmware/fw_dynamic.md
@@ -20,7 +20,7 @@ the booting stage binary to follow OpenSBI firmware.
 A platform can enable *FW_DYNAMIC* firmware using any of the following methods.
 
 1. Specifying `FW_DYNAMIC=y` on the top level `make` command line.
-2. Specifying `FW_DYNAMIC=y` in the target platform *config.mk* configuration
+2. Specifying `FW_DYNAMIC=y` in the target platform *objects.mk* configuration
 file.
 
 The compiled *FW_DYNAMIC* firmware ELF file is named *fw_dynamic.elf*. It's
diff --git a/docs/firmware/fw_jump.md b/docs/firmware/fw_jump.md
index eea3013..35a4301 100644
--- a/docs/firmware/fw_jump.md
+++ b/docs/firmware/fw_jump.md
@@ -15,7 +15,7 @@ and the booting stage binary to follow the OpenSBI firmware.
 A platform *FW_JUMP* firmware can be enabled by any of the following methods:
 
 1. Specifying `FW_JUMP=y` on the top level `make` command line.
-2. Specifying `FW_JUMP=y` in the target platform *config.mk* configuration file.
+2. Specifying `FW_JUMP=y` in the target platform *objects.mk* configuration file.
 
 The compiled *FW_JUMP* firmware ELF file is named *fw_jump.elf*. Its expanded
 image file is *fw_jump.bin*. Both files are created in the platform-specific
@@ -26,7 +26,7 @@ build directory under the *build/platform/<platform_subdir>/firmware* directory.
 
 To operate correctly, a *FW_JUMP* firmware requires some configuration
 parameters to be defined using either the top level `make` command line or the
-target platform *config.mk* configuration file. The possible parameters are as
+target platform *objects.mk* configuration file. The possible parameters are as
 follows:
 
 * **FW_JUMP_ADDR** - Address of the entry point of the booting stage to be
diff --git a/docs/firmware/fw_payload.md b/docs/firmware/fw_payload.md
index 0947448..3bb918f 100644
--- a/docs/firmware/fw_payload.md
+++ b/docs/firmware/fw_payload.md
@@ -20,7 +20,7 @@ Enabling *FW_PAYLOAD* compilation
 The *FW_PAYLOAD* firmware can be enabled by any of the following methods:
 
 1. Specifying `FW_PAYLOAD=y` on the top level `make` command line.
-2. Specifying `FW_PAYLOAD=y` in the target platform *config.mk* configuration
+2. Specifying `FW_PAYLOAD=y` in the target platform *objects.mk* configuration
    file.
 
 The compiled *FW_PAYLOAD* firmware ELF file is named *fw_jump.elf*. Its
@@ -33,7 +33,7 @@ Configuration Options
 
 A *FW_PAYLOAD* firmware is built according to configuration parameters and
 options. These configuration parameters can be defined using either the top
-level `make` command line or the target platform *config.mk* configuration
+level `make` command line or the target platform *objects.mk* configuration
 file. The parameters currently defined are as follows:
 
 * **FW_PAYLOAD_OFFSET** - Offset from *FW_TEXT_BASE* where the payload binary
diff --git a/docs/platform/platform.md b/docs/platform/platform.md
index cb4bec6..b726e1d 100644
--- a/docs/platform/platform.md
+++ b/docs/platform/platform.md
@@ -42,8 +42,8 @@ OpenSBI currently supports the following virtual and hardware platforms:
 The code for these supported platforms can be used as example to implement
 support for other platforms. The *platform/template* directory also provides
 template files for implementing support for a new platform. The *object.mk*,
-*config.mk* and *platform.c* template files provides enough comments to
-facilitate the implementation.
+*Kconfig*, *configs/defconfig* and *platform.c* template files provides enough
+comments to facilitate the implementation.
 
 [generic.md]: generic.md
 [qemu_virt.md]: qemu_virt.md
diff --git a/docs/platform_guide.md b/docs/platform_guide.md
index b6c2c2e..8ac5aa0 100644
--- a/docs/platform_guide.md
+++ b/docs/platform_guide.md
@@ -28,11 +28,12 @@ Adding support for a new platform
 Support for a new platform named *&lt;xyz&gt;* can be added as follows:
 
 1. Create a directory named *&lt;xyz&gt;* under the *platform/* directory.
-2. Create a platform configuration file named *config.mk* under the
-   *platform/&lt;xyz&gt;/* directory. This configuration file will provide
+2. Create a platform configuration files named *Kconfig* and *configs/defconfig*
+   under the *platform/&lt;xyz&gt;/* directory. This configuration files will
+   provide the build time configuration for the sources to be compiled.
+3. Create a *platform/&lt;xyz&gt;/objects.mk* file for listing the platform
+   object files to be compiled. This file also provides platform-specific
    compiler flags, and select firmware options.
-3. Create a *platform/&lt;xyz&gt;/objects.mk* file for listing the
-   platform-specific object files to be compiled.
 4. Create a *platform/&lt;xyz&gt;/platform.c* file providing a
    *struct sbi_platform* instance.
 
-- 
2.34.1



