1
0
mirror of https://github.com/SerenityOS/serenity synced 2024-07-09 09:00:46 +00:00

Kernel: Move IO delay code to x86 architecture subdirectory

Many code patterns and hardware procedures rely on reliable delay in the
microseconds granularity, and since they are using such delays which are
valid cases, but should not rely on x86 specific code, we allow to
determine in compile time the proper platform-specific code to use to
invoke such delays.
This commit is contained in:
Liav A 2022-09-02 11:23:32 +03:00 committed by Linus Groh
parent cac72259d0
commit 84fbab6803
19 changed files with 96 additions and 54 deletions

15
Kernel/Arch/Delay.h Normal file
View File

@ -0,0 +1,15 @@
/*
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Types.h>
namespace Kernel {
void microseconds_delay(u32 microseconds);
}

View File

@ -0,0 +1,17 @@
/*
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Arch/Delay.h>
#include <Kernel/Arch/x86/IO.h>
namespace Kernel {
void microseconds_delay(u32 microseconds)
{
IO::delay(microseconds);
}
}

View File

@ -6,6 +6,7 @@
*/
#include <AK/Platform.h>
#include <Kernel/Arch/Delay.h>
#include <Kernel/Bus/PCI/API.h>
#include <Kernel/Bus/USB/UHCI/UHCIController.h>
#include <Kernel/Bus/USB/USBRequest.h>
@ -633,7 +634,7 @@ void UHCIController::reset_port(u8 port)
// Wait at least 50 ms for the port to reset.
// This is T DRSTR in the USB 2.0 Specification Page 186 Table 7-13.
constexpr u16 reset_delay = 50 * 1000;
IO::delay(reset_delay);
microseconds_delay(reset_delay);
port_data &= ~UHCI_PORTSC_PORT_RESET;
if (port == 0)
@ -644,7 +645,7 @@ void UHCIController::reset_port(u8 port)
// Wait 10 ms for the port to recover.
// This is T RSTRCY in the USB 2.0 Specification Page 188 Table 7-14.
constexpr u16 reset_recovery_delay = 10 * 1000;
IO::delay(reset_recovery_delay);
microseconds_delay(reset_recovery_delay);
port_data = port == 0 ? read_portsc1() : read_portsc2();
port_data |= UHCI_PORTSC_PORT_ENABLED;

View File

