uefi stand: Guess the console better

For server machines, ComOut is set to the set of devices that the efi
console suppots. Parse it to see if we have serial, video or both.
Make that take precidence over the command line args. boot1.efi parses
them, but loader.efi doesn't. It's not clear where to read boot.conf
from, so we don't do that. The command line args can still be set via
efibootmgr, which is more inline with the UEFI boot manager to replace
that. These args are typically used only to set serial vs video and
the com speed line. We can infer that from ComOut, so do so.
Remember the com speed and hw.uart.console to match.

RelNotes: yes
Sponsored by: Netflix
Differential Revision: https://reviews.freebsd.org/D15917
This commit is contained in:
Warner Losh 2018-07-14 00:40:38 +00:00
parent 2e08e39ff5
commit 3fecfbebe9
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=336270
2 changed files with 171 additions and 17 deletions

View File

@ -67,10 +67,11 @@ extern EFI_SYSTEM_TABLE *ST;
static int
bi_getboothowto(char *kargs)
{
const char *sw;
const char *sw, *tmp;
char *opts;
char *console;
int howto;
int howto, speed, port;
char buf[50];
howto = boot_parse_cmdline(kargs);
howto |= boot_env_to_howto();
@ -81,6 +82,35 @@ bi_getboothowto(char *kargs)
howto |= RB_SERIAL;
if (strcmp(console, "nullconsole") == 0)
howto |= RB_MUTE;
if (strcmp(console, "efi") == 0) {
/*
* If we found a com port and com speed, we need to tell
* the kernel where the serial port is, and how
* fast. Ideally, we'd get the port from ACPI, but that
* isn't running in the loader. Do the next best thing
* by allowing it to be set by a loader.conf variable,
* either a EFI specific one, or the compatible
* comconsole_port if not. PCI support is needed, but
* for that we'd ideally refactor the
* libi386/comconsole.c code to have identical behavior.
*/
tmp = getenv("efi_com_speed");
if (tmp != NULL) {
speed = strtol(tmp, NULL, 0);
tmp = getenv("efi_com_port");
if (tmp == NULL)
tmp = getenv("comconsole_port");
/* XXX fallback to EFI variable set in rc.d? */
if (tmp != NULL)
port = strtol(tmp, NULL, 0);
else
port = 0x3f8;
snprintf(buf, sizeof(buf), "io:%d,br:%d", port,
speed);
env_setenv("hw.uart.console", EV_VOLATILE, buf,
NULL, NULL);
}
}
}
return (howto);

View File

