[PATCH v3] x86/early_printk: add MMIO-based UARTs

Denis Mukhin via B4 Relay posted 1 patch 8 months, 3 weeks ago
Documentation/admin-guide/kernel-parameters.txt |  9 ++++-
arch/x86/kernel/early_printk.c                  | 45 ++++++++++++++++++++++++-
2 files changed, 52 insertions(+), 2 deletions(-)
[PATCH v3] x86/early_printk: add MMIO-based UARTs
Posted by Denis Mukhin via B4 Relay 8 months, 3 weeks ago
From: Denis Mukhin <dmukhin@ford.com>

During the bring-up of an x86 board, the kernel was crashing before
reaching the platform's console driver because of a bug in the firmware,
leaving no trace of the boot progress.

It was discovered that the only available method to debug the kernel
boot process was via the platform's MMIO-based UART, as the board lacked
an I/O port-based UART, PCI UART, or functional video output.

Then it turned out that earlyprintk= does not have a knob to configure
the MMIO-mapped UART.

Extend the early printk facility to support platform MMIO-based UARTs
on x86 systems, enabling debugging during the system bring-up phase.

The command line syntax to enable platform MMIO-based UART is:
  earlyprintk=mmio,membase[,{nocfg|baudrate}][,keep]

Note, the change does not integrate MMIO-based UART support to:
  arch/x86/boot/early_serial_console.c

Also, update kernel parameters documentation with the new syntax and
add missing 'nocfg' setting to PCI serial cards description.

Signed-off-by: Denis Mukhin <dmukhin@ford.com>
---
Changes in v3:
- Fixed formatting in early_mmio_serial_init()
- Rebased on tip/master, new code switched to using static_call_update()
- Doc update: added nocfg description for earlyprintk=pciserial and a note
  on 32-bit memory-mapped UARTs
- Link to v2: https://lore.kernel.org/r/20250314-earlyprintk-v2-1-2bcbe05290b8@ford.com

