summaryrefslogtreecommitdiff
path: root/Kernel
diff options
context:
space:
mode:
authorLiav A <liavalb@gmail.com>2022-12-22 03:46:22 +0200
committerJelle Raaijmakers <jelle@gmta.nl>2023-01-06 11:09:56 +0100
commit0f7cc468b2221d54ea79523922a2f21790c02645 (patch)
treec4925082a1d18b26e3367839a01536daa5792abc /Kernel
parentfc5bcd84764d60ae18a74a3918d3d0ee6d6a913e (diff)
downloadserenity-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.cpp19
-rw-r--r--Kernel/CommandLine.cpp19
-rw-r--r--Kernel/CommandLine.h10
-rw-r--r--Kernel/Devices/HID/HIDManagement.cpp37
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());