mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 15:40:19 +00:00
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.
This commit is contained in:
parent
fc5bcd8476
commit
0f7cc468b2
Notes:
sideshowbarker
2024-07-17 08:43:11 +09:00
Author: https://github.com/supercomputer7 Commit: https://github.com/SerenityOS/serenity/commit/0f7cc468b2 Pull-request: https://github.com/SerenityOS/serenity/pull/15346 Reviewed-by: https://github.com/linusg
6 changed files with 62 additions and 34 deletions
|
@ -29,8 +29,6 @@ List of options:
|
||||||
|
|
||||||
* **`disable_physical_storage`** - If present on the command line, neither AHCI, or IDE controllers will be initialized on boot.
|
* **`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_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.
|
* **`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.
|
* **`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.
|
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.
|
* **`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`**
|
* **`pci`** - This parameter expects **`ecam`**, **`io`** or **`none`**. When selecting **`none`**
|
||||||
|
|
|
@ -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:
|
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_physical_storage`
|
||||||
- `disable_ps2_controller`
|
|
||||||
- `disable_uhci_controller`
|
- `disable_uhci_controller`
|
||||||
|
|
||||||
Because iPXE (unlike GRUB) doesn't support VESA VBE modesetting when booting a multiboot kernel,
|
Because iPXE (unlike GRUB) doesn't support VESA VBE modesetting when booting a multiboot kernel,
|
||||||
|
|
|
@ -107,23 +107,12 @@ UNMAP_AFTER_INIT ErrorOr<void> I8042Controller::detect_devices()
|
||||||
|
|
||||||
TRY(do_wait_then_write(I8042Port::Command, I8042Command::ReadConfiguration));
|
TRY(do_wait_then_write(I8042Port::Command, I8042Command::ReadConfiguration));
|
||||||
configuration = TRY(do_wait_then_read(I8042Port::Buffer));
|
configuration = TRY(do_wait_then_read(I8042Port::Buffer));
|
||||||
TRY(do_wait_then_write(I8042Port::Command, I8042Command::WriteConfiguration));
|
|
||||||
configuration &= ~I8042ConfigurationFlag::FirstPS2PortInterrupt;
|
configuration &= ~I8042ConfigurationFlag::FirstPS2PortInterrupt;
|
||||||
configuration &= ~I8042ConfigurationFlag::SecondPS2PortInterrupt;
|
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;
|
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;
|
TRY(do_wait_then_write(I8042Port::Command, I8042Command::WriteConfiguration));
|
||||||
dbgln("I8042: {} channel controller", m_is_dual_channel ? "Dual" : "Single");
|
TRY(do_wait_then_write(I8042Port::Buffer, configuration));
|
||||||
|
|
||||||
// Perform controller self-test
|
// Perform controller self-test
|
||||||
TRY(do_wait_then_write(I8042Port::Command, I8042Command::TestPS2Controller));
|
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));
|
TRY(do_wait_then_write(I8042Port::Buffer, configuration));
|
||||||
} else {
|
} else {
|
||||||
dbgln("I8042: Controller self test failed");
|
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
|
// Test ports and enable them if available
|
||||||
TRY(do_wait_then_write(I8042Port::Command, I8042Command::TestFirstPS2Port));
|
TRY(do_wait_then_write(I8042Port::Command, I8042Command::TestFirstPS2Port));
|
||||||
auto first_port_test_result = TRY(do_wait_then_read(I8042Port::Buffer));
|
auto first_port_test_result = TRY(do_wait_then_read(I8042Port::Buffer));
|
||||||
|
|
|
@ -139,6 +139,20 @@ UNMAP_AFTER_INIT bool CommandLine::is_early_boot_console_disabled() const
|
||||||
PANIC("Unknown early_boot_console setting: {}", value);
|
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
|
UNMAP_AFTER_INIT bool CommandLine::is_vmmouse_enabled() const
|
||||||
{
|
{
|
||||||
return lookup("vmmouse"sv).value_or("on"sv) == "on"sv;
|
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);
|
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
|
UNMAP_AFTER_INIT bool CommandLine::disable_physical_storage() const
|
||||||
{
|
{
|
||||||
return contains("disable_physical_storage"sv);
|
return contains("disable_physical_storage"sv);
|
||||||
|
|
|
@ -24,6 +24,13 @@ enum class HPETMode {
|
||||||
NonPeriodic
|
NonPeriodic
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class I8042PresenceMode {
|
||||||
|
Automatic,
|
||||||
|
AggressiveTest,
|
||||||
|
Force,
|
||||||
|
None,
|
||||||
|
};
|
||||||
|
|
||||||
enum class AcpiFeatureLevel {
|
enum class AcpiFeatureLevel {
|
||||||
Enabled,
|
Enabled,
|
||||||
Limited,
|
Limited,
|
||||||
|
@ -77,13 +84,13 @@ public:
|
||||||
[[nodiscard]] bool is_legacy_time_enabled() const;
|
[[nodiscard]] bool is_legacy_time_enabled() const;
|
||||||
[[nodiscard]] bool is_pc_speaker_enabled() const;
|
[[nodiscard]] bool is_pc_speaker_enabled() const;
|
||||||
[[nodiscard]] GraphicsSubsystemMode graphics_subsystem_mode() const;
|
[[nodiscard]] GraphicsSubsystemMode graphics_subsystem_mode() const;
|
||||||
|
[[nodiscard]] I8042PresenceMode i8042_presence_mode() const;
|
||||||
[[nodiscard]] bool is_force_pio() const;
|
[[nodiscard]] bool is_force_pio() const;
|
||||||
[[nodiscard]] AcpiFeatureLevel acpi_feature_level() const;
|
[[nodiscard]] AcpiFeatureLevel acpi_feature_level() const;
|
||||||
[[nodiscard]] StringView system_mode() const;
|
[[nodiscard]] StringView system_mode() const;
|
||||||
[[nodiscard]] PanicMode panic_mode(Validate should_validate = Validate::No) const;
|
[[nodiscard]] PanicMode panic_mode(Validate should_validate = Validate::No) const;
|
||||||
[[nodiscard]] HPETMode hpet_mode() const;
|
[[nodiscard]] HPETMode hpet_mode() const;
|
||||||
[[nodiscard]] bool disable_physical_storage() const;
|
[[nodiscard]] bool disable_physical_storage() const;
|
||||||
[[nodiscard]] bool disable_ps2_controller() const;
|
|
||||||
[[nodiscard]] bool disable_uhci_controller() const;
|
[[nodiscard]] bool disable_uhci_controller() const;
|
||||||
[[nodiscard]] bool disable_usb() const;
|
[[nodiscard]] bool disable_usb() const;
|
||||||
[[nodiscard]] bool disable_virtio() const;
|
[[nodiscard]] bool disable_virtio() const;
|
||||||
|
@ -93,6 +100,7 @@ public:
|
||||||
[[nodiscard]] NonnullOwnPtrVector<KString> userspace_init_args() const;
|
[[nodiscard]] NonnullOwnPtrVector<KString> userspace_init_args() const;
|
||||||
[[nodiscard]] StringView root_device() const;
|
[[nodiscard]] StringView root_device() const;
|
||||||
[[nodiscard]] bool is_nvme_polling_enabled() const;
|
[[nodiscard]] bool is_nvme_polling_enabled() const;
|
||||||
|
[[nodiscard]] bool is_i8042_force_scan_code_2() const;
|
||||||
[[nodiscard]] size_t switch_to_tty() const;
|
[[nodiscard]] size_t switch_to_tty() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -120,24 +120,39 @@ UNMAP_AFTER_INIT ErrorOr<void> HIDManagement::enumerate()
|
||||||
// emulation of the PS/2 controller if it was set by the BIOS.
|
// 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
|
// 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.
|
// 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)
|
#if ARCH(X86_64)
|
||||||
m_i8042_controller = I8042Controller::initialize();
|
auto has_i8042_controller = false;
|
||||||
|
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
|
// 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
|
// 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
|
// 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.
|
// 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())
|
if (ACPI::Parser::the() && ACPI::Parser::the()->have_8042())
|
||||||
has_i8042_controller = true;
|
has_i8042_controller = true;
|
||||||
else if (m_i8042_controller->check_existence_via_probing({}))
|
else if (i8042_controller->check_existence_via_probing({}))
|
||||||
has_i8042_controller = true;
|
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.
|
// Note: If we happen to not have i8042 just return "gracefully" for now.
|
||||||
if (!has_i8042_controller)
|
if (!has_i8042_controller)
|
||||||
return {};
|
return {};
|
||||||
|
m_i8042_controller = i8042_controller;
|
||||||
TRY(m_i8042_controller->detect_devices());
|
TRY(m_i8042_controller->detect_devices());
|
||||||
if (m_i8042_controller->mouse())
|
if (m_i8042_controller->mouse())
|
||||||
m_hid_devices.append(m_i8042_controller->mouse().release_nonnull());
|
m_hid_devices.append(m_i8042_controller->mouse().release_nonnull());
|
||||||
|
|
Loading…
Reference in a new issue