From e4a3671633f2b7783123c5456aa0ab3c27d69cc4 Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Wed, 25 Jun 2025 16:10:56 -0400 Subject: kernel/amd64: i8042: Use DMI for quirk checking On certain embedded controllers (ECs) like the EC present on the Thinkpad T420s, writes to the i8042 controller configuration byte are not very liked by the EC internal logic and thus result in them asserting an NMI as a certain host bus error. To workaround this, Hyra falls back to polling on these specific devices. Previously, Hyra did not have DMI/SMBIOS capabilities and therefore had to rely on assuming *all* Lenovo devices were "hostile". As of now since we are capable grabbing the product version from DMI, we can target specific devices like the T420s and this commit provides that logic. This commit additionally implements logic to enable interrupts on PS/2 port 0 (aka the keyboard). Signed-off-by: Ian Moffett --- sys/arch/amd64/isa/i8042.c | 60 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) (limited to 'sys') diff --git a/sys/arch/amd64/isa/i8042.c b/sys/arch/amd64/isa/i8042.c index b32ff5b..eab0f78 100644 --- a/sys/arch/amd64/isa/i8042.c +++ b/sys/arch/amd64/isa/i8042.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -156,6 +157,45 @@ i8042_write(uint16_t port, uint8_t val) outb(port, val); } +/* + * Read from an i8042 register. + * + * @port: I/O port + */ +static uint8_t +i8042_read(uint16_t port) +{ + i8042_obuf_wait(); + return inb(port); +} + +/* + * Read the i8042 controller configuration + * byte. + */ +static uint8_t +i8042_read_conf(void) +{ + uint8_t conf; + + i8042_write(I8042_CMD, I8042_GET_CONFB); + i8042_obuf_wait(); + conf = i8042_read(I8042_DATA); + return conf; +} + +/* + * Write a new value to the i8042 controller + * configuration byte. + */ +static void +i8042_write_conf(uint8_t conf) +{ + i8042_write(I8042_CMD, I8042_SET_CONFB); + i8042_ibuf_wait(); + i8042_write(I8042_DATA, conf); +} + /* * Send a data to a device * @@ -204,11 +244,22 @@ static void i8042_en_intr(void) { struct intr_hand ih; + uint8_t conf; ih.func = i8042_kb_event; ih.priority = IPL_BIO; ih.irq = KB_IRQ; intr_register("i8042-kb", &ih); + + /* + * Enable the clock of PS/2 port 0 and tell + * the controller that we are accepting + * interrupts. + */ + conf = i8042_read_conf(); + conf &= ~I8042_PORT0_CLK; + conf |= I8042_PORT0_INTR; + i8042_write_conf(conf); } static void @@ -350,6 +401,8 @@ i8042_sync_loop(void) static int i8042_init(void) { + const char *prodver = NULL; + /* Try to request a general purpose timer */ if (req_timer(TIMER_GP, &tmr) != TMRR_SUCCESS) { pr_error("failed to fetch general purpose timer\n"); @@ -378,9 +431,12 @@ i8042_init(void) * etc... As of now, treat the i8042 like a fucking bomb * if this bit is set. */ - if (strcmp(acpi_oemid(), "LENOVO") == 0) { + if ((prodver = dmi_prodver()) == NULL) { + prodver = "None"; + } + if (strcmp(prodver, "ThinkPad T420s") == 0) { quirks |= I8042_HOSTILE; - pr_trace("lenovo device, assuming hostile\n"); + pr_trace("ThinkPad T420s detected, assuming hostile\n"); pr_trace("disabling irq 1, polling as fallback\n"); spawn(&polltd, i8042_sync_loop, NULL, 0, NULL); } -- cgit v1.2.3