]> xenbits.xensource.com Git - unikraft/unikraft.git/commitdiff
drivers: Add `PS/2` subsystem with a dumbed down PS2 keyboard driver
authorSergiu Moga <sergiu@unikraft.io>
Tue, 16 Jan 2024 20:35:50 +0000 (22:35 +0200)
committerUnikraft Bot <monkey@unikraft.io>
Thu, 6 Mar 2025 10:49:16 +0000 (10:49 +0000)
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 <sergiu@unikraft.io>
Approved-by: Michalis Pappas <michalis@unikraft.io>
Reviewed-by: Michalis Pappas <michalis@unikraft.io>
GitHub-Closes: #1265

drivers/Config.uk
drivers/Makefile.uk
drivers/ukps2/Config.uk [new file with mode: 0644]
drivers/ukps2/Makefile.uk [new file with mode: 0644]
drivers/ukps2/kbd/Config.uk [new file with mode: 0644]
drivers/ukps2/kbd/Makefile.uk [new file with mode: 0644]
drivers/ukps2/kbd/dumbkbd.c [new file with mode: 0644]
lib/ukboot/Config.uk
plat/Config.uk
plat/kvm/Config.uk

index 0c1729b09e2b0fe200d90be2da7f2508a78ebe2e..2a39fa370a348a58552a149b067a37812ea66d1b 100644 (file)
@@ -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
index 109657fa323a655c7ecc07752f47bd1a876f1d58..96099a7e8989e2d350e30a136f97be11a23ee4f9 100644 (file)
@@ -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 (file)
index 0000000..8815c6c
--- /dev/null
@@ -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 (file)
index 0000000..0e81df2
--- /dev/null
@@ -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 (file)
index 0000000..e1c190d
--- /dev/null
@@ -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 (file)
index 0000000..1b68d6c
--- /dev/null
@@ -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 (file)
index 0000000..4ed33f3
--- /dev/null
@@ -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 <uk/bitops.h>
+#include <uk/event.h>
+#include <uk/init.h>
+#include <uk/intctlr.h>
+#include <x86/delay.h>
+
+/* 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);
index e25ca6c097fddebb28ff63525d77b3bedc999514..af7bcbbece3d0c15878881c94bea02df61a47689 100644 (file)
@@ -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
index a9eca87f0b51a87a6ac71351936d6c99702d0955..70c810df1395e9c61146a8c628a1968e0534b651 100644 (file)
@@ -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
index c6640cef3fb88933dd309b6311d0eadab080e842..31f85682e41bbcb694869402dd4a66caf8827ef3 100644 (file)
@@ -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