Changes in v2:
- Fixed description of early_mmio_serial_init()
- Link to v1: https://lore.kernel.org/r/20250313-earlyprintk-v1-1-8f818d77a8dd@ford.com
---
 Documentation/admin-guide/kernel-parameters.txt |  9 ++++-
 arch/x86/kernel/early_printk.c                  | 45 ++++++++++++++++++++++++-
 2 files changed, 52 insertions(+), 2 deletions(-)

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 3e5e41cbe3ce907bb286a6d86456241e02976f6d..1d1eaeac04b31ba808b9196d0f928050acfd17b7 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1407,14 +1407,21 @@
 			earlyprintk=serial[,0x...[,baudrate]]
 			earlyprintk=ttySn[,baudrate]
 			earlyprintk=dbgp[debugController#]
-			earlyprintk=pciserial[,force],bus:device.function[,baudrate]
+			earlyprintk=pciserial[,force],bus:device.function[,{nocfg|baudrate}]
 			earlyprintk=xdbc[xhciController#]
 			earlyprintk=bios
+			earlyprintk=mmio,membase[,{nocfg|baudrate}]
 
 			earlyprintk is useful when the kernel crashes before
 			the normal console is initialized. It is not enabled by
 			default because it has some cosmetic problems.
 
+			Only 32-bit memory addresses are supported for "mmio"
+			and "pciserial" devices.
+
+			Use "nocfg" to skip UART configuration, assume
+			BIOS/firmware has configured UART correctly.
+
 			Append ",keep" to not disable it when the real console
 			takes over.
 
diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c
index fc1714bad04588abc3468a0e72df26513250162b..611f27e3890c2852cece31a9e948e7212c4e10fe 100644
--- a/arch/x86/kernel/early_printk.c
+++ b/arch/x86/kernel/early_printk.c
@@ -190,7 +190,6 @@ static __init void early_serial_init(char *s)
 	early_serial_hw_init(divisor);
 }
 
-#ifdef CONFIG_PCI
 static __noendbr void mem32_serial_out(unsigned long addr, int offset, int value)
 {
 	u32 __iomem *vaddr = (u32 __iomem *)addr;
@@ -207,6 +206,45 @@ static __noendbr unsigned int mem32_serial_in(unsigned long addr, int offset)
 }
 ANNOTATE_NOENDBR_SYM(mem32_serial_in);
 
+/*
+ * early_mmio_serial_init() - Initialize MMIO-based early serial console.
+ * @s: MMIO-based serial specification.
+ */
+static __init void early_mmio_serial_init(char *s)
+{
+	unsigned long baudrate;
+	unsigned long membase;
+	char *e;
+
+	if (*s == ',')
+		s++;
+
+	if (!strncmp(s, "0x", 2)) {
+		/* NB: only 32-bit addresses are supported. */
+		membase = simple_strtoul(s, &e, 16);
+		early_serial_base = (unsigned long)early_ioremap(membase, PAGE_SIZE);
+
+		static_call_update(serial_in, mem32_serial_in);
+		static_call_update(serial_out, mem32_serial_out);
+
+		s += strcspn(s, ",");
+		if (*s == ',')
+			s++;
+	}
+
+	if (!strncmp(s, "nocfg", 5)) {
+		baudrate = 0;
+	} else {
+		baudrate = simple_strtoul(s, &e, 0);
+		if (baudrate == 0 || s == e)
+			baudrate = DEFAULT_BAUD;
+	}
+
+	if (baudrate)
+		early_serial_hw_init(115200 / baudrate);
+}
+
+#ifdef CONFIG_PCI
 /*
  * early_pci_serial_init()
  *
@@ -351,6 +389,11 @@ static int __init setup_early_printk(char *buf)
 	keep = (strstr(buf, "keep") != NULL);
 
 	while (*buf != '\0') {
+		if (!strncmp(buf, "mmio", 4)) {
+			early_mmio_serial_init(buf + 4);
+			early_console_register(&early_serial_console, keep);
+			buf += 4;
+		}
 		if (!strncmp(buf, "serial", 6)) {
 			buf += 6;
 			early_serial_init(buf);

---
base-commit: cc086afdd3132fa2a80c3891639c2352521e5d73
change-id: 20250313-earlyprintk-f68bcf10febc

Best regards,
-- 
Denis Mukhin <dmukhin@ford.com>
Re: [PATCH v3] x86/early_printk: add MMIO-based UARTs
Posted by Andy Shevchenko 8 months, 1 week ago
On Mon, Mar 24, 2025 at 05:55:40PM -0700, Denis Mukhin wrote:
> During the bring-up of an x86 board, the kernel was crashing before
> reaching the platform's console driver because of a bug in the firmware,
> leaving no trace of the boot progress.
> 
> It was discovered that the only available method to debug the kernel
> boot process was via the platform's MMIO-based UART, as the board lacked
> an I/O port-based UART, PCI UART, or functional video output.
> 
> Then it turned out that earlyprintk= does not have a knob to configure
> the MMIO-mapped UART.
> 
> Extend the early printk facility to support platform MMIO-based UARTs
> on x86 systems, enabling debugging during the system bring-up phase.
> 
> The command line syntax to enable platform MMIO-based UART is:
>   earlyprintk=mmio,membase[,{nocfg|baudrate}][,keep]
> 
> Note, the change does not integrate MMIO-based UART support to:
>   arch/x86/boot/early_serial_console.c
> 
> Also, update kernel parameters documentation with the new syntax and
> add missing 'nocfg' setting to PCI serial cards description.

Just for your information: Have you seen this rather old series of mine?

https://bitbucket.org/andy-shev/linux/commits/branch/topic%2Fx86%2Fboot-earlyprintk

-- 
With Best Regards,
Andy Shevchenko
Re: [PATCH v3] x86/early_printk: add MMIO-based UARTs
Posted by Denis Mukhin 8 months, 1 week ago
Hi!

On Monday, April 7th, 2025 at 9:38 AM, Andy Shevchenko <andriy.shevchenko@intel.com> wrote:

> 
> 
> On Mon, Mar 24, 2025 at 05:55:40PM -0700, Denis Mukhin wrote:
> 
> > During the bring-up of an x86 board, the kernel was crashing before
> > reaching the platform's console driver because of a bug in the firmware,
> > leaving no trace of the boot progress.
> > 
> > It was discovered that the only available method to debug the kernel
> > boot process was via the platform's MMIO-based UART, as the board lacked
> > an I/O port-based UART, PCI UART, or functional video output.
> > 
> > Then it turned out that earlyprintk= does not have a knob to configure
> > the MMIO-mapped UART.
> > 
> > Extend the early printk facility to support platform MMIO-based UARTs
> > on x86 systems, enabling debugging during the system bring-up phase.
> > 
> > The command line syntax to enable platform MMIO-based UART is:
> > earlyprintk=mmio,membase[,{nocfg|baudrate}][,keep]
> > 
> > Note, the change does not integrate MMIO-based UART support to:
> > arch/x86/boot/early_serial_console.c
> > 
> > Also, update kernel parameters documentation with the new syntax and
> > add missing 'nocfg' setting to PCI serial cards description.
> 
> 
> Just for your information: Have you seen this rather old series of mine?
> 
> https://bitbucket.org/andy-shev/linux/commits/branch/topic%2Fx86%2Fboot-earlyprintk

This is a nice cleanup!
Thanks for sharing.

Sorry, I haven't seen the series above, I had to write a patch (which was months ago).
It's just I could not post it on the mailing list until recently.

> 
> --
> With Best Regards,
> Andy Shevchenko
> 

Thanks,
Denis
Re: [PATCH v3] x86/early_printk: add MMIO-based UARTs
Posted by Andy Shevchenko 8 months, 1 week ago
On Mon, Apr 07, 2025 at 11:04:42PM +0000, Denis Mukhin wrote:
> On Monday, April 7th, 2025 at 9:38 AM, Andy Shevchenko <andriy.shevchenko@intel.com> wrote:
> > On Mon, Mar 24, 2025 at 05:55:40PM -0700, Denis Mukhin wrote:

> > > During the bring-up of an x86 board, the kernel was crashing before
> > > reaching the platform's console driver because of a bug in the firmware,
> > > leaving no trace of the boot progress.
> > > 
> > > It was discovered that the only available method to debug the kernel
> > > boot process was via the platform's MMIO-based UART, as the board lacked
> > > an I/O port-based UART, PCI UART, or functional video output.
> > > 
> > > Then it turned out that earlyprintk= does not have a knob to configure
> > > the MMIO-mapped UART.
> > > 
> > > Extend the early printk facility to support platform MMIO-based UARTs
> > > on x86 systems, enabling debugging during the system bring-up phase.
> > > 
> > > The command line syntax to enable platform MMIO-based UART is:
> > > earlyprintk=mmio,membase[,{nocfg|baudrate}][,keep]
> > > 
> > > Note, the change does not integrate MMIO-based UART support to:
> > > arch/x86/boot/early_serial_console.c
> > > 
> > > Also, update kernel parameters documentation with the new syntax and
> > > add missing 'nocfg' setting to PCI serial cards description.
> > 
> > Just for your information: Have you seen this rather old series of mine?
> > 
> > https://bitbucket.org/andy-shev/linux/commits/branch/topic%2Fx86%2Fboot-earlyprintk
> 
> This is a nice cleanup!
> Thanks for sharing.
> 
> Sorry, I haven't seen the series above, I had to write a patch (which was months ago).
> It's just I could not post it on the mailing list until recently.

No problem. Can you look at
https://lore.kernel.org/r/20250407172214.792745-1-andriy.shevchenko@linux.intel.com
? I forgot to Cc you and that is an important fix.

-- 
With Best Regards,
Andy Shevchenko
Re: [PATCH v3] x86/early_printk: add MMIO-based UARTs
Posted by Denis Mukhin 8 months, 1 week ago
On Tuesday, April 8th, 2025 at 1:30 AM, Andy Shevchenko <andriy.shevchenko@intel.com> wrote:

> 
> 
> On Mon, Apr 07, 2025 at 11:04:42PM +0000, Denis Mukhin wrote:
> 
> > On Monday, April 7th, 2025 at 9:38 AM, Andy Shevchenko andriy.shevchenko@intel.com wrote:
> > 
> > > On Mon, Mar 24, 2025 at 05:55:40PM -0700, Denis Mukhin wrote:
> 
> > > > During the bring-up of an x86 board, the kernel was crashing before
> > > > reaching the platform's console driver because of a bug in the firmware,
> > > > leaving no trace of the boot progress.
> > > > 
> > > > It was discovered that the only available method to debug the kernel
> > > > boot process was via the platform's MMIO-based UART, as the board lacked
> > > > an I/O port-based UART, PCI UART, or functional video output.
> > > > 
> > > > Then it turned out that earlyprintk= does not have a knob to configure
> > > > the MMIO-mapped UART.
> > > > 
> > > > Extend the early printk facility to support platform MMIO-based UARTs
> > > > on x86 systems, enabling debugging during the system bring-up phase.
> > > > 
> > > > The command line syntax to enable platform MMIO-based UART is:
> > > > earlyprintk=mmio,membase[,{nocfg|baudrate}][,keep]
> > > > 
> > > > Note, the change does not integrate MMIO-based UART support to:
> > > > arch/x86/boot/early_serial_console.c
> > > > 
> > > > Also, update kernel parameters documentation with the new syntax and
> > > > add missing 'nocfg' setting to PCI serial cards description.
> > > 
> > > Just for your information: Have you seen this rather old series of mine?
> > > 
> > > https://bitbucket.org/andy-shev/linux/commits/branch/topic%2Fx86%2Fboot-earlyprintk
> > 
> > This is a nice cleanup!
> > Thanks for sharing.
> > 
> > Sorry, I haven't seen the series above, I had to write a patch (which was months ago).
> > It's just I could not post it on the mailing list until recently.
> 
> 
> No problem. Can you look at
> https://lore.kernel.org/r/20250407172214.792745-1-andriy.shevchenko@linux.intel.com
> ? I forgot to Cc you and that is an important fix.

Done, thanks!

> 
> --
> With Best Regards,
> Andy Shevchenko
[tip: x86/urgent] x86/early_printk: Add support for MMIO-based UARTs
Posted by tip-bot2 for Denis Mukhin 8 months, 3 weeks ago
The following commit has been merged into the x86/urgent branch of tip:

Commit-ID:     3181424aeac2f6596534bf43021a10eae294a9b0
Gitweb:        https://git.kernel.org/tip/3181424aeac2f6596534bf43021a10eae294a9b0
Author:        Denis Mukhin <dmukhin@ford.com>
AuthorDate:    Mon, 24 Mar 2025 17:55:40 -07:00
Committer:     Ingo Molnar <mingo@kernel.org>
CommitterDate: Tue, 25 Mar 2025 08:35:38 +01:00

x86/early_printk: Add support for MMIO-based UARTs

During the bring-up of an x86 board, the kernel was crashing before
reaching the platform's console driver because of a bug in the firmware,
leaving no trace of the boot progress.

The only available method to debug the kernel boot process was via the
platform's MMIO-based UART, as the board lacked an I/O port-based UART,
PCI UART, or functional video output.

Then it turned out that earlyprintk= does not have a knob to configure
the MMIO-mapped UART.

Extend the early printk facility to support platform MMIO-based UARTs
on x86 systems, enabling debugging during the system bring-up phase.

The command line syntax to enable platform MMIO-based UART is:

  earlyprintk=mmio,membase[,{nocfg|baudrate}][,keep]

Note, the change does not integrate MMIO-based UART support to:

  arch/x86/boot/early_serial_console.c

Also, update kernel parameters documentation with the new syntax and
add the missing 'nocfg' setting to the PCI serial cards description.

Signed-off-by: Denis Mukhin <dmukhin@ford.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Link: https://lore.kernel.org/r/20250324-earlyprintk-v3-1-aee7421dc469@ford.com
---
 Documentation/admin-guide/kernel-parameters.txt |  9 ++-
 arch/x86/kernel/early_printk.c                  | 45 +++++++++++++++-
 2 files changed, 52 insertions(+), 2 deletions(-)

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 866427d..649261e 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1407,14 +1407,21 @@
 			earlyprintk=serial[,0x...[,baudrate]]
 			earlyprintk=ttySn[,baudrate]
 			earlyprintk=dbgp[debugController#]
-			earlyprintk=pciserial[,force],bus:device.function[,baudrate]
+			earlyprintk=pciserial[,force],bus:device.function[,{nocfg|baudrate}]
 			earlyprintk=xdbc[xhciController#]
 			earlyprintk=bios
+			earlyprintk=mmio,membase[,{nocfg|baudrate}]
 
 			earlyprintk is useful when the kernel crashes before
 			the normal console is initialized. It is not enabled by
 			default because it has some cosmetic problems.
 
+			Only 32-bit memory addresses are supported for "mmio"
+			and "pciserial" devices.
+
+			Use "nocfg" to skip UART configuration, assume
+			BIOS/firmware has configured UART correctly.
+
 			Append ",keep" to not disable it when the real console
 			takes over.
 
diff --git a/arch/x86/kernel/early_printk.c b/arch/x86/kernel/early_printk.c
index fc1714b..611f27e 100644
--- a/arch/x86/kernel/early_printk.c
+++ b/arch/x86/kernel/early_printk.c
@@ -190,7 +190,6 @@ static __init void early_serial_init(char *s)
 	early_serial_hw_init(divisor);
 }
 
-#ifdef CONFIG_PCI
 static __noendbr void mem32_serial_out(unsigned long addr, int offset, int value)
 {
 	u32 __iomem *vaddr = (u32 __iomem *)addr;
@@ -208,6 +207,45 @@ static __noendbr unsigned int mem32_serial_in(unsigned long addr, int offset)
 ANNOTATE_NOENDBR_SYM(mem32_serial_in);
 
 /*
+ * early_mmio_serial_init() - Initialize MMIO-based early serial console.
+ * @s: MMIO-based serial specification.
+ */
+static __init void early_mmio_serial_init(char *s)
+{
+	unsigned long baudrate;
+	unsigned long membase;
+	char *e;
+
+	if (*s == ',')
+		s++;
+
+	if (!strncmp(s, "0x", 2)) {
+		/* NB: only 32-bit addresses are supported. */
+		membase = simple_strtoul(s, &e, 16);
+		early_serial_base = (unsigned long)early_ioremap(membase, PAGE_SIZE);
+
+		static_call_update(serial_in, mem32_serial_in);
+		static_call_update(serial_out, mem32_serial_out);
+
+		s += strcspn(s, ",");
+		if (*s == ',')
+			s++;
+	}
+
+	if (!strncmp(s, "nocfg", 5)) {
+		baudrate = 0;
+	} else {
+		baudrate = simple_strtoul(s, &e, 0);
+		if (baudrate == 0 || s == e)
+			baudrate = DEFAULT_BAUD;
+	}
+
+	if (baudrate)
+		early_serial_hw_init(115200 / baudrate);
+}
+
+#ifdef CONFIG_PCI
+/*
  * early_pci_serial_init()
  *
  * This function is invoked when the early_printk param starts with "pciserial"
@@ -351,6 +389,11 @@ static int __init setup_early_printk(char *buf)
 	keep = (strstr(buf, "keep") != NULL);
 
 	while (*buf != '\0') {
+		if (!strncmp(buf, "mmio", 4)) {
+			early_mmio_serial_init(buf + 4);
+			early_console_register(&early_serial_console, keep);
+			buf += 4;
+		}
 		if (!strncmp(buf, "serial", 6)) {
 			buf += 6;
 			early_serial_init(buf);