@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Arch/Delay.h>
#include <Kernel/Arch/x86/IO.h>
#include <Kernel/Bus/USB/USBClasses.h>
#include <Kernel/Bus/USB/USBController.h>
@ -83,7 +84,7 @@ ErrorOr<void> Hub::enumerate_and_power_on_hub()
}
// Wait for the ports to power up. power_on_to_power_good_time is in units of 2 ms and we want in us, so multiply by 2000.
IO::delay(descriptor.power_on_to_power_good_time * 2000);
microseconds_delay(descriptor.power_on_to_power_good_time * 2000);
memcpy(&m_hub_descriptor, &descriptor, sizeof(USBHubDescriptor));
@ -171,7 +172,7 @@ void Hub::check_for_port_updates()
// FIXME: Timeout
while (debounce_timer < debounce_interval) {
IO::delay(debounce_disconnect_check_interval);
microseconds_delay(debounce_disconnect_check_interval);
debounce_timer += debounce_disconnect_check_interval;
if (auto result = get_port_status(port_number, port_status); result.is_error()) {
@ -203,7 +204,7 @@ void Hub::check_for_port_updates()
// Wait at least 10 ms for the port to reset.
// This is T DRST in the USB 2.0 Specification Page 186 Table 7-13.
constexpr u16 reset_delay = 10 * 1000;
IO::delay(reset_delay);
microseconds_delay(reset_delay);
if (auto result = get_port_status(port_number, port_status); result.is_error()) {
dbgln("USB Hub: Error occurred when getting status while resetting port {}: {}.", port_number, result.error());
@ -224,7 +225,7 @@ void Hub::check_for_port_updates()
// Wait 10 ms for the port to recover.
// This is T RSTRCY in the USB 2.0 Specification Page 188 Table 7-14.
constexpr u16 reset_recovery_delay = 10 * 1000;
IO::delay(reset_recovery_delay);
microseconds_delay(reset_recovery_delay);
dbgln_if(USB_DEBUG, "USB Hub: Reset complete!");

View File

@ -333,6 +333,7 @@ if ("${SERENITY_ARCH}" STREQUAL "i686" OR "${SERENITY_ARCH}" STREQUAL "x86_64")
${KERNEL_SOURCES}
Arch/Processor.cpp
Arch/x86/common/Delay.cpp
Arch/x86/common/I8042Reboot.cpp
Arch/x86/common/ScopedCritical.cpp
Arch/x86/common/SmapDisabler.cpp

View File

@ -5,6 +5,7 @@
*/
#include <AK/Format.h>
#include <Kernel/Arch/Delay.h>
#include <Kernel/Devices/Audio/AC97.h>
#include <Kernel/Devices/DeviceManagement.h>
#include <Kernel/Memory/AnonymousVMObject.h>
@ -291,7 +292,7 @@ void AC97::AC97Channel::reset()
control_register.out(AudioControlRegisterFlag::ResetRegisters);
while ((control_register.in<u8>() & AudioControlRegisterFlag::ResetRegisters) > 0)
IO::delay(50);
microseconds_delay(50);
m_dma_running.with([](auto& dma_running) {
dma_running = false;

View File

@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Arch/Delay.h>
#include <Kernel/Arch/x86/IO.h>
#include <Kernel/Devices/HID/I8042Controller.h>
#include <Kernel/Devices/HID/PS2KeyboardDevice.h>
@ -71,7 +72,7 @@ UNMAP_AFTER_INIT bool I8042Controller::check_existence_via_probing(Badge<HIDMana
break;
}
// Note: Wait 500 microseconds in case the controller couldn't respond
IO::delay(500);
microseconds_delay(500);
}
if (!successful_self_test) {
dbgln("I8042: Trying to probe for existence of controller failed");
@ -246,7 +247,7 @@ ErrorOr<void> I8042Controller::drain_output_buffer()
return {};
IO::in8(I8042Port::Buffer);
IO::delay(100);
microseconds_delay(100);
}
return Error::from_errno(EBUSY);
}
@ -332,14 +333,14 @@ ErrorOr<void> I8042Controller::prepare_for_input(HIDDevice::Type device)
for (int attempt = 0; attempt < 1000; attempt++) {
u8 status = IO::in8(I8042Port::Status);
if (!(status & I8042StatusFlag::OutputBuffer)) {
IO::delay(1000);
microseconds_delay(1000);
continue;
}
if (device == HIDDevice::Type::Unknown)
return {};
if ((status & I8042StatusFlag::SecondPS2PortOutputBuffer) == second_port_flag)
return {};
IO::delay(1000);
microseconds_delay(1000);
}
return Error::from_errno(EBUSY);
}
@ -351,7 +352,7 @@ ErrorOr<void> I8042Controller::prepare_for_output()
u8 status = IO::in8(I8042Port::Status);
if (!(status & I8042StatusFlag::InputBuffer))
return {};
IO::delay(1000);
microseconds_delay(1000);
}
return Error::from_errno(EBUSY);
}

View File

@ -5,6 +5,7 @@
*/
#include <AK/Singleton.h>
#include <Kernel/Arch/Delay.h>
#include <Kernel/Arch/x86/IO.h>
#include <Kernel/Bus/PCI/API.h>
#include <Kernel/Bus/PCI/IDs.h>
@ -46,7 +47,7 @@ void GraphicsManagement::disable_vga_emulation_access_permanently()
IO::out8(0x3c4, 1);
u8 sr1 = IO::in8(0x3c5);
IO::out8(0x3c5, sr1 | 1 << 5);
IO::delay(1000);
microseconds_delay(1000);
m_vga_access_is_disabled = true;
}

