diff --git a/Base/usr/share/man/man7/boot_parameters.md b/Base/usr/share/man/man7/boot_parameters.md index 35b5c5d1c17..b04099bedd9 100644 --- a/Base/usr/share/man/man7/boot_parameters.md +++ b/Base/usr/share/man/man7/boot_parameters.md @@ -28,9 +28,7 @@ List of options: during the boot sequence. Leaving only the AHCI and Ram Disk controllers. * **`disable_physical_storage`** - If present on the command line, neither AHCI, or IDE controllers will be initialized on boot. - -* **`disable_ps2_controller`** - If present on the command line, the I8042 keyboard controller will not be initialized on boot. - + * **`disable_uhci_controller`** - If present on the command line, the UHCI controller will not be initialized on boot. * **`disable_virtio`** - If present on the command line, virtio devices will not be detected, and initialized on boot. @@ -61,6 +59,12 @@ has set up before booting the Kernel, don't initialize any driver. * **`init_args`** - This parameter expects a set of arguments to pass to the **`init`** program. The value should be a set of strings separated by `,` characters. +* **`i8042_presence_mode`** - This parameter expects one of the following values: + **`aggressive-test`** - The i8042 initialization sequence should only try an aggressive presence test. + **`auto`** - The i8042 initialization sequence should try to check if ACPI says i8042 exists, and if not an aggressive presence test should take place to determine presence. + **`none`** - Assume there's no i8042 controller in the system. + **`force`** - Assume there's i8042 controller in the system. + * **`panic`** - This parameter expects **`halt`** or **`shutdown`**. This is particularly useful in CI contexts. * **`pci`** - This parameter expects **`ecam`**, **`io`** or **`none`**. When selecting **`none`** diff --git a/Documentation/NetworkBoot.md b/Documentation/NetworkBoot.md index bce3b9eae58..752112d34e4 100644 --- a/Documentation/NetworkBoot.md +++ b/Documentation/NetworkBoot.md @@ -175,7 +175,6 @@ Don't forget to replace `X.Y.Z.W` with your HTTP server IP address. For troubleshooting purposes, you can add the following command line arguments if you suspect our implementation fails to work with your hardware: - `disable_physical_storage` -- `disable_ps2_controller` - `disable_uhci_controller` Because iPXE (unlike GRUB) doesn't support VESA VBE modesetting when booting a multiboot kernel, diff --git a/Kernel/Arch/x86_64/ISABus/I8042Controller.cpp b/Kernel/Arch/x86_64/ISABus/I8042Controller.cpp index 096ba932b03..f4ceeea04e3 100644 --- a/Kernel/Arch/x86_64/ISABus/I8042Controller.cpp +++ b/Kernel/Arch/x86_64/ISABus/I8042Controller.cpp @@ -107,23 +107,12 @@ UNMAP_AFTER_INIT ErrorOr I8042Controller::detect_devices() TRY(do_wait_then_write(I8042Port::Command, I8042Command::ReadConfiguration)); configuration = TRY(do_wait_then_read(I8042Port::Buffer)); - TRY(do_wait_then_write(I8042Port::Command, I8042Command::WriteConfiguration)); configuration &= ~I8042ConfigurationFlag::FirstPS2PortInterrupt; configuration &= ~I8042ConfigurationFlag::SecondPS2PortInterrupt; - - // Note: The default BIOS on the QEMU microvm machine type (qboot) doesn't - // behave like SeaBIOS, which means it doesn't set first port scan code translation. - // However we rely on compatibility feature of the i8042 to send scan codes of set 1. - // To ensure that the controller is always outputting correct scan codes, set it - // to scan code 2 (because SeaBIOS on regular QEMU machine does this for us) and enable - // first port translation to ensure all scan codes are translated to scan code set 1. configuration |= I8042ConfigurationFlag::FirstPS2PortTranslation; - TRY(do_wait_then_write(I8042Port::Buffer, configuration)); - TRY(do_wait_then_write(I8042Port::Buffer, I8042Command::SetScanCodeSet)); - TRY(do_wait_then_write(I8042Port::Buffer, 0x2)); - m_is_dual_channel = (configuration & I8042ConfigurationFlag::SecondPS2PortClock) != 0; - dbgln("I8042: {} channel controller", m_is_dual_channel ? "Dual" : "Single"); + TRY(do_wait_then_write(I8042Port::Command, I8042Command::WriteConfiguration)); + TRY(do_wait_then_write(I8042Port::Buffer, configuration)); // Perform controller self-test TRY(do_wait_then_write(I8042Port::Command, I8042Command::TestPS2Controller)); @@ -134,8 +123,12 @@ UNMAP_AFTER_INIT ErrorOr I8042Controller::detect_devices() TRY(do_wait_then_write(I8042Port::Buffer, configuration)); } else { dbgln("I8042: Controller self test failed"); + return Error::from_errno(EIO); } + m_is_dual_channel = (configuration & I8042ConfigurationFlag::SecondPS2PortClock) != 0; + dbgln("I8042: {} channel controller", m_is_dual_channel ? "Dual" : "Single"); + // Test ports and enable them if available TRY(do_wait_then_write(I8042Port::Command, I8042Command::TestFirstPS2Port)); auto first_port_test_result = TRY(do_wait_then_read(I8042Port::Buffer)); diff --git a/Kernel/CommandLine.cpp b/Kernel/CommandLine.cpp index 3b37f14567e..f234f792501 100644 --- a/Kernel/CommandLine.cpp +++ b/Kernel/CommandLine.cpp @@ -139,6 +139,20 @@ UNMAP_AFTER_INIT bool CommandLine::is_early_boot_console_disabled() const PANIC("Unknown early_boot_console setting: {}", value); } +UNMAP_AFTER_INIT I8042PresenceMode CommandLine::i8042_presence_mode() const +{ + auto value = lookup("i8042_presence_mode"sv).value_or("auto"sv); + if (value == "auto"sv) + return I8042PresenceMode::Automatic; + if (value == "none"sv) + return I8042PresenceMode::None; + if (value == "force"sv) + return I8042PresenceMode::Force; + if (value == "aggressive-test"sv) + return I8042PresenceMode::AggressiveTest; + PANIC("Unknown i8042_presence_mode setting: {}", value); +} + UNMAP_AFTER_INIT bool CommandLine::is_vmmouse_enabled() const { return lookup("vmmouse"sv).value_or("on"sv) == "on"sv; @@ -220,11 +234,6 @@ UNMAP_AFTER_INIT bool CommandLine::is_physical_networking_disabled() const return contains("disable_physical_networking"sv); } -UNMAP_AFTER_INIT bool CommandLine::disable_ps2_controller() const -{ - return contains("disable_ps2_controller"sv); -} - UNMAP_AFTER_INIT bool CommandLine::disable_physical_storage() const { return contains("disable_physical_storage"sv); diff --git a/Kernel/CommandLine.h b/Kernel/CommandLine.h index 5e002ea1d7d..a7c5d0f974c 100644 --- a/Kernel/CommandLine.h +++ b/Kernel/CommandLine.h @@ -24,6 +24,13 @@ enum class HPETMode { NonPeriodic }; +enum class I8042PresenceMode { + Automatic, + AggressiveTest, + Force, + None, +}; + enum class AcpiFeatureLevel { Enabled, Limited, @@ -77,13 +84,13 @@ public: [[nodiscard]] bool is_legacy_time_enabled() const; [[nodiscard]] bool is_pc_speaker_enabled() const; [[nodiscard]] GraphicsSubsystemMode graphics_subsystem_mode() const; + [[nodiscard]] I8042PresenceMode i8042_presence_mode() const; [[nodiscard]] bool is_force_pio() const; [[nodiscard]] AcpiFeatureLevel acpi_feature_level() const; [[nodiscard]] StringView system_mode() const; [[nodiscard]] PanicMode panic_mode(Validate should_validate = Validate::No) const; [[nodiscard]] HPETMode hpet_mode() const; [[nodiscard]] bool disable_physical_storage() const; - [[nodiscard]] bool disable_ps2_controller() const; [[nodiscard]] bool disable_uhci_controller() const; [[nodiscard]] bool disable_usb() const; [[nodiscard]] bool disable_virtio() const; @@ -93,6 +100,7 @@ public: [[nodiscard]] NonnullOwnPtrVector userspace_init_args() const; [[nodiscard]] StringView root_device() const; [[nodiscard]] bool is_nvme_polling_enabled() const; + [[nodiscard]] bool is_i8042_force_scan_code_2() const; [[nodiscard]] size_t switch_to_tty() const; private: diff --git a/Kernel/Devices/HID/HIDManagement.cpp b/Kernel/Devices/HID/HIDManagement.cpp index 56b8d57e478..1ec30195c2a 100644 --- a/Kernel/Devices/HID/HIDManagement.cpp +++ b/Kernel/Devices/HID/HIDManagement.cpp @@ -120,24 +120,39 @@ UNMAP_AFTER_INIT ErrorOr HIDManagement::enumerate() // emulation of the PS/2 controller if it was set by the BIOS. // If ACPI indicates we have an i8042 controller and the USB controller was // set to emulate PS/2, we should not initialize the PS/2 controller. - if (kernel_command_line().disable_ps2_controller()) - return {}; #if ARCH(X86_64) - m_i8042_controller = I8042Controller::initialize(); - - // Note: If ACPI is disabled or doesn't indicate that we have an i8042, we - // still perform a manual existence check via probing, which is relevant on - // QEMU, for example. This probing check is known to not work on bare metal - // in all cases, so if we can get a 'yes' from ACPI, we skip it. auto has_i8042_controller = false; - if (ACPI::Parser::the() && ACPI::Parser::the()->have_8042()) - has_i8042_controller = true; - else if (m_i8042_controller->check_existence_via_probing({})) + auto i8042_controller = I8042Controller::initialize(); + switch (kernel_command_line().i8042_presence_mode()) { + case I8042PresenceMode::Automatic: { + // Note: If ACPI is disabled or doesn't indicate that we have an i8042, we + // still perform a manual existence check via probing, which is relevant on + // QEMU, for example. This probing check is known to not work on bare metal + // in all cases, so if we can get a 'yes' from ACPI, we skip it. + if (ACPI::Parser::the() && ACPI::Parser::the()->have_8042()) + has_i8042_controller = true; + else if (i8042_controller->check_existence_via_probing({})) + has_i8042_controller = true; + break; + } + case I8042PresenceMode::Force: { has_i8042_controller = true; + break; + } + case I8042PresenceMode::None: { + break; + } + case I8042PresenceMode::AggressiveTest: { + if (i8042_controller->check_existence_via_probing({})) + has_i8042_controller = true; + break; + } + } // Note: If we happen to not have i8042 just return "gracefully" for now. if (!has_i8042_controller) return {}; + m_i8042_controller = i8042_controller; TRY(m_i8042_controller->detect_devices()); if (m_i8042_controller->mouse()) m_hid_devices.append(m_i8042_controller->mouse().release_nonnull());