From: Sergiu Moga Date: Tue, 16 Jan 2024 20:35:50 +0000 (+0200) Subject: drivers: Add `PS/2` subsystem with a dumbed down PS2 keyboard driver X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=64cb671ad84d3dbae86d0ddf0fde67ba9eacf311;p=unikraft%2Funikraft.git drivers: Add `PS/2` subsystem with a dumbed down PS2 keyboard driver Add a directory to represent what will be in the future the subsystem for PS/2 controller drivers subsystem. Implement a basic, dumbed down, driver stub for the PS/2 keyboard. Write just enough functionality to register an IRQ handler for the well known default legacy PIC routed PS/2 keyboard IRQ line and be able to process 3 scan codes received in a burst manner: CTRL + ALT + DEL This functionality is bare minimum required to achieve shutdown with Firecracker's `SendCtrlAltDel` command. It may not work on QEMU and is obviously not the way the real driver should be implemented. Additionally, introduce a new invisible config option: `CONFIG_HAVE_SHUTDOWN_DISPATCHER` Now, whenever a component implements the functionality of raising a shutdown event, it must announce it system-wide by selecting this config. That being said, make the keyboard driver do this. (In the future, when we will be able to receive actual keystrokes, we may be able to make this a separate config of the keyboard driver, besides the actual functionality of processing keystrokes). Signed-off-by: Sergiu Moga Approved-by: Michalis Pappas Reviewed-by: Michalis Pappas GitHub-Closes: #1265 --- diff --git a/drivers/Config.uk b/drivers/Config.uk index 0c1729b09..2a39fa370 100644 --- a/drivers/Config.uk +++ b/drivers/Config.uk @@ -6,6 +6,10 @@ menu "Interrupt controller" source "$(shell,$(UK_BASE)/support/build/config-submenu.sh -q -o '$(KCONFIG_DIR)/drivers-intctlr.uk' -r '$(KCONFIG_DRIV_BASE)/ukintctlr' -l '$(KCONFIG_DRIV_BASE)/ukintctlr' -e '$(KCONFIG_EXCLUDEDIRS)')" endmenu +menu "PS/2 controller" +source "$(shell,$(UK_BASE)/support/build/config-submenu.sh -q -o '$(KCONFIG_DIR)/drivers-ukps2.uk' -r '$(KCONFIG_DRIV_BASE)/ukps2' -l '$(KCONFIG_DRIV_BASE)/ukps2' -e '$(KCONFIG_EXCLUDEDIRS)')" +endmenu + menu "Random Number Generator" source "$(shell,$(UK_BASE)/support/build/config-submenu.sh -q -o '$(KCONFIG_DIR)/drivers-random.uk' -r '$(KCONFIG_DRIV_BASE)/ukrandom' -l '$(KCONFIG_DRIV_BASE)/ukrandom' -e '$(KCONFIG_EXCLUDEDIRS)')" endmenu @@ -26,4 +30,5 @@ endmenu config HAVE_IBMPC bool select HAVE_IBMPC_NS16550 + select HAVE_IBMPC_PS2 select HAVE_IBMPC_VGA diff --git a/drivers/Makefile.uk b/drivers/Makefile.uk index 109657fa3..96099a7e8 100644 --- a/drivers/Makefile.uk +++ b/drivers/Makefile.uk @@ -9,6 +9,7 @@ UK_DRIV_BASE := $(CONFIG_UK_BASE)/drivers $(eval $(call import_lib,$(UK_DRIV_BASE)/ukbus)) $(eval $(call import_lib,$(UK_DRIV_BASE)/ukconsole)) $(eval $(call import_lib,$(UK_DRIV_BASE)/ukintctlr)) +$(eval $(call import_lib,$(UK_DRIV_BASE)/ukps2)) $(eval $(call import_lib,$(UK_DRIV_BASE)/ukrandom)) $(eval $(call import_lib,$(UK_DRIV_BASE)/ukrtc)) $(eval $(call import_lib,$(UK_DRIV_BASE)/virtio)) diff --git a/drivers/ukps2/Config.uk b/drivers/ukps2/Config.uk new file mode 100644 index 000000000..8815c6c63 --- /dev/null +++ b/drivers/ukps2/Config.uk @@ -0,0 +1,3 @@ +config HAVE_IBMPC_PS2 + bool + depends on ARCH_X86_64 diff --git a/drivers/ukps2/Makefile.uk b/drivers/ukps2/Makefile.uk new file mode 100644 index 000000000..0e81df29a --- /dev/null +++ b/drivers/ukps2/Makefile.uk @@ -0,0 +1,9 @@ +################################################################################ +# +# Driver registrations +# +################################################################################ + +UK_DRIV_PS2_BASE = $(UK_DRIV_BASE)/ukps2 + +$(eval $(call import_lib,$(UK_DRIV_PS2_BASE)/kbd)) diff --git a/drivers/ukps2/kbd/Config.uk b/drivers/ukps2/kbd/Config.uk new file mode 100644 index 000000000..e1c190d05 --- /dev/null +++ b/drivers/ukps2/kbd/Config.uk @@ -0,0 +1,4 @@ +config LIBUKPS2_KBD + bool "PS/2 Keyboard dumbed down driver" + depends on HAVE_IBMPC_PS2 + select HAVE_SHUTDOWN_DISPATCHER diff --git a/drivers/ukps2/kbd/Makefile.uk b/drivers/ukps2/kbd/Makefile.uk new file mode 100644 index 000000000..1b68d6c67 --- /dev/null +++ b/drivers/ukps2/kbd/Makefile.uk @@ -0,0 +1,8 @@ +$(eval $(call addlib_s,libukps2_kbd,$(CONFIG_LIBUKPS2_KBD))) + +# FIXME: Remove this and make the library self contained +LIBUKPS2_KBD_ASINCLUDES-$(CONFIG_LIBUKPS2_KBD) += -I$(UK_PLAT_COMMON_BASE)/include +LIBUKPS2_KBD_CINCLUDES-$(CONFIG_LIBUKPS2_KBD) += -I$(UK_PLAT_COMMON_BASE)/include +LIBUKPS2_KBD_CXXINCLUDES-$(CONFIG_LIBUKPS2_KBD) += -I$(UK_PLAT_COMMON_BASE)/include + +LIBUKPS2_KBD_SRCS-$(CONFIG_LIBUKPS2_KBD) += $(LIBUKPS2_KBD_BASE)/dumbkbd.c|isr diff --git a/drivers/ukps2/kbd/dumbkbd.c b/drivers/ukps2/kbd/dumbkbd.c new file mode 100644 index 000000000..4ed33f361 --- /dev/null +++ b/drivers/ukps2/kbd/dumbkbd.c @@ -0,0 +1,137 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* Copyright (c) 2025, Unikraft GmbH and The Unikraft Authors. + * Licensed under the BSD-3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + */ + +#include +#include +#include +#include +#include + +/* Default legacy IRQ for the PS/2 keyboard */ +#define PIC1_IRQ_KBD 0x1 + +#define I8042_DATA_REG 0x60 + +#define I8042_STATUS_REG 0x64 +#define I8042_STATUS_RECV_FULL UK_BIT(0) +#define I8042_STATUS_SEND_FULL UK_BIT(1) + +#define I8042_CMD_REG 0x64 +#define I8042_CMD_READ_CFG 0x20 +#define I8042_CMD_WRITE_CFG 0x60 +#define I8042_CMD_DIS_KBD 0xAD +#define I8042_CMD_EN_KBD 0xAE + +#define I8042_CFG_REG_EN_KBD_IRQ UK_BIT(0) +#define I8042_CFG_REG_DIS_KBD_CLK UK_BIT(4) + +/* ACPI scan code set - enough for Firecracker "Send CtrlAltDelete" */ +#define I8042_KEY_CTRL 0x0014 +#define I8042_KEY_ALT 0x0011 +#define I8042_KEY_DEL 0xE071 + +static __u8 lctrl_pressed; +static __u8 lalt_pressed; + +UK_EVENT(UKPLAT_SHUTDOWN_EVENT); + +static int kbd_ps2_irq_handler(void *arg __unused) +{ + /* Read received scan code (ACPI scan code set) */ + __u16 sc; + + /* If not a key pressed, probably another event on first PS/2 port */ + if (!(inb(I8042_STATUS_REG) & I8042_STATUS_RECV_FULL)) + return 0; + + sc = (inb(I8042_DATA_REG) << 8) + inb(I8042_DATA_REG); + + switch (sc) { + /* TODO: This is dumbed down, enough for Firecracker shutdown. In + * reality the scan codes will most likely not conveniently come one + * after another. + */ + case ((I8042_KEY_CTRL << 8) | I8042_KEY_ALT): + lctrl_pressed = 1; + lalt_pressed = 1; + + break; + case I8042_KEY_DEL: + if (!(lctrl_pressed && lalt_pressed)) + break; + + uk_raise_event(UKPLAT_SHUTDOWN_EVENT, (void *)UKPLAT_HALT); + + break; + } + + return 0; +} + +/* Quick, dumbed down, initialization for PS/2 keyboard */ +static int kbd_ps2_probe(struct uk_init_ctx *ictx __unused) +{ + int counter = 0; + __u8 cfg; + int rc; + + /* In Firecracker's words: + * "A i8042 PS/2 controller that emulates just enough to shutdown the + * machine." + * See Firecracker's 80128ea61b30 ("New API action: SendCtrlAltDel"). + */ + + /* PS/2 Keyboard is on first port. Just enable it. */ + outb(I8042_CMD_REG, I8042_CMD_EN_KBD); + + /* Send read current configuration register command */ + outb(I8042_CMD_REG, I8042_CMD_READ_CFG); + + /* Wait for response byte by checking status bit of receive buffer. + * Try for 5 times, but it should work the first time usually. + */ + while (!(inb(I8042_STATUS_REG) & I8042_STATUS_RECV_FULL)) { + if (unlikely(counter >= 5)) { + outb(I8042_CMD_REG, I8042_CMD_DIS_KBD); + uk_pr_err("PS/2 Controller unresponsie\n"); + return -ENODEV; + } + + /* We are advised to wait 50ms if the controller hasn't + * responded yet. + */ + udelay(50); + counter++; + } + + /* Read the configuration register */ + cfg = inb(I8042_DATA_REG); + + /* This shouldn't even be needed in a virtualized environment, e.g. + * on Firecracker the above sending of I8042_CMD_EN_KBD would be enough. + */ + cfg |= I8042_CFG_REG_EN_KBD_IRQ; + cfg &= ~I8042_CFG_REG_DIS_KBD_CLK; + + /* Send write current configuration register command */ + outb(I8042_CMD_REG, I8042_CMD_WRITE_CFG); + + /* Send the new configuration register value */ + outb(I8042_DATA_REG, cfg); + + /* TODO: Legacy wired to Master PIC IRQ 1, with I/O-APIC this is likely + * rewired so check ACPI MADT Interrupt Source Override. + */ + rc = uk_intctlr_irq_register(PIC1_IRQ_KBD, kbd_ps2_irq_handler, NULL); + if (unlikely(rc)) { + uk_pr_err("Failed to register PS/2 Keryboard IRQ\n"); + return rc; + } + + return 0; +} + +uk_early_initcall(kbd_ps2_probe, 0x0); diff --git a/lib/ukboot/Config.uk b/lib/ukboot/Config.uk index e25ca6c09..af7bcbbec 100644 --- a/lib/ukboot/Config.uk +++ b/lib/ukboot/Config.uk @@ -4,6 +4,8 @@ menuconfig LIBUKBOOT select LIBUKDEBUG select LIBUKARGPARSE select HAVE_BOOTENTRY + imply LIBUKBOOT_MAINTHREAD if HAVE_SHUTDOWN_DISPATCHER + imply LIBUKBOOT_SHUTDOWNREQ_HANDLER if HAVE_SHUTDOWN_DISPATCHER default y # FIXME: binary buddy allocator is hard-coded for now @@ -188,6 +190,7 @@ config LIBUKBOOT_MAINTHREAD_NOHALT config LIBUKBOOT_SHUTDOWNREQ_HANDLER bool "Register shutdown request handler" depends on LIBUKBOOT_MAINTHREAD + depends on HAVE_SHUTDOWN_DISPATCHER help This setting enables the init thread to initiate a shutdown if it is requested by a driver on the shutdown_req event queue diff --git a/plat/Config.uk b/plat/Config.uk index a9eca87f0..70c810df1 100644 --- a/plat/Config.uk +++ b/plat/Config.uk @@ -124,3 +124,6 @@ config FPSIMD # only allow a single platform (including external platforms). comment "Warning: Selecting multiple platforms is unsupported!" depends on PLAT_KVM && PLAT_XEN + +config HAVE_SHUTDOWN_DISPATCHER + bool diff --git a/plat/kvm/Config.uk b/plat/kvm/Config.uk index c6640cef3..31f85682e 100644 --- a/plat/kvm/Config.uk +++ b/plat/kvm/Config.uk @@ -111,11 +111,13 @@ config KVM_VMM_FIRECRACKER bool "Firecracker" select KVM_BOOT_PROTO_LXBOOT select HAVE_IBMPC_NS16550 if ARCH_X86_64 + select HAVE_IBMPC_PS2 if ARCH_X86_64 select HAVE_MMIO select VIRTIO_MMIO_LINUX_COMPAT_CMDLINE if LIBVIRTIO_MMIO imply LIBNS16550 if LIBUKCONSOLE imply LIBNS16550_EARLY_CONSOLE if LIBUKCONSOLE imply LIBNS16550_COM1 if LIBUKCONSOLE && ARCH_X86_64 + imply LIBUKPS2_KBD if ARCH_X86_64 imply LIBUKINTCTLR_GICV3 if ARCH_ARM_64 imply LIBUKRTC_PL031 depends on ARCH_X86_64 || ARCH_ARM_64