View File

@ -4,7 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Arch/x86/IO.h>
#include <Kernel/Arch/Delay.h>
#include <Kernel/Bus/PCI/API.h>
#include <Kernel/Debug.h>
#include <Kernel/Devices/DeviceManagement.h>
@ -371,7 +371,7 @@ bool IntelNativeDisplayConnector::gmbus_wait_for(IntelGraphics::GMBusStatus desi
default:
VERIFY_NOT_REACHED();
}
IO::delay(1000);
microseconds_delay(1000);
milliseconds_passed++;
}
}
@ -544,7 +544,7 @@ void IntelNativeDisplayConnector::set_display_timings(Graphics::Modesetting cons
dbgln_if(INTEL_GRAPHICS_DEBUG, "sourceSize - {}, {}", (modesetting.vertical.active - 1), (modesetting.horizontal.active - 1));
write_to_register(IntelGraphics::RegisterIndex::PipeASource, (modesetting.vertical.active - 1) | (modesetting.horizontal.active - 1) << 16);
IO::delay(200);
microseconds_delay(200);
}
bool IntelNativeDisplayConnector::wait_for_enabled_pipe_a(size_t milliseconds_timeout) const
@ -553,7 +553,7 @@ bool IntelNativeDisplayConnector::wait_for_enabled_pipe_a(size_t milliseconds_ti
while (current_time < milliseconds_timeout) {
if (pipe_a_enabled())
return true;
IO::delay(1000);
microseconds_delay(1000);
current_time++;
}
return false;
@ -564,7 +564,7 @@ bool IntelNativeDisplayConnector::wait_for_disabled_pipe_a(size_t milliseconds_t
while (current_time < milliseconds_timeout) {
if (!pipe_a_enabled())
return true;
IO::delay(1000);
microseconds_delay(1000);
current_time++;
}
return false;
@ -576,7 +576,7 @@ bool IntelNativeDisplayConnector::wait_for_disabled_pipe_b(size_t milliseconds_t
while (current_time < milliseconds_timeout) {
if (!pipe_b_enabled())
return true;
IO::delay(1000);
microseconds_delay(1000);
current_time++;
}
return false;
@ -662,14 +662,14 @@ void IntelNativeDisplayConnector::enable_dpll_without_vga(IntelGraphics::PLLSett
set_dpll_registers(settings);
IO::delay(200);
microseconds_delay(200);
write_to_register(IntelGraphics::RegisterIndex::DPLLControlA, (6 << 9) | (settings.p1) << 16 | (1 << 26) | (1 << 28) | (1 << 31));
write_to_register(IntelGraphics::RegisterIndex::DPLLMultiplierA, (dac_multiplier - 1) | ((dac_multiplier - 1) << 8));
// The specification says we should wait (at least) about 150 microseconds
// after enabling the DPLL to allow the clock to stabilize
IO::delay(200);
microseconds_delay(200);
VERIFY(read_from_register(IntelGraphics::RegisterIndex::DPLLControlA) & (1 << 31));
}

View File

@ -8,7 +8,7 @@
#include <AK/Memory.h>
#include <AK/Singleton.h>
#include <AK/Types.h>
#include <Kernel/Arch/x86/IO.h>
#include <Kernel/Arch/Delay.h>
#include <Kernel/Arch/x86/MSR.h>
#include <Kernel/Arch/x86/ProcessorInfo.h>
#include <Kernel/Debug.h>
@ -186,7 +186,7 @@ void APIC::set_siv(u32 offset, u8 interrupt)
void APIC::wait_for_pending_icr()
{
while ((read_register(APIC_REG_ICR_LOW) & APIC_ICR_DELIVERY_PENDING) != 0) {
IO::delay(200);
microseconds_delay(200);
}
}
@ -409,13 +409,13 @@ UNMAP_AFTER_INIT void APIC::do_boot_aps()
// INIT
write_icr({ 0, 0, ICRReg::INIT, ICRReg::Physical, ICRReg::Assert, ICRReg::TriggerMode::Edge, ICRReg::AllExcludingSelf });
IO::delay(10 * 1000);
microseconds_delay(10 * 1000);
for (int i = 0; i < 2; i++) {
// SIPI
write_icr({ 0x08, 0, ICRReg::StartUp, ICRReg::Physical, ICRReg::Assert, ICRReg::TriggerMode::Edge, ICRReg::AllExcludingSelf }); // start execution at P8000
IO::delay(200);
microseconds_delay(200);
}
// Now wait until the ap_cpu_init_pending variable dropped to 0, which means all APs are initialized and no longer need these special mappings
@ -423,7 +423,7 @@ UNMAP_AFTER_INIT void APIC::do_boot_aps()
dbgln_if(APIC_DEBUG, "APIC: Waiting for {} AP(s) to finish initialization...", aps_to_enable);
do {
// Wait a little bit
IO::delay(200);
microseconds_delay(200);
} while (m_apic_ap_count.load(AK::MemoryOrder::memory_order_consume) != aps_to_enable);
}
@ -537,7 +537,7 @@ UNMAP_AFTER_INIT void APIC::init_finished(u32 cpu)
// we don't want APs to trigger IPIs (e.g. through MM) while the BSP
// is unable to process them
while (!m_apic_ap_continue.load(AK::MemoryOrder::memory_order_consume)) {
IO::delay(200);
microseconds_delay(200);
}
dbgln_if(APIC_DEBUG, "APIC: CPU #{} continues, all others are initialized", cpu);

View File

@ -4,8 +4,9 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Format.h>
#include <AK/Time.h>
#include <Kernel/Arch/x86/IO.h>
#include <Kernel/Arch/Delay.h>
#include <Kernel/CMOS.h>
#include <Kernel/RTC.h>
@ -43,7 +44,7 @@ static bool try_to_read_registers(unsigned& year, unsigned& month, unsigned& day
update_in_progress_ended_successfully = true;
break;
}
IO::delay(1000);
microseconds_delay(1000);
time_passed_in_milliseconds++;
}

View File

@ -8,6 +8,7 @@
#include <AK/BuiltinWrappers.h>
#include <AK/OwnPtr.h>
#include <AK/Types.h>
#include <Kernel/Arch/Delay.h>
#include <Kernel/Bus/PCI/API.h>
#include <Kernel/CommandLine.h>
#include <Kernel/Library/LockRefPtr.h>
@ -42,7 +43,7 @@ bool AHCIController::reset()
return false;
if (!(hba().control_regs.ghc & 1))
break;
IO::delay(1000);
microseconds_delay(1000);
retry++;
}
// Note: Turn on AHCI HBA and Global HBA Interrupts.

