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