diff --git a/docs/grub.texi b/docs/grub.texi
index 321da38f0..c69a11391 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -2719,6 +2719,10 @@ you want to use COM2, you must specify @samp{--unit=1} instead. This
command accepts many other options, so please refer to @ref{serial},
for more details.
+Without argument or with @samp{--port=auto}, GRUB will attempt to use
+ACPI when available to auto-detect the default serial port and its
+configuration.
+
The commands @command{terminal_input} (@pxref{terminal_input}) and
@command{terminal_output} (@pxref{terminal_output}) choose which type of
terminal you want to use. In the case above, the terminal will be a
@@ -4172,6 +4176,11 @@ be in the range 5-8 and stop bits must be 1 or 2. Default is 8 data
bits and one stop bit. @var{parity} is one of @samp{no}, @samp{odd},
@samp{even} and defaults to @samp{no}.
+If passed no @var{unit} nor @var{port}, or if @var{port} is set to
+@samp{auto} then GRUB will attempt to use ACPI to automatically detect
+the system default serial port and its configuration. If this information
+is not available, it will default to @var{unit} 0.
+
The serial port is not used as a communication channel unless the
@command{terminal_input} or @command{terminal_output} command is used
(@pxref{terminal_input}, @pxref{terminal_output}).
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index ba967aac8..71093a100 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2053,6 +2053,7 @@ module = {
name = serial;
common = term/serial.c;
x86 = term/ns8250.c;
+ x86 = term/ns8250-spcr.c;
ieee1275 = term/ieee1275/serial.c;
mips_arc = term/arc/serial.c;
efi = term/efi/serial.c;
diff --git a/grub-core/term/ns8250-spcr.c b/grub-core/term/ns8250-spcr.c
new file mode 100644
index 000000000..8885d24b4
--- /dev/null
+++ b/grub-core/term/ns8250-spcr.c
@@ -0,0 +1,87 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2022 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see .
+ */
+
+#if !defined(GRUB_MACHINE_IEEE1275) && !defined(GRUB_MACHINE_QEMU)
+
+#include
+#include
+#include
+#include
+#include
+
+char *
+grub_ns8250_spcr_init (void)
+{
+ struct grub_acpi_spcr *spcr;
+ struct grub_serial_config config;
+
+ spcr = grub_acpi_find_table (GRUB_ACPI_SPCR_SIGNATURE);
+ if (spcr == NULL)
+ return NULL;
+ if (spcr->hdr.revision < 2)
+ return NULL;
+ if (spcr->intf_type != GRUB_ACPI_SPCR_INTF_TYPE_16550 &&
+ spcr->intf_type != GRUB_ACPI_SPCR_INTF_TYPE_16550X)
+ return NULL;
+ /* For now, we only support byte accesses. */
+ if (spcr->base_addr.access_size != GRUB_ACPI_GENADDR_SIZE_BYTE &&
+ spcr->base_addr.access_size != GRUB_ACPI_GENADDR_SIZE_LGCY)
+ return NULL;
+ config.word_len = 8;
+ config.parity = GRUB_SERIAL_PARITY_NONE;
+ config.stop_bits = GRUB_SERIAL_STOP_BITS_1;
+ config.base_clock = UART_DEFAULT_BASE_CLOCK;
+ if (spcr->flow_control & GRUB_ACPI_SPCR_FC_RTSCTS)
+ config.rtscts = 1;
+ else
+ config.rtscts = 0;
+ switch (spcr->baud_rate)
+ {
+ case GRUB_ACPI_SPCR_BAUD_9600:
+ config.speed = 9600;
+ break;
+ case GRUB_ACPI_SPCR_BAUD_19200:
+ config.speed = 19200;
+ break;
+ case GRUB_ACPI_SPCR_BAUD_57600:
+ config.speed = 57600;
+ break;
+ case GRUB_ACPI_SPCR_BAUD_115200:
+ config.speed = 115200;
+ break;
+ case GRUB_ACPI_SPCR_BAUD_CURRENT:
+ default:
+ /*
+ * We don't (yet) have a way to read the currently
+ * configured speed in HW, so let's use a sane default.
+ */
+ config.speed = 115200;
+ break;
+ };
+ switch (spcr->base_addr.space_id)
+ {
+ case GRUB_ACPI_GENADDR_MEM_SPACE:
+ return grub_serial_ns8250_add_mmio (spcr->base_addr.addr, &config);
+ case GRUB_ACPI_GENADDR_IO_SPACE:
+ return grub_serial_ns8250_add_port (spcr->base_addr.addr, &config);
+ default:
+ return NULL;
+ };
+}
+
+#endif
diff --git a/grub-core/term/serial.c b/grub-core/term/serial.c
index cdbbc54e3..8d01977b6 100644
--- a/grub-core/term/serial.c
+++ b/grub-core/term/serial.c
@@ -157,13 +157,27 @@ grub_serial_find (const char *name)
{
name = grub_serial_ns8250_add_port (grub_strtoul (&name[sizeof ("port") - 1],
0, 16), NULL);
- if (!name)
- return NULL;
+ if (name == NULL)
+ return NULL;
FOR_SERIAL_PORTS (port)
- if (grub_strcmp (port->name, name) == 0)
- break;
+ if (grub_strcmp (port->name, name) == 0)
+ break;
}
+
+#if (defined(__i386__) || defined(__x86_64__)) && !defined(GRUB_MACHINE_IEEE1275) && !defined(GRUB_MACHINE_QEMU)
+ if (!port && grub_strcmp (name, "auto") == 0)
+ {
+ /* Look for an SPCR if any. If not, default to com0. */
+ name = grub_ns8250_spcr_init ();
+ if (name == NULL)
+ name = "com0";
+
+ FOR_SERIAL_PORTS (port)
+ if (grub_strcmp (port->name, name) == 0)
+ break;
+ }
+#endif
#endif
#ifdef GRUB_MACHINE_IEEE1275
@@ -210,7 +224,7 @@ grub_cmd_serial (grub_extcmd_context_t ctxt, int argc, char **args)
name = args[0];
if (!name)
- name = "com0";
+ name = "auto";
port = grub_serial_find (name);
if (!port)
diff --git a/include/grub/serial.h b/include/grub/serial.h
index a94327140..8d6ed56a3 100644
--- a/include/grub/serial.h
+++ b/include/grub/serial.h
@@ -185,6 +185,7 @@ grub_serial_config_defaults (struct grub_serial_port *port)
#if defined(__mips__) || defined (__i386__) || defined (__x86_64__)
void grub_ns8250_init (void);
+char *grub_ns8250_spcr_init (void);
char *grub_serial_ns8250_add_port (grub_port_t port, struct grub_serial_config *config);
char *grub_serial_ns8250_add_mmio (grub_addr_t addr, struct grub_serial_config *config);
#endif