View File

@ -8,6 +8,7 @@
// please look at Documentation/Kernel/AHCILocking.md
#include <AK/Atomic.h>
#include <Kernel/Arch/Delay.h>
#include <Kernel/Locking/Spinlock.h>
#include <Kernel/Memory/MemoryManager.h>
#include <Kernel/Memory/ScatterGatherList.h>
@ -552,7 +553,7 @@ bool AHCIPort::spin_until_ready() const
size_t spin = 0;
dbgln_if(AHCI_DEBUG, "AHCI Port {}: Spinning until ready.", representative_port_index());
while ((m_port_registers.tfd & (ATA_SR_BSY | ATA_SR_DRQ)) && spin <= 100) {
IO::delay(1000);
microseconds_delay(1000);
spin++;
}
if (spin == 100) {
@ -719,7 +720,7 @@ bool AHCIPort::identify_device()
success = true;
break;
}
IO::delay(1000); // delay with 1 milliseconds
microseconds_delay(1000); // delay with 1 milliseconds
time_elapsed++;
}
@ -739,7 +740,7 @@ void AHCIPort::wait_until_condition_met_or_timeout(size_t delay_in_microseconds,
while (retry < retries) {
if (condition_being_met())
break;
IO::delay(delay_in_microseconds);
microseconds_delay(delay_in_microseconds);
retry++;
}
}
@ -852,7 +853,7 @@ bool AHCIPort::initiate_sata_reset()
full_memory_barrier();
set_interface_state(AHCI::DeviceDetectionInitialization::PerformInterfaceInitializationSequence);
// The AHCI specification says to wait now a 1 millisecond
IO::delay(1000);
microseconds_delay(1000);
full_memory_barrier();
set_interface_state(AHCI::DeviceDetectionInitialization::NoActionRequested);
full_memory_barrier();

