diff options
author | Ian Moffett <ian@osmora.org> | 2025-06-25 16:10:56 -0400 |
---|---|---|
committer | Ian Moffett <ian@osmora.org> | 2025-06-25 16:21:10 -0400 |
commit | e4a3671633f2b7783123c5456aa0ab3c27d69cc4 (patch) | |
tree | 9680eb02d8473064782fba3676231f511aa1c0a3 | |
parent | 32ff35677e1444727dd12b4e7d93476f62f8135a (diff) |
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 <ian@osmora.org>
-rw-r--r-- | sys/arch/amd64/isa/i8042.c | 60 |
1 files changed, 58 insertions, 2 deletions
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 <dev/acpi/acpi.h> #include <dev/timer.h> #include <dev/cons/cons.h> +#include <dev/dmi/dmi.h> #include <machine/cpu.h> #include <machine/pio.h> #include <machine/isa/i8042var.h> @@ -157,6 +158,45 @@ i8042_write(uint16_t port, uint8_t 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 * * @aux: If true, send to aux device (mouse) @@ -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); } |