From 3181424aeac2f6596534bf43021a10eae294a9b0 Mon Sep 17 00:00:00 2001 From: Denis Mukhin Date: Mon, 24 Mar 2025 17:55:40 -0700 Subject: [PATCH] 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 Signed-off-by: Ingo Molnar Link: https://lore.kernel.org/r/20250324-earlyprintk-v3-1-aee7421dc469@ford.com --- .../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 866427da6add..649261e97628 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 fc1714bad045..611f27e3890c 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); -- 2.25.1