diff options
author | Liav A <liavalb@gmail.com> | 2022-12-22 03:46:22 +0200 |
---|---|---|
committer | Jelle Raaijmakers <jelle@gmta.nl> | 2023-01-06 11:09:56 +0100 |
commit | 0f7cc468b2221d54ea79523922a2f21790c02645 (patch) | |
tree | c4925082a1d18b26e3367839a01536daa5792abc /Kernel | |
parent | fc5bcd84764d60ae18a74a3918d3d0ee6d6a913e (diff) | |
download | serenity-0f7cc468b2221d54ea79523922a2f21790c02645.zip |
Kernel: Make i8042 controller initialization sequence more robust
The setting of scan code set sequence is removed, as it's buggy and
could lead the controller to fail immediately when doing self-test
afterwards. We will restore it when we understand how to do so safely.
Allow the user to determine a preferred detection path with a new kernel
command line argument. The defualt option is to check i8042 presence
with an ACPI check and if necessary - an "aggressive" test to determine
i8042 existence in the system.
Also, keep the i8042 controller pointer on the stack, so don't assign
m_i8042_controller member pointer if it does not exist.
Diffstat (limited to 'Kernel')
-rw-r--r-- | Kernel/Arch/x86_64/ISABus/I8042Controller.cpp | 19 | ||||
-rw-r--r-- | Kernel/CommandLine.cpp | 19 | ||||
-rw-r--r-- | Kernel/CommandLine.h | 10 | ||||
-rw-r--r-- | Kernel/Devices/HID/HIDManagement.cpp | 37 |
4 files changed, 55 insertions, 30 deletions
diff --git a/Kernel/Arch/x86_64/ISABus/I8042Controller.cpp b/Kernel/Arch/x86_64/ISABus/I8042Controller.cpp index 096ba932b0..f4ceeea04e 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<void> 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<void> 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 3b37f14567..f234f79250 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 5e002ea1d7..a7c5d0f974 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<KString> 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 56b8d57e47..1ec30195c2 100644 --- a/Kernel/Devices/HID/HIDManagement.cpp +++ b/Kernel/Devices/HID/HIDManagement.cpp @@ -120,24 +120,39 @@ UNMAP_AFTER_INIT ErrorOr<void> 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()); |