Skip to content

Commit

Permalink
amd64 loader: Use efiserialio for Hyper-V booted systems
Browse files Browse the repository at this point in the history
UEFI provides ConIn/ConOut handles for consoles that it supports,
which include the text-video and serial ports. When the serial port
is available, use the UEFI driver instead of direct io-port accesses
to avoid conflicts between the firmware and direct hardware access, as
happens on Hyper-V (Azure) setups.

This change enables efiserialio to be built for efi-amd64 and has
higher order priority vs comconsole, and only uses efiserialio
if the hypervisor is Hyper-V. When efiserialio successfully
probes, it will set efi_comconsole_avail=true which will prevent
comconsole from probing in this setup.

Tested on Hyper-V, ESXi and Azure VMs.

PR:		264267
Reviewed by:	kevans, whu
Tested by:	whu
Obtained from:	Rubicon Communications, LLC (Netgate)
MFC after:	2 weeks
Sponsored by:	Rubicon Communications, LLC (Netgate)
  • Loading branch information
Wei Hu authored and Wei Hu committed Mar 18, 2023
1 parent ab3ff87 commit 927358d
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 7 deletions.
1 change: 1 addition & 0 deletions stand/efi/loader/arch/amd64/Makefile.inc
Expand Up @@ -5,6 +5,7 @@ SRCS+= amd64_tramp.S \
elf64_freebsd.c \
trap.c \
multiboot2.c \
efiserialio.c \
exc.S

.PATH: ${BOOTSRC}/i386/libi386
Expand Down
11 changes: 9 additions & 2 deletions stand/efi/loader/bootinfo.c
Expand Up @@ -119,10 +119,17 @@ bi_getboothowto(char *kargs)
if (tmp != NULL)
speed = strtol(tmp, NULL, 0);
tmp = getenv("efi_com_port");
if (tmp == NULL)
tmp = getenv("comconsole_port");
if (tmp != NULL)
port = strtol(tmp, NULL, 0);
if (port <= 0) {
tmp = getenv("comconsole_port");
if (tmp != NULL)
port = strtol(tmp, NULL, 0);
else {
if (port == 0)
port = 0x3f8;
}
}
if (speed != -1 && port != -1) {
snprintf(buf, sizeof(buf), "io:%d,br:%d", port,
speed);
Expand Down
6 changes: 6 additions & 0 deletions stand/efi/loader/conf.c
Expand Up @@ -81,13 +81,19 @@ struct netif_driver *netif_drivers[] = {

extern struct console efi_console;
extern struct console comconsole;
#if defined(__amd64__)
extern struct console eficomconsole;
#endif
#if defined(__amd64__) || defined(__i386__)
extern struct console nullconsole;
extern struct console spinconsole;
#endif

struct console *consoles[] = {
&efi_console,
#if defined(__amd64__)
&eficomconsole,
#endif
&comconsole,
#if defined(__amd64__) || defined(__i386__)
&nullconsole,
Expand Down
43 changes: 38 additions & 5 deletions stand/efi/loader/efiserialio.c
Expand Up @@ -69,6 +69,11 @@ static int comc_speed_set(struct env_var *, int, const void *);

static struct serial *comc_port;
extern struct console efi_console;
bool efi_comconsole_avail = false;

#if defined(__amd64__)
#define comconsole eficomconsole
#endif

struct console comconsole = {
.c_name = "comconsole",
Expand Down Expand Up @@ -254,11 +259,22 @@ comc_probe(struct console *sc)
char *env, *buf, *ep;
size_t sz;

#if defined(__amd64__)
/*
* For x86-64, don't use this driver if not running in Hyper-V.
*/
env = getenv("smbios.bios.version");
if (env == NULL || strncmp(env, "Hyper-V", 7) != 0) {
return;
}
#endif

if (comc_port == NULL) {
comc_port = calloc(1, sizeof (struct serial));
if (comc_port == NULL)
return;
}

/* Use defaults from firmware */
comc_port->databits = 8;
comc_port->parity = DefaultParity;
Expand Down Expand Up @@ -308,6 +324,10 @@ comc_probe(struct console *sc)
comc_port_set, env_nounset);

env = getenv("efi_com_speed");
if (env == NULL)
/* fallback to comconsole setting */
env = getenv("comconsole_speed");

if (comc_parse_intval(env, &val) == CMD_OK)
comc_port->baudrate = val;

Expand All @@ -318,8 +338,13 @@ comc_probe(struct console *sc)
comc_speed_set, env_nounset);

comconsole.c_flags = 0;
if (comc_setup())
if (comc_setup()) {
sc->c_flags = C_PRESENTIN | C_PRESENTOUT;
efi_comconsole_avail = true;
} else {
/* disable being seen as "comconsole" */
comconsole.c_name = "efiserialio";
}
}

static int
Expand Down Expand Up @@ -489,6 +514,7 @@ comc_setup(void)
{
EFI_STATUS status;
UINT32 control;
char *ev;

/* port is not usable */
if (comc_port->sio == NULL)
Expand All @@ -498,10 +524,17 @@ comc_setup(void)
if (EFI_ERROR(status))
return (false);

status = comc_port->sio->SetAttributes(comc_port->sio,
comc_port->baudrate, comc_port->receivefifodepth,
comc_port->timeout, comc_port->parity,
comc_port->databits, comc_port->stopbits);
ev = getenv("smbios.bios.version");
if (ev != NULL && strncmp(ev, "Hyper-V", 7) == 0) {
status = comc_port->sio->SetAttributes(comc_port->sio,
0, 0, 0, DefaultParity, 0, DefaultStopBits);
} else {
status = comc_port->sio->SetAttributes(comc_port->sio,
comc_port->baudrate, comc_port->receivefifodepth,
comc_port->timeout, comc_port->parity,
comc_port->databits, comc_port->stopbits);
}

if (EFI_ERROR(status))
return (false);

Expand Down
14 changes: 14 additions & 0 deletions stand/i386/libi386/comconsole.c
Expand Up @@ -85,6 +85,20 @@ comc_probe(struct console *cp)
int speed, port;
uint32_t locator;

#if defined(__amd64__)
extern bool efi_comconsole_avail;

if (efi_comconsole_avail) {
/*
* If EFI provides serial I/O, then don't use this legacy
* com driver to avoid conflicts with the firmware's driver.
* Change c_name so that it cannot be found in the lookup.
*/
comconsole.c_name = "xcomconsole";
return;
}
#endif

if (comc_curspeed == 0) {
comc_curspeed = COMSPEED;
/*
Expand Down

0 comments on commit 927358d

Please sign in to comment.