View File

@ -4,7 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Arch/x86/IO.h>
#include <Kernel/Arch/Delay.h>
#include <Kernel/Storage/ATA/ATADiskDevice.h>
#include <Kernel/Storage/ATA/ATAPort.h>
#include <Kernel/Storage/ATA/Definitions.h>
@ -499,7 +499,7 @@ ErrorOr<void> ATAPort::execute_polled_command(TransactionDirection direction, LB
break;
}
IO::delay(1000);
microseconds_delay(1000);
milliseconds_elapsed++;
}
if (milliseconds_elapsed > completion_timeout_in_milliseconds) {

View File

@ -7,6 +7,7 @@
#include <AK/ByteBuffer.h>
#include <AK/Singleton.h>
#include <AK/StringView.h>
#include <Kernel/Arch/Delay.h>
#include <Kernel/Arch/x86/IO.h>
#include <Kernel/Bus/PCI/API.h>
#include <Kernel/Memory/MemoryManager.h>
@ -44,13 +45,13 @@ StringView IDEChannel::channel_type_string() const
bool IDEChannel::select_device_and_wait_until_not_busy(DeviceType device_type, size_t milliseconds_timeout)
{
IO::delay(20);
microseconds_delay(20);
u8 slave = device_type == DeviceType::Slave;
m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out<u8>(0xA0 | (slave << 4)); // First, we need to select the drive itself
IO::delay(20);
microseconds_delay(20);
size_t time_elapsed = 0;
while (m_io_group.control_base().in<u8>() & ATA_SR_BSY && time_elapsed <= milliseconds_timeout) {
IO::delay(1000);
microseconds_delay(1000);
time_elapsed++;
}
return time_elapsed <= milliseconds_timeout;
@ -63,10 +64,10 @@ ErrorOr<void> IDEChannel::port_phy_reset()
// reset the channel
u8 device_control = m_io_group.control_base().in<u8>();
// Wait 30 milliseconds
IO::delay(30000);
microseconds_delay(30000);
m_io_group.control_base().out<u8>(device_control | (1 << 2));
// Wait 30 milliseconds
IO::delay(30000);
microseconds_delay(30000);
m_io_group.control_base().out<u8>(device_control);
// Wait up to 30 seconds before failing
if (!select_device_and_wait_until_not_busy(DeviceType::Master, 30000)) {
@ -220,7 +221,7 @@ ErrorOr<void> IDEChannel::wait_if_busy_until_timeout(size_t timeout_in_milliseco
{
size_t time_elapsed = 0;
while (m_io_group.control_base().in<u8>() & ATA_SR_BSY && time_elapsed <= timeout_in_milliseconds) {
IO::delay(1000);
microseconds_delay(1000);
time_elapsed++;
}
if (time_elapsed <= timeout_in_milliseconds)
@ -249,7 +250,7 @@ ErrorOr<void> IDEChannel::load_taskfile_into_registers(ATAPort::TaskFile const&
// Note: Preserve the selected drive, always use LBA addressing
auto driver_register = ((m_io_group.io_base().offset(ATA_REG_HDDEVSEL).in<u8>() & (1 << 4)) | (head | (1 << 5) | (1 << 6)));
m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out<u8>(driver_register);
IO::delay(50);
microseconds_delay(50);
if (lba_mode == LBAMode::FortyEightBit) {
m_io_group.io_base().offset(ATA_REG_SECCOUNT1).out<u8>((task_file.count >> 8) & 0xFF);
@ -272,7 +273,7 @@ ErrorOr<void> IDEChannel::load_taskfile_into_registers(ATAPort::TaskFile const&
auto status = m_io_group.control_base().in<u8>();
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY))
break;
IO::delay(1000);
microseconds_delay(1000);
time_elapsed++;
}
m_io_group.io_base().offset(ATA_REG_COMMAND).out<u8>(task_file.command);
@ -284,9 +285,9 @@ ErrorOr<void> IDEChannel::device_select(size_t device_index)
VERIFY(m_lock.is_locked());
if (device_index > 1)
return Error::from_errno(EINVAL);
IO::delay(20);
microseconds_delay(20);
m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out<u8>(0xA0 | ((device_index) << 4));
IO::delay(20);
microseconds_delay(20);
return {};
}

View File

@ -7,9 +7,8 @@
#include <AK/Format.h>
#include <AK/Types.h>
#include <Kernel/Arch/Delay.h>
#include <Kernel/Arch/SafeMem.h>
#include <Kernel/Arch/x86/IO.h>
#include <Kernel/Arch/x86/Processor.h>
#include <Kernel/Bus/PCI/API.h>
#include <Kernel/CommandLine.h>
#include <Kernel/Devices/Device.h>
@ -77,7 +76,7 @@ bool NVMeController::wait_for_ready(bool expected_ready_bit_value)
u32 expected_rdy = expected_ready_bit_value ? 1 : 0;
while (((m_controller_regs->csts >> CSTS_RDY_BIT) & 0x1) != expected_rdy) {
IO::delay(one_ms_io_delay);
microseconds_delay(one_ms_io_delay);
if (--wait_iterations == 0) {
if (((m_controller_regs->csts >> CSTS_RDY_BIT) & 0x1) != expected_rdy) {

View File

@ -4,7 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Arch/x86/IO.h>
#include <Kernel/Arch/Delay.h>
#include <Kernel/Devices/BlockDevice.h>
#include <Kernel/Storage/NVMe/NVMeDefinitions.h>
#include <Kernel/Storage/NVMe/NVMePollQueue.h>
@ -20,7 +20,7 @@ void NVMePollQueue::submit_sqe(NVMeSubmission& sub)
NVMeQueue::submit_sqe(sub);
SpinlockLocker lock_cq(m_cq_lock);
while (!process_cq()) {
IO::delay(1);
microseconds_delay(1);
}
}

View File

@ -4,7 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Arch/x86/IO.h>
#include <Kernel/Arch/Delay.h>
#include <Kernel/StdLib.h>
#include <Kernel/Storage/NVMe/NVMeController.h>
#include <Kernel/Storage/NVMe/NVMeInterruptQueue.h>
@ -126,7 +126,7 @@ u16 NVMeQueue::submit_sync_sqe(NVMeSubmission& sub)
index = m_qdepth - 1;
}
cqe_cid = m_cqe_array[index].command_id;
IO::delay(1);
microseconds_delay(1);
} while (cid != cqe_cid);
auto status = CQ_STATUS_FIELD(m_cqe_array[m_cq_head].status);

View File

@ -7,6 +7,7 @@
*/
#include <AK/StdLibExtras.h>
#include <Kernel/Arch/Delay.h>
#include <Kernel/CommandLine.h>
#include <Kernel/Debug.h>
#include <Kernel/Devices/DeviceManagement.h>
@ -327,7 +328,7 @@ void VirtualConsole::beep()
if (!kernel_command_line().is_pc_speaker_enabled())
return;
PCSpeaker::tone_on(440);
IO::delay(10000);
microseconds_delay(10000);
PCSpeaker::tone_off();
}