aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWei Hu <whu@FreeBSD.org>2023-03-14 15:13:46 +0000
committerWei Hu <whu@FreeBSD.org>2023-03-18 07:07:35 +0000
commit927358dd98cb902160093e0dc0bac002d6b43858 (patch)
treef9c51f90b157fe51b0c56d41c0cba022b24df061
parentab3ff87a333ae586e9a2f779ba323eff8e889791 (diff)
downloadsrc-927358dd98cb902160093e0dc0bac002d6b43858.tar.gz
src-927358dd98cb902160093e0dc0bac002d6b43858.zip
amd64 loader: Use efiserialio for Hyper-V booted systems
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)
-rw-r--r--stand/efi/loader/arch/amd64/Makefile.inc1
-rw-r--r--stand/efi/loader/bootinfo.c11
-rw-r--r--stand/efi/loader/conf.c6
-rw-r--r--stand/efi/loader/efiserialio.c43
-rw-r--r--stand/i386/libi386/comconsole.c14
5 files changed, 68 insertions, 7 deletions
diff --git a/stand/efi/loader/arch/amd64/Makefile.inc b/stand/efi/loader/arch/amd64/Makefile.inc
index 0d9e2648cb59..bd89044bd6c7 100644
--- a/stand/efi/loader/arch/amd64/Makefile.inc
+++ b/stand/efi/loader/arch/amd64/Makefile.inc
@@ -5,6 +5,7 @@ SRCS+= amd64_tramp.S \
elf64_freebsd.c \
trap.c \
multiboot2.c \
+ efiserialio.c \
exc.S
.PATH: ${BOOTSRC}/i386/libi386
diff --git a/stand/efi/loader/bootinfo.c b/stand/efi/loader/bootinfo.c
index 939f2cf4c3fe..d79f59343af1 100644
--- a/stand/efi/loader/bootinfo.c
+++ b/stand/efi/loader/bootinfo.c
@@ -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);
diff --git a/stand/efi/loader/conf.c b/stand/efi/loader/conf.c
index 863c9188c72c..051e1a3381d1 100644
--- a/stand/efi/loader/conf.c
+++ b/stand/efi/loader/conf.c
@@ -81,6 +81,9 @@ 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;
@@ -88,6 +91,9 @@ extern struct console spinconsole;
struct console *consoles[] = {
&efi_console,
+#if defined(__amd64__)
+ &eficomconsole,
+#endif
&comconsole,
#if defined(__amd64__) || defined(__i386__)
&nullconsole,
diff --git a/stand/efi/loader/efiserialio.c b/stand/efi/loader/efiserialio.c
index 375e679d2590..5fbc700f6ac2 100644
--- a/stand/efi/loader/efiserialio.c
+++ b/stand/efi/loader/efiserialio.c
@@ -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",
@@ -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;
@@ -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;
@@ -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
@@ -489,6 +514,7 @@ comc_setup(void)
{
EFI_STATUS status;
UINT32 control;
+ char *ev;
/* port is not usable */
if (comc_port->sio == NULL)
@@ -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);
diff --git a/stand/i386/libi386/comconsole.c b/stand/i386/libi386/comconsole.c
index ed1f1aa08ed7..3fbb6a292c19 100644
--- a/stand/i386/libi386/comconsole.c
+++ b/stand/i386/libi386/comconsole.c
@@ -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;
/*