Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 60 additions & 12 deletions kernel/drivers/input/ps2mouse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,14 +345,20 @@ void IrqHandler()
duetos::sched::WaitQueueWakeOne(&g_readers);
}

} // namespace

void Ps2MouseInit()
// Polled 8042 controller + aux-device bring-up (steps 1-5). Split
// out so Ps2MouseInit can run the WHOLE dialogue under a single
// interrupts-disabled window with one clean exit. This matters:
// Ps2MouseInit runs AFTER Ps2KeyboardInit has already unmasked
// IRQ 1, so every non-aux controller response we poll for here
// (the 0xA9 test result, ReadConfigByte's reply, device ACKs)
// also raises the keyboard IRQ — the keyboard ISR then reads
// port 0x60 first and our poll loop spins to its cap forever.
// On QEMU the race window happened not to bite; on VirtualBox
// the controller's IRQ timing makes the steal deterministic,
// which is exactly the "port-2 self-test no response" boot bail.
// Returns true iff the mouse ACKed enable-reporting.
bool Ps2MouseControllerBringup()
{
static constinit bool s_initialised = false;
KASSERT(!s_initialised, "drivers/ps2mouse", "Ps2MouseInit called twice");
s_initialised = true;

// Step 1: enable the aux channel. The keyboard driver's
// ControllerInit disabled it during its bring-up; re-enable
// before anything else.
Expand All @@ -370,13 +376,13 @@ void Ps2MouseInit()
if (!TryWaitOutputFull(&port2_test))
{
core::Log(core::LogLevel::Warn, "drivers/ps2mouse", "port-2 self-test no response (no PS/2 mouse?)");
return;
return false;
}
if (port2_test != kResponseTestPort2Pass)
{
core::LogWithValue(core::LogLevel::Warn, "drivers/ps2mouse", "port-2 self-test failed (no PS/2 mouse?)",
port2_test);
return;
return false;
}

// Step 3: device reset + set defaults. The mouse's "set defaults"
Expand All @@ -387,15 +393,15 @@ void Ps2MouseInit()
if (!MouseSendAndAck(kMouseCmdSetDefaults))
{
core::Log(core::LogLevel::Warn, "drivers/ps2mouse", "set-defaults (0xF6) not ACKed — mouse disabled");
return;
return false;
}

// Step 4: enable data reporting. Without this, the mouse stays
// mute regardless of movement.
if (!MouseSendAndAck(kMouseCmdEnableReporting))
{
core::Log(core::LogLevel::Warn, "drivers/ps2mouse", "enable-reporting (0xF4) not ACKed — mouse disabled");
return;
return false;
}

// Step 5: flip on the aux-channel IRQ + active clock in the
Expand All @@ -404,10 +410,44 @@ void Ps2MouseInit()
config |= kConfigPort2IrqEnable;
config = static_cast<u8>(config & ~kConfigPort2ClockDisable);
WriteConfigByte(config);
return true;
}

} // namespace

void Ps2MouseInit()
{
static constinit bool s_initialised = false;
KASSERT(!s_initialised, "drivers/ps2mouse", "Ps2MouseInit called twice");
s_initialised = true;

// Run steps 1-6 with interrupts disabled. Ps2KeyboardInit has
// already unmasked IRQ 1, so without this the live keyboard ISR
// races every non-aux controller byte we poll for below and the
// mouse never initialises (the VirtualBox "port-2 self-test no
// response" bail). Save/restore IF rather than unconditionally
// STI so a future caller that runs with interrupts already off
// isn't surprised. The whole dialogue is bounded spin-polls +
// register writes — no sleep / block — so a CLI window is safe.
constexpr u64 kRflagsIf = 1ULL << 9;
const bool irqs_were_on = (arch::ReadRflags() & kRflagsIf) != 0;
arch::Cli();

if (!Ps2MouseControllerBringup())
{
if (irqs_were_on)
{
arch::Sti();
}
return;
}

// Step 6: route through the IOAPIC + IDT. Identical shape to
// the keyboard. Note that IRQ 12 may or may not have a MADT
// override on real hardware — IsaIrqToGsi handles that.
// override on real hardware — IsaIrqToGsi handles that. Still
// under CLI: these are IDT / IOAPIC register writes, and the
// mouse IRQ can't be delivered until the route below lands
// anyway.
arch::IdtSetGate(kMouseVector, reinterpret_cast<u64>(&isr_44));
arch::IrqInstall(kMouseVector, &IrqHandler);
const u32 gsi = acpi::IsaIrqToGsi(kMouseIsaIrq);
Expand All @@ -416,6 +456,14 @@ void Ps2MouseInit()

g_available = true;

// Route is live; the polled dialogue is done. Re-enable
// interrupts (if the caller had them on) before the logging
// tail so the CLI window stays as tight as the bug fix needs.
if (irqs_were_on)
{
arch::Sti();
}

duetos::core::LogWithValue(duetos::core::LogLevel::Info, "drivers/ps2mouse", "routed isa_irq", kMouseIsaIrq);
duetos::core::LogWithValue(duetos::core::LogLevel::Info, "drivers/ps2mouse", " gsi", gsi);
duetos::core::LogWithValue(duetos::core::LogLevel::Info, "drivers/ps2mouse", " vector", kMouseVector);
Expand Down
14 changes: 14 additions & 0 deletions kernel/security/login.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ namespace duetos::core
using duetos::drivers::video::ConsoleWrite;
using duetos::drivers::video::ConsoleWriteChar;
using duetos::drivers::video::ConsoleWriteln;
using duetos::drivers::video::FramebufferBeginCompose;
using duetos::drivers::video::FramebufferDrawRect;
using duetos::drivers::video::FramebufferDrawString;
using duetos::drivers::video::FramebufferDropShadow;
using duetos::drivers::video::FramebufferEndCompose;
using duetos::drivers::video::FramebufferFillRect;
using duetos::drivers::video::FramebufferFillRectGradient;
using duetos::drivers::video::FramebufferGet;
Expand Down Expand Up @@ -422,6 +424,14 @@ void DrawField(u32 x, u32 y, u32 w, u32 h, const char* text, u32 len, bool mask,
void GuiRepaint()
{
const GuiLayout l = ComputeLayout();
// Compose the whole panel offscreen so the 1 Hz ui-ticker
// repaint lands in a single blit. Without this the full-screen
// gradient clear is visible on its own for a frame on
// un-coalesced host framebuffers (VBox), reading as a 1 Hz
// flicker. Mirrors DesktopCompose's BeginCompose/EndCompose/
// Present flow; no-op fallback to direct mode if the shadow
// allocator is unavailable.
FramebufferBeginCompose();
DrawBackground(l);
DrawPanel(l);

Expand Down Expand Up @@ -473,6 +483,10 @@ void GuiRepaint()
FramebufferDrawString(16, y_hint, "DEFAULT ACCOUNTS: ADMIN/ADMIN GUEST/(EMPTY)", 0x00C0D0E0, kBgBottom);
}

// Flush the offscreen shadow surface to the live framebuffer in
// one row-by-row copy (no-op if BeginCompose fell back to direct
// mode).
FramebufferEndCompose();
// Push the freshly-painted login surface to the active backend
// (virtio-gpu TRANSFER_TO_HOST_2D + RESOURCE_FLUSH; no-op for
// direct firmware-handoff framebuffers). Without this the
Expand Down
Loading