@ -400,8 +400,8 @@ interactive_interrupt(const char *msg)
return (false);
}
int
parse_args(int argc, CHAR16 *argv[], bool has_kbd)
static int
parse_args(int argc, CHAR16 *argv[])
{
int i, j, howto;
bool vargood;
@ -429,12 +429,98 @@ parse_args(int argc, CHAR16 *argv[], bool has_kbd)
return (howto);
}
/*
* Parse ConOut (the list of consoles active) and see if we can find a
* serial port and/or a video port. It would be nice to also walk the
* ACPI name space to map the UID for the serial port to a port. The
* latter is especially hard.
*/
static int
parse_uefi_con_out(void)
{
int how, rv;
int vid_seen = 0, com_seen = 0, seen = 0;
size_t sz;
char buf[4096], *ep;
EFI_DEVICE_PATH *node;
ACPI_HID_DEVICE_PATH *acpi;
UART_DEVICE_PATH *uart;
bool pci_pending;
how = 0;
sz = sizeof(buf);
rv = efi_global_getenv("ConOut", buf, &sz);
if (rv != EFI_SUCCESS)
goto out;
ep = buf + sz;
node = (EFI_DEVICE_PATH *)buf;
while ((char *)node < ep && !IsDevicePathEndType(node)) {
pci_pending = false;
if (DevicePathType(node) == ACPI_DEVICE_PATH &&
DevicePathSubType(node) == ACPI_DP) {
/* Check for Serial node */
acpi = (void *)node;
if (EISA_ID_TO_NUM(acpi->HID) == 0x501)
com_seen = ++seen;
} else if (DevicePathType(node) == MESSAGING_DEVICE_PATH &&
DevicePathSubType(node) == MSG_UART_DP) {
char bd[16];
uart = (void *)node;
snprintf(bd, sizeof(bd), "%d", uart->BaudRate);
setenv("efi_com_speed", bd, 1);
printf("Console speed is %d\n", uart->BaudRate);
} else if (DevicePathType(node) == ACPI_DEVICE_PATH &&
DevicePathSubType(node) == ACPI_ADR_DP) {
/* Check for AcpiAdr() Node for video */
vid_seen = ++seen;
} else if (DevicePathType(node) == HARDWARE_DEVICE_PATH &&
DevicePathSubType(node) == HW_PCI_DP) {
/*
* Note, vmware fusion has a funky console device
* PciRoot(0x0)/Pci(0xf,0x0)
* which we can only detect at the end since we also
* have to cope with:
* PciRoot(0x0)/Pci(0x1f,0x0)/Serial(0x1)
* so only match it if it's last.
*/
pci_pending = true;
}
node = NextDevicePathNode(node); /* Skip the end node */
}
if (pci_pending && vid_seen == 0)
vid_seen = ++seen;
/*
* Truth table for RB_MULTIPLE | RB_SERIAL
* Value Result
* 0 Use only video console
* RB_SERIAL Use only serial console
* RB_MULTIPLE Use both video and serial console
* (but video is primary so gets rc messages)
* both Use both video and serial console
* (but serial is primary so gets rc messages)
*
* Try to honor this as best we can. If only one of serial / video
* found, then use that. Otherwise, use the first one we found.
* This also implies if we found nothing, default to video.
*/
how = 0;
if (vid_seen && com_seen) {
how |= RB_MULTIPLE;
if (com_seen < vid_seen)
how |= RB_SERIAL;
} else if (com_seen)
how |= RB_SERIAL;
out:
return (how);
}
EFI_STATUS
main(int argc, CHAR16 *argv[])
{
EFI_GUID *guid;
int howto, i;
int howto, i, uhowto;
UINTN k;
bool has_kbd;
char *s;
@ -481,23 +567,61 @@ main(int argc, CHAR16 *argv[])
*/
bcache_init(32768, 512);
howto = parse_args(argc, argv, has_kbd);
howto = parse_args(argc, argv);
if (!has_kbd && (howto & RB_PROBE))
howto |= RB_SERIAL | RB_MULTIPLE;
howto &= ~RB_PROBE;
boot_howto_to_env(howto);
uhowto = parse_uefi_con_out();
/*
* XXX we need fallback to this stuff after looking at the ConIn, ConOut and ConErr variables
* We now have two notions of console. howto should be viewed as
* overrides.
*/
if (howto & RB_MULTIPLE) {
if (howto & RB_SERIAL)
setenv("console", "comconsole efi" , 1);
else
setenv("console", "efi comconsole" , 1);
} else if (howto & RB_SERIAL) {
setenv("console", "comconsole" , 1);
} else
setenv("console", "efi", 1);
#define VIDEO_ONLY 0
#define SERIAL_ONLY RB_SERIAL
#define VID_SER_BOTH RB_MULTIPLE
#define SER_VID_BOTH (RB_SERIAL | RB_MULTIPLE)
#define CON_MASK (RB_SERIAL | RB_MULTIPLE)
if ((howto & CON_MASK) == 0) {
/* No override, uhowto is controlling and efi cons is perfect */
howto = howto | (uhowto & CON_MASK);
setenv("console", "efi", 1);
} else if ((howto & CON_MASK) == (uhowto & CON_MASK)) {
/* override matches what UEFI told us, efi console is perfect */
setenv("console", "efi", 1);
} else if ((uhowto & (CON_MASK)) != 0) {
/*
* We detected a serial console on ConOut. All possible
* overrides include serial. We can't really override what efi
* gives us, so we use it knowing it's the best choice.
*/
setenv("console", "efi", 1);
} else {
/*
* We detected some kind of serial in the override, but ConOut
* has no serial, so we have to sort out which case it really is.
*/
switch (howto & CON_MASK) {
case SERIAL_ONLY:
setenv("console", "comconsole", 1);
break;
case VID_SER_BOTH:
setenv("console", "efi comconsole", 1);
break;
case SER_VID_BOTH:
setenv("console", "comconsole efi", 1);
break;
/* case VIDEO_ONLY can't happen -- it's the first if above */
}
}
/*
* howto is set now how we want to export the flags to the kernel, so
* set the env based on it.
*/
boot_howto_to_env(howto);
if (efi_copy_init()) {
printf("failed to allocate staging area\n");
return (EFI_BUFFER_TOO_SMALL);