Kernel/Storage: Remove ATA IDE support

Nobody uses this functionality. I used this code on my old 2007 ICH7
test machine about a year ago, but bare metal is a small aspect of the
project, so it's safe to assume that nobody really tests this piece of
code.
Therefore, let's drop this for good and focus on more modern hardware.
This commit is contained in:
Liav A. 2024-04-29 20:57:16 +03:00 committed by Andrew Kaster
parent f474d0c316
commit 2cb86c1309
33 changed files with 314 additions and 2029 deletions

View file

@ -24,9 +24,6 @@ List of options:
* **`boot_prof`** - If present on the command line, global system profiling will be enabled
as soon as possible during the boot sequence. Allowing you to profile startup of all applications.
* **`disable_ide`** - If present on the command line, the IDE controller will not be initialized
during the boot sequence. Leaving only the AHCI and Ram Disk controllers.
* **`disable_physical_storage`** - If present on the command line, neither AHCI, or IDE controllers will be initialized on boot.
* **`disable_ps2_mouse`** - If present on the command line, no PS2 mouse will be attached.
@ -50,8 +47,6 @@ List of options:
* **`graphics_subsystem_mode`** - This parameter expects one of the following values. **`on`**- Boot into the graphical environment if possible (default). **`off`** - Boot into text mode, don't initialize any driver. **`limited`** - Boot into the pre-defined framebuffer that the bootloader
has set up before booting the Kernel, don't initialize any driver.
* **`force_pio`** - If present on the command line, the IDE controllers will be force into PIO mode when initialized IDE Channels on boot.
* **`hpet`** - This parameter expects one of the following values. **`periodic`** - The High Precision Event Timer should
be configured in a periodic mode. **`nonperiodic`** - The High Precision Event Timer should eb configure din non-periodic mode.

View file

@ -454,7 +454,7 @@ void init_stage2(void*)
for (auto* init_function = driver_init_table_start; init_function != driver_init_table_end; init_function++)
(*init_function)();
StorageManagement::the().initialize(kernel_command_line().is_force_pio(), kernel_command_line().is_nvme_polling_enabled());
StorageManagement::the().initialize(kernel_command_line().is_nvme_polling_enabled());
for (int i = 0; i < 5; ++i) {
if (StorageManagement::the().determine_boot_device(kernel_command_line().root_device()))
break;

View file

@ -1,56 +0,0 @@
/*
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/OwnPtr.h>
#include <AK/Types.h>
#include <Kernel/Arch/x86_64/ISABus/IDEController.h>
#include <Kernel/Bus/PCI/API.h>
#include <Kernel/Devices/Storage/ATA/ATADiskDevice.h>
#include <Kernel/Devices/Storage/ATA/GenericIDE/Channel.h>
#include <Kernel/Library/LockRefPtr.h>
#include <Kernel/Sections.h>
namespace Kernel {
UNMAP_AFTER_INIT ErrorOr<NonnullRefPtr<ISAIDEController>> ISAIDEController::initialize()
{
auto controller = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) ISAIDEController()));
TRY(controller->initialize_channels());
return controller;
}
UNMAP_AFTER_INIT ISAIDEController::ISAIDEController()
{
}
UNMAP_AFTER_INIT ErrorOr<void> ISAIDEController::initialize_channels()
{
auto primary_base_io_window = TRY(IOWindow::create_for_io_space(IOAddress(0x1F0), 8));
auto primary_control_io_window = TRY(IOWindow::create_for_io_space(IOAddress(0x3F6), 4));
auto secondary_base_io_window = TRY(IOWindow::create_for_io_space(IOAddress(0x170), 8));
auto secondary_control_io_window = TRY(IOWindow::create_for_io_space(IOAddress(0x376), 4));
auto initialize_and_enumerate = [](IDEChannel& channel) -> ErrorOr<void> {
TRY(channel.allocate_resources_for_isa_ide_controller({}));
TRY(channel.detect_connected_devices());
return {};
};
auto primary_channel_io_window_group = IDEChannel::IOWindowGroup { move(primary_base_io_window), move(primary_control_io_window) };
auto secondary_channel_io_window_group = IDEChannel::IOWindowGroup { move(secondary_base_io_window), move(secondary_control_io_window) };
m_channels[0] = TRY(IDEChannel::create(*this, move(primary_channel_io_window_group), IDEChannel::ChannelType::Primary));
TRY(initialize_and_enumerate(*m_channels[0]));
m_channels[0]->enable_irq();
m_channels[1] = TRY(IDEChannel::create(*this, move(secondary_channel_io_window_group), IDEChannel::ChannelType::Secondary));
TRY(initialize_and_enumerate(*m_channels[1]));
m_channels[1]->enable_irq();
dbgln("ISA IDE controller detected and initialized");
return {};
}
}

View file

@ -1,28 +0,0 @@
/*
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/OwnPtr.h>
#include <AK/Types.h>
#include <Kernel/Devices/Storage/ATA/GenericIDE/Controller.h>
#include <Kernel/Devices/Storage/StorageDevice.h>
#include <Kernel/Library/LockRefPtr.h>
namespace Kernel {
class AsyncBlockDeviceRequest;
class ISAIDEController final : public IDEController {
public:
static ErrorOr<NonnullRefPtr<ISAIDEController>> initialize();
private:
ISAIDEController();
ErrorOr<void> initialize_channels();
};
}

View file

@ -1,167 +0,0 @@
/*
* Copyright (c) 2020-2022, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/OwnPtr.h>
#include <AK/Types.h>
#include <Kernel/Arch/x86_64/PCI/IDELegacyModeController.h>
#include <Kernel/Bus/PCI/API.h>
#include <Kernel/Devices/Storage/ATA/ATADiskDevice.h>
#include <Kernel/Devices/Storage/ATA/GenericIDE/Channel.h>
#include <Kernel/Library/LockRefPtr.h>
#include <Kernel/Sections.h>
namespace Kernel {
UNMAP_AFTER_INIT ErrorOr<NonnullRefPtr<PCIIDELegacyModeController>> PCIIDELegacyModeController::initialize(PCI::DeviceIdentifier const& device_identifier, bool force_pio)
{
auto controller = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) PCIIDELegacyModeController(device_identifier)));
PCI::enable_io_space(device_identifier);
PCI::enable_memory_space(device_identifier);
PCI::enable_bus_mastering(device_identifier);
ArmedScopeGuard disable_interrupts_on_failure([&] {
controller->disable_pin_based_interrupts();
});
controller->enable_pin_based_interrupts();
TRY(controller->initialize_and_enumerate_channels(force_pio));
disable_interrupts_on_failure.disarm();
return controller;
}
UNMAP_AFTER_INIT PCIIDELegacyModeController::PCIIDELegacyModeController(PCI::DeviceIdentifier const& device_identifier)
: PCI::Device(const_cast<PCI::DeviceIdentifier&>(device_identifier))
, m_prog_if(device_identifier.prog_if())
, m_interrupt_line(device_identifier.interrupt_line())
{
}
bool PCIIDELegacyModeController::is_pci_native_mode_enabled() const
{
return (m_prog_if.value() & 0x05) != 0;
}
bool PCIIDELegacyModeController::is_pci_native_mode_enabled_on_primary_channel() const
{
return (m_prog_if.value() & 0x1) == 0x1;
}
bool PCIIDELegacyModeController::is_pci_native_mode_enabled_on_secondary_channel() const
{
return (m_prog_if.value() & 0x4) == 0x4;
}
bool PCIIDELegacyModeController::is_bus_master_capable() const
{
return m_prog_if.value() & (1 << 7);
}
static char const* detect_controller_type(u8 programming_value)
{
switch (programming_value) {
case 0x00:
return "ISA Compatibility mode-only controller";
case 0x05:
return "PCI native mode-only controller";
case 0x0A:
return "ISA Compatibility mode controller, supports both channels switched to PCI native mode";
case 0x0F:
return "PCI native mode controller, supports both channels switched to ISA compatibility mode";
case 0x80:
return "ISA Compatibility mode-only controller, supports bus mastering";
case 0x85:
return "PCI native mode-only controller, supports bus mastering";
case 0x8A:
return "ISA Compatibility mode controller, supports both channels switched to PCI native mode, supports bus mastering";
case 0x8F:
return "PCI native mode controller, supports both channels switched to ISA compatibility mode, supports bus mastering";
default:
VERIFY_NOT_REACHED();
}
VERIFY_NOT_REACHED();
}
UNMAP_AFTER_INIT ErrorOr<void> PCIIDELegacyModeController::initialize_and_enumerate_channels(bool force_pio)
{
dbgln("IDE controller @ {}: interrupt line was set to {}", device_identifier().address(), m_interrupt_line.value());
dbgln("IDE controller @ {}: {}", device_identifier().address(), detect_controller_type(m_prog_if.value()));
{
auto bus_master_base = IOAddress(PCI::get_BAR4(device_identifier()) & (~1));
dbgln("IDE controller @ {}: bus master base was set to {}", device_identifier().address(), bus_master_base);
}
auto initialize_and_enumerate = [&force_pio](IDEChannel& channel) -> ErrorOr<void> {
TRY(channel.allocate_resources_for_pci_ide_controller({}, force_pio));
TRY(channel.detect_connected_devices());
return {};
};
if (!is_bus_master_capable())
force_pio = true;
OwnPtr<IOWindow> primary_base_io_window;
OwnPtr<IOWindow> primary_control_io_window;
if (!is_pci_native_mode_enabled_on_primary_channel()) {
primary_base_io_window = TRY(IOWindow::create_for_io_space(IOAddress(0x1F0), 8));
primary_control_io_window = TRY(IOWindow::create_for_io_space(IOAddress(0x3F6), 4));
} else {
primary_base_io_window = TRY(IOWindow::create_for_pci_device_bar(device_identifier(), PCI::HeaderType0BaseRegister::BAR0));
auto pci_primary_control_io_window = TRY(IOWindow::create_for_pci_device_bar(device_identifier(), PCI::HeaderType0BaseRegister::BAR1));
// Note: the PCI IDE specification says we should access the IO address with an offset of 2
// on native PCI IDE controllers.
primary_control_io_window = TRY(pci_primary_control_io_window->create_from_io_window_with_offset(2, 4));
}
VERIFY(primary_base_io_window);
VERIFY(primary_control_io_window);
OwnPtr<IOWindow> secondary_base_io_window;
OwnPtr<IOWindow> secondary_control_io_window;
if (!is_pci_native_mode_enabled_on_primary_channel()) {
secondary_base_io_window = TRY(IOWindow::create_for_io_space(IOAddress(0x170), 8));
secondary_control_io_window = TRY(IOWindow::create_for_io_space(IOAddress(0x376), 4));
} else {
secondary_base_io_window = TRY(IOWindow::create_for_pci_device_bar(device_identifier(), PCI::HeaderType0BaseRegister::BAR2));
auto pci_secondary_control_io_window = TRY(IOWindow::create_for_pci_device_bar(device_identifier(), PCI::HeaderType0BaseRegister::BAR3));
// Note: the PCI IDE specification says we should access the IO address with an offset of 2
// on native PCI IDE controllers.
secondary_control_io_window = TRY(pci_secondary_control_io_window->create_from_io_window_with_offset(2, 4));
}
VERIFY(secondary_base_io_window);
VERIFY(secondary_control_io_window);
auto primary_bus_master_io = TRY(IOWindow::create_for_pci_device_bar(device_identifier(), PCI::HeaderType0BaseRegister::BAR4, 16));
auto secondary_bus_master_io = TRY(primary_bus_master_io->create_from_io_window_with_offset(8));
// FIXME: On IOAPIC based system, this value might be completely wrong
// On QEMU for example, it should be "u8 irq_line = 22;" to actually work.
auto irq_line = m_interrupt_line.value();
if (is_pci_native_mode_enabled()) {
VERIFY(irq_line != 0);
}
auto primary_channel_io_window_group = IDEChannel::IOWindowGroup { primary_base_io_window.release_nonnull(), primary_control_io_window.release_nonnull(), move(primary_bus_master_io) };
auto secondary_channel_io_window_group = IDEChannel::IOWindowGroup { secondary_base_io_window.release_nonnull(), secondary_control_io_window.release_nonnull(), move(secondary_bus_master_io) };
if (is_pci_native_mode_enabled_on_primary_channel()) {
m_channels[0] = TRY(IDEChannel::create(*this, irq_line, move(primary_channel_io_window_group), IDEChannel::ChannelType::Primary));
} else {
m_channels[0] = TRY(IDEChannel::create(*this, move(primary_channel_io_window_group), IDEChannel::ChannelType::Primary));
}
TRY(initialize_and_enumerate(*m_channels[0]));
m_channels[0]->enable_irq();
if (is_pci_native_mode_enabled_on_secondary_channel()) {
m_channels[1] = TRY(IDEChannel::create(*this, irq_line, move(secondary_channel_io_window_group), IDEChannel::ChannelType::Secondary));
} else {
m_channels[1] = TRY(IDEChannel::create(*this, move(secondary_channel_io_window_group), IDEChannel::ChannelType::Secondary));
}
TRY(initialize_and_enumerate(*m_channels[1]));
m_channels[1]->enable_irq();
return {};
}
}

View file

@ -1,40 +0,0 @@
/*
* Copyright (c) 2020-2022, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/OwnPtr.h>
#include <AK/Types.h>
#include <Kernel/Devices/Storage/ATA/GenericIDE/Controller.h>
#include <Kernel/Devices/Storage/StorageDevice.h>
#include <Kernel/Library/LockRefPtr.h>
namespace Kernel {
class AsyncBlockDeviceRequest;
class PCIIDELegacyModeController final : public IDEController
, public PCI::Device {
public:
static ErrorOr<NonnullRefPtr<PCIIDELegacyModeController>> initialize(PCI::DeviceIdentifier const&, bool force_pio);
virtual StringView device_name() const override { return "PCIIDELegacyModeController"sv; }
bool is_bus_master_capable() const;
bool is_pci_native_mode_enabled() const;
private:
bool is_pci_native_mode_enabled_on_primary_channel() const;
bool is_pci_native_mode_enabled_on_secondary_channel() const;
explicit PCIIDELegacyModeController(PCI::DeviceIdentifier const&);
ErrorOr<void> initialize_and_enumerate_channels(bool force_pio);
// FIXME: Find a better way to get the ProgrammingInterface
PCI::ProgrammingInterface m_prog_if;
PCI::InterruptLine m_interrupt_line;
};
}

View file

@ -92,11 +92,6 @@ UNMAP_AFTER_INIT bool CommandLine::is_boot_profiling_enabled() const
return contains("boot_prof"sv);
}
UNMAP_AFTER_INIT bool CommandLine::is_ide_enabled() const
{
return !contains("disable_ide"sv);
}
UNMAP_AFTER_INIT bool CommandLine::is_smp_enabled() const
{
// Note: We can't enable SMP mode without enabling the IOAPIC.
@ -195,11 +190,6 @@ bool CommandLine::is_pc_speaker_enabled() const
PANIC("Unknown pcspeaker setting: {}", value);
}
UNMAP_AFTER_INIT bool CommandLine::is_force_pio() const
{
return contains("force_pio"sv);
}
UNMAP_AFTER_INIT StringView CommandLine::root_device() const
{
return lookup("root"sv).value_or("lun0:0:0"sv);

View file

@ -72,7 +72,6 @@ public:
[[nodiscard]] bool contains(StringView key) const;
[[nodiscard]] bool is_boot_profiling_enabled() const;
[[nodiscard]] bool is_ide_enabled() const;
[[nodiscard]] bool is_ioapic_enabled() const;
[[nodiscard]] bool is_smp_enabled_without_ioapic_enabled() const;
[[nodiscard]] bool is_smp_enabled() const;
@ -85,7 +84,6 @@ public:
[[nodiscard]] bool i8042_enable_first_port_translation() 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;

View file

@ -113,15 +113,11 @@ set(KERNEL_SOURCES
Devices/GPU/VirtIO/GPU3DDevice.cpp
Devices/GPU/VirtIO/GraphicsAdapter.cpp
Devices/Loop/LoopDevice.cpp
Devices/Storage/ATA/AHCI/Controller.cpp
Devices/Storage/ATA/AHCI/Port.cpp
Devices/Storage/ATA/AHCI/InterruptHandler.cpp
Devices/Storage/ATA/GenericIDE/Controller.cpp
Devices/Storage/ATA/GenericIDE/Channel.cpp
Devices/Storage/ATA/ATAController.cpp
Devices/Storage/ATA/ATADevice.cpp
Devices/Storage/ATA/ATADiskDevice.cpp
Devices/Storage/ATA/ATAPort.cpp
Devices/Storage/AHCI/ATADevice.cpp
Devices/Storage/AHCI/ATADiskDevice.cpp
Devices/Storage/AHCI/Controller.cpp
Devices/Storage/AHCI/InterruptHandler.cpp
Devices/Storage/AHCI/Port.cpp
Devices/Storage/NVMe/NVMeController.cpp
Devices/Storage/NVMe/NVMeNameSpace.cpp
Devices/Storage/NVMe/NVMeInterruptQueue.cpp
@ -419,10 +415,8 @@ if ("${SERENITY_ARCH}" STREQUAL "x86_64")
Arch/x86_64/ISABus/HID/VMWareMouseDevice.cpp
Arch/x86_64/ISABus/I8042Controller.cpp
Arch/x86_64/ISABus/IDEController.cpp
Arch/x86_64/ISABus/SerialDevice.cpp
Arch/x86_64/PCI/Controller/HostBridge.cpp
Arch/x86_64/PCI/IDELegacyModeController.cpp
Arch/x86_64/PCI/Initializer.cpp
Arch/x86_64/PCI/MSI.cpp

View file

@ -5,18 +5,18 @@
*/
#include <AK/StringView.h>
#include <Kernel/Devices/Storage/ATA/ATADevice.h>
#include <Kernel/Devices/Storage/AHCI/ATADevice.h>
#include <Kernel/Devices/Storage/StorageManagement.h>
#include <Kernel/Sections.h>
namespace Kernel {
static StorageDevice::LUNAddress convert_ata_address_to_lun_address(ATAController const& controller, ATADevice::Address ata_address)
static StorageDevice::LUNAddress convert_ata_address_to_lun_address(AHCIController const& controller, ATA::Address ata_address)
{
return StorageDevice::LUNAddress { controller.controller_id(), ata_address.port, ata_address.subport };
}
ATADevice::ATADevice(ATAController const& controller, ATADevice::Address ata_address, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block)
ATADevice::ATADevice(AHCIController const& controller, ATA::Address ata_address, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block)
: StorageDevice(convert_ata_address_to_lun_address(controller, ata_address), controller.hardware_relative_controller_id(), logical_sector_size, max_addressable_block)
, m_controller(controller)
, m_ata_address(ata_address)
@ -28,9 +28,8 @@ ATADevice::~ATADevice() = default;
void ATADevice::start_request(AsyncBlockDeviceRequest& request)
{
auto controller = m_controller.strong_ref();
VERIFY(controller);
controller->start_request(*this, request);
VERIFY(m_controller);
m_controller->start_request(m_ata_address, request);
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <Kernel/Devices/Storage/AHCI/Controller.h>
#include <Kernel/Devices/Storage/StorageDevice.h>
#include <Kernel/Interrupts/IRQHandler.h>
#include <Kernel/Locking/Mutex.h>
namespace Kernel {
class ATADevice : public StorageDevice {
public:
virtual ~ATADevice() override;
// ^BlockDevice
virtual void start_request(AsyncBlockDeviceRequest&) override;
u16 ata_capabilites() const { return m_capabilities; }
ATA::Address const& ata_address() const { return m_ata_address; }
protected:
ATADevice(AHCIController const&, ATA::Address, u16, u16, u64);
// FIXME: Add proper locking to ensure hotplug can work.
LockRefPtr<AHCIController> m_controller;
ATA::Address const m_ata_address;
u16 const m_capabilities;
};
}

View file

@ -6,13 +6,13 @@
#include <AK/StringView.h>
#include <Kernel/Devices/DeviceManagement.h>
#include <Kernel/Devices/Storage/ATA/ATADiskDevice.h>
#include <Kernel/Devices/Storage/AHCI/ATADiskDevice.h>
#include <Kernel/Devices/Storage/StorageManagement.h>
#include <Kernel/Sections.h>
namespace Kernel {
NonnullLockRefPtr<ATADiskDevice> ATADiskDevice::create(ATAController const& controller, ATADevice::Address ata_address, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block)
NonnullLockRefPtr<ATADiskDevice> ATADiskDevice::create(AHCIController const& controller, ATA::Address ata_address, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block)
{
auto disk_device_or_error = DeviceManagement::try_create_device<ATADiskDevice>(controller, ata_address, capabilities, logical_sector_size, max_addressable_block);
// FIXME: Find a way to propagate errors
@ -20,7 +20,7 @@ NonnullLockRefPtr<ATADiskDevice> ATADiskDevice::create(ATAController const& cont
return disk_device_or_error.release_value();
}
ATADiskDevice::ATADiskDevice(ATAController const& controller, ATADevice::Address ata_address, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block)
ATADiskDevice::ATADiskDevice(AHCIController const& controller, ATA::Address ata_address, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block)
: ATADevice(controller, ata_address, capabilities, logical_sector_size, max_addressable_block)
{
}

View file

@ -6,7 +6,7 @@
#pragma once
#include <Kernel/Devices/Storage/ATA/ATADevice.h>
#include <Kernel/Devices/Storage/AHCI/ATADevice.h>
#include <Kernel/Interrupts/IRQHandler.h>
#include <Kernel/Locking/Mutex.h>
@ -18,14 +18,14 @@ class ATADiskDevice final : public ATADevice {
friend class DeviceManagement;
public:
static NonnullLockRefPtr<ATADiskDevice> create(ATAController const&, ATADevice::Address, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block);
static NonnullLockRefPtr<ATADiskDevice> create(AHCIController const&, ATA::Address, u16 capabilities, u16 logical_sector_size, u64 max_addressable_block);
virtual ~ATADiskDevice() override;
// ^StorageDevice
virtual CommandSet command_set() const override { return CommandSet::ATA; }
private:
ATADiskDevice(ATAController const&, Address, u16, u16, u64);
ATADiskDevice(AHCIController const&, ATA::Address, u16, u16, u64);
// ^DiskDevice
virtual StringView class_name() const override;

View file

@ -12,8 +12,9 @@
#include <Kernel/Boot/CommandLine.h>
#include <Kernel/Bus/PCI/API.h>
#include <Kernel/Bus/PCI/BarMapping.h>
#include <Kernel/Devices/Storage/ATA/AHCI/Controller.h>
#include <Kernel/Devices/Storage/ATA/AHCI/InterruptHandler.h>
#include <Kernel/Devices/Storage/AHCI/Controller.h>
#include <Kernel/Devices/Storage/AHCI/InterruptHandler.h>
#include <Kernel/Devices/Storage/StorageManagement.h>
#include <Kernel/Library/LockRefPtr.h>
#include <Kernel/Memory/MemoryManager.h>
@ -87,9 +88,9 @@ size_t AHCIController::devices_count() const
return count;
}
void AHCIController::start_request(ATADevice const& device, AsyncBlockDeviceRequest& request)
void AHCIController::start_request(ATA::Address address, AsyncBlockDeviceRequest& request)
{
auto port = m_ports[device.ata_address().port];
auto port = m_ports[address.port];
VERIFY(port);
port->start_request(request);
}
@ -111,7 +112,7 @@ volatile AHCI::HBA& AHCIController::hba() const
}
UNMAP_AFTER_INIT AHCIController::AHCIController(PCI::DeviceIdentifier const& pci_device_identifier)
: ATAController()
: StorageController(StorageManagement::generate_relative_ahci_controller_id({}))
, PCI::Device(const_cast<PCI::DeviceIdentifier&>(pci_device_identifier))
{
}

View file

@ -8,8 +8,8 @@
#include <AK/OwnPtr.h>
#include <AK/Types.h>
#include <Kernel/Devices/Storage/ATA/AHCI/Definitions.h>
#include <Kernel/Devices/Storage/ATA/ATAController.h>
#include <Kernel/Devices/Storage/AHCI/Definitions.h>
#include <Kernel/Devices/Storage/StorageController.h>
#include <Kernel/Devices/Storage/StorageDevice.h>
#include <Kernel/Library/LockRefPtr.h>
#include <Kernel/Memory/TypedMapping.h>
@ -20,7 +20,7 @@ namespace Kernel {
class AsyncBlockDeviceRequest;
class AHCIInterruptHandler;
class AHCIPort;
class AHCIController final : public ATAController
class AHCIController final : public StorageController
, public PCI::Device {
friend class AHCIInterruptHandler;
@ -34,9 +34,10 @@ public:
virtual ErrorOr<void> reset() override;
virtual ErrorOr<void> shutdown() override;
virtual size_t devices_count() const override;
virtual void start_request(ATADevice const&, AsyncBlockDeviceRequest&) override;
virtual void complete_current_request(AsyncDeviceRequest::RequestResult) override;
void start_request(ATA::Address, AsyncBlockDeviceRequest&);
void handle_interrupt_for_port(Badge<AHCIInterruptHandler>, u32 port_index) const;
private:

View file

@ -1,4 +1,5 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
@ -8,6 +9,7 @@
#include <AK/StdLibExtraDetails.h>
#include <AK/Types.h>
#include <AK/Vector.h>
namespace Kernel::FIS {
@ -445,3 +447,218 @@ struct [[gnu::packed]] CommandTable {
static_assert(AssertSize<CommandTable, 0x80>());
}
namespace Kernel::ATA {
// NOTE: For SATA drives (AHCI driven HBAs), a port can be a number from 0 to 31,
// and subport can be a number from 0 to 14 (only 15 devices are allowed to
// be connected to one SATA port multiplier).
struct Address {
// FIXME: u32 for this value is wasteful, because even AHCI only support 32 ports
u32 port;
u8 subport;
};
enum DeviceSignature : u32 {
ATA = 0x00000101,
ATAPI = 0xEB140101,
EnclosureManagementBridge = 0xC33C0101,
PortMultiplier = 0x96690101,
Unconnected = 0xFFFFFFFF
};
}
#define ATA_SR_BSY 0x80
#define ATA_SR_DRDY 0x40
#define ATA_SR_DF 0x20
#define ATA_SR_DSC 0x10
#define ATA_SR_DRQ 0x08
#define ATA_SR_CORR 0x04
#define ATA_SR_IDX 0x02
#define ATA_SR_ERR 0x01
#define ATA_ER_BBK 0x80
#define ATA_ER_UNC 0x40
#define ATA_ER_MC 0x20
#define ATA_ER_IDNF 0x10
#define ATA_ER_MCR 0x08
#define ATA_ER_ABRT 0x04
#define ATA_ER_TK0NF 0x02
#define ATA_ER_AMNF 0x01
#define ATA_CMD_READ_PIO 0x20
#define ATA_CMD_READ_PIO_EXT 0x24
#define ATA_CMD_READ_DMA 0xC8
#define ATA_CMD_READ_DMA_EXT 0x25
#define ATA_CMD_WRITE_PIO 0x30
#define ATA_CMD_WRITE_PIO_EXT 0x34
#define ATA_CMD_WRITE_DMA 0xCA
#define ATA_CMD_WRITE_DMA_EXT 0x35
#define ATA_CMD_CACHE_FLUSH 0xE7
#define ATA_CMD_CACHE_FLUSH_EXT 0xEA
#define ATA_CMD_PACKET 0xA0
#define ATA_CMD_IDENTIFY_PACKET 0xA1
#define ATA_CMD_IDENTIFY 0xEC
#define ATAPI_CMD_READ 0xA8
#define ATAPI_CMD_EJECT 0x1B
#define ATA_IDENT_DEVICETYPE 0
#define ATA_IDENT_CYLINDERS 2
#define ATA_IDENT_HEADS 6
#define ATA_IDENT_SECTORS 12
#define ATA_IDENT_SERIAL 20
#define ATA_IDENT_MODEL 54
#define ATA_IDENT_CAPABILITIES 98
#define ATA_IDENT_FIELDVALID 106
#define ATA_IDENT_MAX_LBA 120
#define ATA_IDENT_COMMANDSETS 164
#define ATA_IDENT_MAX_LBA_EXT 200
#define ATA_USE_LBA_ADDRESSING (1 << 6)
#define IDE_ATA 0x00
#define IDE_ATAPI 0x01
#define ATA_REG_DATA 0x00
#define ATA_REG_ERROR 0x01
#define ATA_REG_FEATURES 0x01
#define ATA_REG_SECCOUNT0 0x02
#define ATA_REG_LBA0 0x03
#define ATA_REG_LBA1 0x04
#define ATA_REG_LBA2 0x05
#define ATA_REG_HDDEVSEL 0x06
#define ATA_REG_COMMAND 0x07
#define ATA_REG_STATUS 0x07
#define ATA_REG_SECCOUNT1 0x08
#define ATA_REG_LBA3 0x09
#define ATA_REG_LBA4 0x0A
#define ATA_REG_LBA5 0x0B
#define ATA_CTL_CONTROL 0x00
#define ATA_CTL_ALTSTATUS 0x00
#define ATA_CTL_DEVADDRESS 0x01
#define ATA_CAP_LBA 0x200
namespace Kernel {
struct [[gnu::packed]] ATAIdentifyBlock {
u16 general_configuration;
u16 obsolete;
u16 specific_configuration;
u16 obsolete2;
u16 retired[2];
u16 obsolete3;
u16 reserved_for_cfa[2];
u16 retired2;
u16 serial_number[10];
u16 retired3[2];
u16 obsolete4;
u16 firmware_revision[4];
u16 model_number[20];
u16 maximum_logical_sectors_per_drq;
u16 trusted_computing_features;
u16 capabilities[2];
u16 obsolete5[2];
u16 validity_flags;
u16 obsolete6[5];
u16 security_features;
u32 max_28_bit_addressable_logical_sector;
u16 obsolete7;
u16 dma_modes;
u16 pio_modes;
u16 minimum_multiword_dma_transfer_cycle;
u16 recommended_multiword_dma_transfer_cycle;
u16 minimum_multiword_pio_transfer_cycle_without_flow_control;
u16 minimum_multiword_pio_transfer_cycle_with_flow_control;
u16 additional_supported;
u16 reserved3[5];
u16 queue_depth;
u16 serial_ata_capabilities;
u16 serial_ata_additional_capabilities;
u16 serial_ata_features_supported;
u16 serial_ata_features_enabled;
u16 major_version_number;
u16 minor_version_number;
u16 commands_and_feature_sets_supported[3];
u16 commands_and_feature_sets_supported_or_enabled[3];
u16 ultra_dma_modes;
u16 timing_for_security_features[2];
u16 apm_level;
u16 master_password_id;
u16 hardware_reset_results;
u16 obsolete8;
u16 stream_minimum_request_time;
u16 streaming_transfer_time_for_dma;
u16 streaming_access_latency;
u16 streaming_performance_granularity[2];
u64 user_addressable_logical_sectors_count;
u16 streaming_transfer_time_for_pio;
u16 max_512_byte_blocks_per_data_set_management_command;
u16 physical_sector_size_to_logical_sector_size;
u16 inter_seek_delay_for_acoustic_testing;
u16 world_wide_name[4];
u16 reserved4[4];
u16 obsolete9;
u32 logical_sector_size;
u16 commands_and_feature_sets_supported2;
u16 commands_and_feature_sets_supported_or_enabled2;
u16 reserved_for_expanded_supported_and_enabled_settings[6];
u16 obsolete10;
u16 security_status;
u16 vendor_specific[31];
u16 reserved_for_cfa2[8];
u16 device_nominal_form_factor;
u16 data_set_management_command_support;
u16 additional_product_id[4];
u16 reserved5[2];
u16 current_media_serial_number[30];
u16 sct_command_transport;
u16 reserved6[2];
u16 logical_sectors_alignment_within_physical_sector;
u32 write_read_verify_sector_mode_3_count;
u32 write_read_verify_sector_mode_2_count;
u16 obsolete11[3];
u16 nominal_media_rotation_rate;
u16 reserved7;
u16 obsolete12;
u16 write_read_verify_feature_set_current_mode;
u16 reserved8;
u16 transport_major_version_number;
u16 transport_minor_version_number;
u16 reserved9[6];
u64 extended_user_addressable_logical_sectors_count;
u16 minimum_512_byte_data_blocks_per_download_microcode_operation;
u16 max_512_byte_data_blocks_per_download_microcode_operation;
u16 reserved10[19];
u16 integrity;
};
};

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Devices/Storage/ATA/AHCI/InterruptHandler.h>
#include <Kernel/Devices/Storage/AHCI/InterruptHandler.h>
namespace Kernel {

View file

@ -7,8 +7,8 @@
#pragma once
#include <Kernel/Devices/Device.h>
#include <Kernel/Devices/Storage/ATA/AHCI/Controller.h>
#include <Kernel/Devices/Storage/ATA/AHCI/Port.h>
#include <Kernel/Devices/Storage/AHCI/Controller.h>
#include <Kernel/Devices/Storage/AHCI/Port.h>
#include <Kernel/Devices/Storage/StorageDevice.h>
#include <Kernel/Interrupts/PCIIRQHandler.h>
#include <Kernel/Library/LockRefPtr.h>

View file

@ -9,9 +9,8 @@
#include <AK/Atomic.h>
#include <Kernel/Arch/Delay.h>
#include <Kernel/Devices/Storage/ATA/AHCI/Port.h>
#include <Kernel/Devices/Storage/ATA/ATADiskDevice.h>
#include <Kernel/Devices/Storage/ATA/Definitions.h>
#include <Kernel/Devices/Storage/AHCI/ATADiskDevice.h>
#include <Kernel/Devices/Storage/AHCI/Port.h>
#include <Kernel/Devices/Storage/StorageManagement.h>
#include <Kernel/Locking/Spinlock.h>
#include <Kernel/Memory/MemoryManager.h>
@ -181,14 +180,9 @@ void AHCIPort::recover_from_fatal_error()
{
MutexLocker locker(m_lock);
SpinlockLocker lock(m_hard_lock);
LockRefPtr<AHCIController> controller = m_parent_controller.strong_ref();
if (!controller) {
dmesgln("AHCI Port {}: fatal error, controller not available", representative_port_index());
return;
}
dmesgln("{}: AHCI Port {} fatal error, shutting down!", controller->device_identifier().address(), representative_port_index());
dmesgln("{}: AHCI Port {} fatal error, SError {}", controller->device_identifier().address(), representative_port_index(), (u32)m_port_registers.serr);
dmesgln("{}: AHCI Port {} fatal error, shutting down!", m_parent_controller->device_identifier().address(), representative_port_index());
dmesgln("{}: AHCI Port {} fatal error, SError {}", m_parent_controller->device_identifier().address(), representative_port_index(), (u32)m_port_registers.serr);
stop_command_list_processing();
stop_fis_receiving();
m_interrupt_enable.clear();
@ -283,12 +277,7 @@ bool AHCIPort::initialize()
// FIXME: We don't support ATAPI devices yet, so for now we don't "create" them
if (!is_atapi_attached()) {
LockRefPtr<AHCIController> controller = m_parent_controller.strong_ref();
if (!controller) {
dmesgln("AHCI Port {}: Device found, but parent controller is not available, abort.", representative_port_index());
return false;
}
m_connected_device = ATADiskDevice::create(*controller, { m_port_index, 0 }, 0, logical_sector_size, max_addressable_sector);
m_connected_device = ATADiskDevice::create(*m_parent_controller, { m_port_index, 0 }, 0, logical_sector_size, max_addressable_sector);
} else {
dbgln("AHCI Port {}: Ignoring ATAPI devices as we don't support them.", representative_port_index());
}
@ -590,10 +579,6 @@ bool AHCIPort::identify_device()
if (!spin_until_ready())
return false;
LockRefPtr<AHCIController> controller = m_parent_controller.strong_ref();
if (!controller)
return false;
auto unused_command_header = try_to_find_unused_command_header();
VERIFY(unused_command_header.has_value());
auto* command_list_entries = (volatile AHCI::CommandHeader*)m_command_list_region->vaddr().as_ptr();

View file

@ -9,10 +9,9 @@
#include <AK/OwnPtr.h>
#include <AK/RefPtr.h>
#include <Kernel/Devices/Device.h>
#include <Kernel/Devices/Storage/ATA/AHCI/Definitions.h>
#include <Kernel/Devices/Storage/ATA/AHCI/InterruptHandler.h>
#include <Kernel/Devices/Storage/ATA/ATADevice.h>
#include <Kernel/Devices/Storage/ATA/Definitions.h>
#include <Kernel/Devices/Storage/AHCI/ATADevice.h>
#include <Kernel/Devices/Storage/AHCI/Definitions.h>
#include <Kernel/Devices/Storage/AHCI/InterruptHandler.h>
#include <Kernel/Interrupts/IRQHandler.h>
#include <Kernel/Library/LockWeakPtr.h>
#include <Kernel/Library/LockWeakable.h>
@ -126,7 +125,7 @@ private:
NonnullRefPtr<Memory::PhysicalPage> const m_identify_buffer_page;
volatile AHCI::PortRegisters& m_port_registers;
LockWeakPtr<AHCIController> m_parent_controller;
NonnullRefPtr<AHCIController> const m_parent_controller;
AHCI::PortInterruptStatusBitField m_interrupt_status;
AHCI::PortInterruptEnableBitField m_interrupt_enable;

View file

@ -1,17 +0,0 @@
/*
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Devices/Storage/ATA/ATAController.h>
#include <Kernel/Devices/Storage/StorageManagement.h>
namespace Kernel {
ATAController::ATAController()
: StorageController(StorageManagement::generate_relative_ata_controller_id({}))
{
}
}

View file

@ -1,29 +0,0 @@
/*
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/OwnPtr.h>
#include <AK/Types.h>
#include <Kernel/Devices/BlockDevice.h>
#include <Kernel/Devices/Storage/StorageController.h>
#include <Kernel/Library/LockRefPtr.h>
namespace Kernel {
class AsyncBlockDeviceRequest;
class ATADevice;
class ATAController
: public StorageController
, public LockWeakable<ATAController> {
public:
virtual void start_request(ATADevice const&, AsyncBlockDeviceRequest&) = 0;
protected:
ATAController();
};
}

View file

@ -1,46 +0,0 @@
/*
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <Kernel/Devices/Storage/ATA/ATAController.h>
#include <Kernel/Devices/Storage/StorageDevice.h>
#include <Kernel/Interrupts/IRQHandler.h>
#include <Kernel/Locking/Mutex.h>
namespace Kernel {
class ATADevice : public StorageDevice {
public:
// Note: For IDE drives, port means Primary or Secondary (0 or 1),
// and subport means Master or Slave (0 or 1).
// For SATA drives (AHCI driven HBAs), a port can be a number from 0 to 31,
// and subport can be a number from 0 to 14 (only 15 devices are allowed to
// be connected to one SATA port multiplier).
struct Address {
// FIXME: u32 for this value is wasteful, because even AHCI only support 32 ports
u32 port;
u8 subport;
};
public:
virtual ~ATADevice() override;
// ^BlockDevice
virtual void start_request(AsyncBlockDeviceRequest&) override;
u16 ata_capabilites() const { return m_capabilities; }
Address const& ata_address() const { return m_ata_address; }
protected:
ATADevice(ATAController const&, Address, u16, u16, u64);
LockWeakPtr<ATAController> m_controller;
Address const m_ata_address;
u16 const m_capabilities;
};
}

View file

@ -1,520 +0,0 @@
/*
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <Kernel/Arch/CPU.h>
#include <Kernel/Arch/Delay.h>
#include <Kernel/Devices/Storage/ATA/ATADiskDevice.h>
#include <Kernel/Devices/Storage/ATA/ATAPort.h>
#include <Kernel/Devices/Storage/ATA/Definitions.h>
#include <Kernel/Tasks/WorkQueue.h>
namespace Kernel {
class ATAPortInterruptDisabler {
public:
ATAPortInterruptDisabler(ATAPort& port)
: m_port(port)
{
(void)port.disable_interrupts();
}
~ATAPortInterruptDisabler()
{
(void)m_port->enable_interrupts();
}
private:
LockRefPtr<ATAPort> m_port;
};
class ATAPortInterruptCleaner {
public:
ATAPortInterruptCleaner(ATAPort& port)
: m_port(port)
{
}
~ATAPortInterruptCleaner()
{
(void)m_port->force_clear_interrupts();
}
private:
LockRefPtr<ATAPort> m_port;
};
void ATAPort::fix_name_string_in_identify_device_block()
{
VERIFY(m_lock.is_locked());
auto* wbuf = (u16*)m_ata_identify_data_buffer->data();
auto* bbuf = m_ata_identify_data_buffer->data() + 27 * 2;
for (size_t word_index = 27; word_index < 47; word_index++) {
u16 data = wbuf[word_index];
*(bbuf++) = MSB(data);
*(bbuf++) = LSB(data);
}
}
ErrorOr<void> ATAPort::detect_connected_devices()
{
MutexLocker locker(m_lock);
for (size_t device_index = 0; device_index < max_possible_devices_connected(); device_index++) {
TRY(device_select(device_index));
auto device_presence = TRY(detect_presence_on_selected_device());
if (!device_presence)
continue;
TaskFile identify_taskfile;
memset(&identify_taskfile, 0, sizeof(TaskFile));
identify_taskfile.command = ATA_CMD_IDENTIFY;
auto buffer = UserOrKernelBuffer::for_kernel_buffer(m_ata_identify_data_buffer->data());
{
auto result = execute_polled_command(TransactionDirection::Read, LBAMode::None, identify_taskfile, buffer, 0, 256, 100, 100);
if (result.is_error()) {
continue;
}
}
ATAIdentifyBlock volatile& identify_block = (ATAIdentifyBlock volatile&)(*m_ata_identify_data_buffer->data());
u16 capabilities = identify_block.capabilities[0];
StringView device_name = StringView((char const*)const_cast<u16*>(identify_block.model_number), 40);
fix_name_string_in_identify_device_block();
u64 max_addressable_block = identify_block.max_28_bit_addressable_logical_sector;
dbgln("ATAPort: device found: Name={}, Capacity={}, Capabilities={:#04x}", device_name.trim_whitespace(), max_addressable_block * 512, capabilities);
// If the drive is so old that it doesn't support LBA, ignore it.
if (!(capabilities & ATA_CAP_LBA)) {
dbgln("ATAPort: device found but without LBA support (what kind of dinosaur we see here?)");
continue;
}
// if we support 48-bit LBA, use that value instead.
if (identify_block.commands_and_feature_sets_supported[1] & (1 << 10))
max_addressable_block = identify_block.user_addressable_logical_sectors_count;
// FIXME: Don't assume all drives will have logical sector size of 512 bytes.
ATADevice::Address address = { m_port_index, static_cast<u8>(device_index) };
m_ata_devices.append(ATADiskDevice::create(m_parent_ata_controller, address, capabilities, 512, max_addressable_block));
}
return {};
}
LockRefPtr<StorageDevice> ATAPort::connected_device(size_t device_index) const
{
MutexLocker locker(m_lock);
if (m_ata_devices.size() > device_index)
return m_ata_devices[device_index];
return {};
}
ErrorOr<void> ATAPort::start_request(ATADevice const& associated_device, AsyncBlockDeviceRequest& request)
{
MutexLocker locker(m_lock);
VERIFY(m_current_request.is_null());
VERIFY(pio_capable() || dma_capable());
dbgln_if(ATA_DEBUG, "ATAPort::start_request");
m_current_request = request;
m_current_request_block_index = 0;
m_current_request_flushing_cache = false;
if (dma_capable()) {
TRY(prepare_and_initiate_dma_transaction(associated_device));
return {};
}
TRY(prepare_and_initiate_pio_transaction(associated_device));
return {};
}
void ATAPort::complete_pio_transaction(AsyncDeviceRequest::RequestResult result)
{
VERIFY(m_current_request);
// Now schedule reading back the buffer as soon as we leave the irq handler.
// This is important so that we can safely write the buffer back,
// which could cause page faults. Note that this may be called immediately
// before Processor::deferred_call_queue returns!
auto work_item_creation_result = g_io_work->try_queue([this, result]() {
dbgln_if(ATA_DEBUG, "ATAPort::complete_pio_transaction result: {}", (int)result);
MutexLocker locker(m_lock);
VERIFY(m_current_request);
auto current_request = m_current_request;
m_current_request.clear();
current_request->complete(result);
});
if (work_item_creation_result.is_error()) {
auto current_request = m_current_request;
m_current_request.clear();
current_request->complete(AsyncDeviceRequest::OutOfMemory);
}
}
void ATAPort::complete_dma_transaction(AsyncDeviceRequest::RequestResult result)
{
// NOTE: this may be called from the interrupt handler!
VERIFY(m_current_request);
VERIFY(m_lock.is_locked());
// Now schedule reading back the buffer as soon as we leave the irq handler.
// This is important so that we can safely write the buffer back,
// which could cause page faults. Note that this may be called immediately
// before Processor::deferred_call_queue returns!
auto work_item_creation_result = g_io_work->try_queue([this, result]() {
dbgln_if(ATA_DEBUG, "ATAPort::complete_dma_transaction result: {}", (int)result);
MutexLocker locker(m_lock);
if (!m_current_request)
return;
auto current_request = m_current_request;
m_current_request.clear();
if (result == AsyncDeviceRequest::Success) {
{
auto result = force_busmastering_status_clean();
if (result.is_error()) {
locker.unlock();
current_request->complete(AsyncDeviceRequest::Failure);
return;
}
}
if (current_request->request_type() == AsyncBlockDeviceRequest::Read) {
if (auto result = current_request->write_to_buffer(current_request->buffer(), m_dma_buffer_region->vaddr().as_ptr(), 512 * current_request->block_count()); result.is_error()) {
locker.unlock();
current_request->complete(AsyncDeviceRequest::MemoryFault);
return;
}
}
}
locker.unlock();
current_request->complete(result);
});
if (work_item_creation_result.is_error()) {
auto current_request = m_current_request;
m_current_request.clear();
current_request->complete(AsyncDeviceRequest::OutOfMemory);
}
}
static void print_ata_status(u8 status)
{
dbgln("ATAPort: print_status: DRQ={} BSY={}, DRDY={}, DSC={}, DF={}, CORR={}, IDX={}, ERR={}",
(status & ATA_SR_DRQ) != 0,
(status & ATA_SR_BSY) != 0,
(status & ATA_SR_DRDY) != 0,
(status & ATA_SR_DSC) != 0,
(status & ATA_SR_DF) != 0,
(status & ATA_SR_CORR) != 0,
(status & ATA_SR_IDX) != 0,
(status & ATA_SR_ERR) != 0);
}
static void try_disambiguate_ata_error(u8 error)
{
dbgln("ATAPort: Error cause:");
switch (error) {
case ATA_ER_BBK:
dbgln("ATAPort: - Bad block");
break;
case ATA_ER_UNC:
dbgln("ATAPort: - Uncorrectable data");
break;
case ATA_ER_MC:
dbgln("ATAPort: - Media changed");
break;
case ATA_ER_IDNF:
dbgln("ATAPort: - ID mark not found");
break;
case ATA_ER_MCR:
dbgln("ATAPort: - Media change request");
break;
case ATA_ER_ABRT:
dbgln("ATAPort: - Command aborted");
break;
case ATA_ER_TK0NF:
dbgln("ATAPort: - Track 0 not found");
break;
case ATA_ER_AMNF:
dbgln("ATAPort: - No address mark");
break;
default:
dbgln("ATAPort: - No one knows");
break;
}
}
ErrorOr<bool> ATAPort::handle_interrupt_after_dma_transaction()
{
if (!dma_capable())
return false;
u8 bstatus = TRY(busmastering_status());
if (!(bstatus & 0x4)) {
// interrupt not from this device, ignore
dbgln_if(ATA_DEBUG, "ATAPort: ignore interrupt");
return false;
}
auto work_item_creation_result = g_ata_work->try_queue([this]() -> void {
MutexLocker locker(m_lock);
u8 status = task_file_status().release_value();
m_entropy_source.add_random_event(status);
// clear bus master interrupt status
{
auto result = force_busmastering_status_clean();
if (result.is_error()) {
complete_dma_transaction(AsyncDeviceRequest::Failure);
return;
}
}
SpinlockLocker lock(m_hard_lock);
dbgln_if(ATA_DEBUG, "ATAPort: interrupt: DRQ={}, BSY={}, DRDY={}",
(status & ATA_SR_DRQ) != 0,
(status & ATA_SR_BSY) != 0,
(status & ATA_SR_DRDY) != 0);
if (!m_current_request) {
dbgln("ATAPort: IRQ but no pending request!");
return;
}
if (status & ATA_SR_ERR) {
print_ata_status(status);
auto device_error = task_file_error().release_value();
dbgln("ATAPort: Error {:#02x}!", (u8)device_error);
try_disambiguate_ata_error(device_error);
complete_dma_transaction(AsyncDeviceRequest::Failure);
return;
}
complete_dma_transaction(AsyncDeviceRequest::Success);
return;
});
if (work_item_creation_result.is_error()) {
auto current_request = m_current_request;
m_current_request.clear();
current_request->complete(AsyncDeviceRequest::OutOfMemory);
return Error::from_errno(ENOMEM);
}
return true;
}
ErrorOr<void> ATAPort::prepare_and_initiate_dma_transaction(ATADevice const& associated_device)
{
VERIFY(m_lock.is_locked());
VERIFY(!m_current_request.is_null());
VERIFY(m_current_request->block_count() <= 256);
// Note: We might be called here from an interrupt handler (like the page fault handler), so queue a read afterwards.
auto work_item_creation_result = g_ata_work->try_queue([this, &associated_device]() -> void {
MutexLocker locker(m_lock);
dbgln_if(ATA_DEBUG, "ATAPort::prepare_and_initiate_dma_transaction ({} x {})", m_current_request->block_index(), m_current_request->block_count());
VERIFY(!m_current_request.is_null());
VERIFY(m_current_request->block_count() <= 256);
{
auto result = device_select(associated_device.ata_address().subport);
if (result.is_error()) {
complete_dma_transaction(AsyncDeviceRequest::Failure);
return;
}
}
if (m_current_request->request_type() == AsyncBlockDeviceRequest::RequestType::Write) {
if (auto result = m_current_request->read_from_buffer(m_current_request->buffer(), m_dma_buffer_region->vaddr().as_ptr(), 512 * m_current_request->block_count()); result.is_error()) {
complete_dma_transaction(AsyncDeviceRequest::MemoryFault);
return;
}
}
prdt().offset = m_dma_buffer_page->paddr().get();
prdt().size = 512 * m_current_request->block_count();
VERIFY(prdt().size <= PAGE_SIZE);
SpinlockLocker hard_lock_locker(m_hard_lock);
{
auto result = stop_busmastering();
if (result.is_error()) {
complete_dma_transaction(AsyncDeviceRequest::Failure);
return;
}
}
if (m_current_request->request_type() == AsyncBlockDeviceRequest::RequestType::Write) {
auto result = prepare_transaction_with_busmastering(TransactionDirection::Write, m_prdt_page->paddr());
if (result.is_error()) {
complete_dma_transaction(AsyncDeviceRequest::Failure);
return;
}
} else {
auto result = prepare_transaction_with_busmastering(TransactionDirection::Read, m_prdt_page->paddr());
if (result.is_error()) {
complete_dma_transaction(AsyncDeviceRequest::Failure);
return;
}
}
TaskFile taskfile;
LBAMode lba_mode = LBAMode::TwentyEightBit;
auto lba = m_current_request->block_index();
if ((lba + m_current_request->block_count()) >= 0x10000000) {
lba_mode = LBAMode::FortyEightBit;
}
memset(&taskfile, 0, sizeof(TaskFile));
taskfile.lba_low[0] = (lba & 0x000000FF) >> 0;
taskfile.lba_low[1] = (lba & 0x0000FF00) >> 8;
taskfile.lba_low[2] = (lba & 0x00FF0000) >> 16;
taskfile.lba_high[0] = (lba & 0xFF000000) >> 24;
taskfile.lba_high[1] = (lba & 0xFF00000000ull) >> 32;
taskfile.lba_high[2] = (lba & 0xFF0000000000ull) >> 40;
taskfile.count = m_current_request->block_count();
if (lba_mode == LBAMode::TwentyEightBit)
taskfile.command = m_current_request->request_type() == AsyncBlockDeviceRequest::RequestType::Write ? ATA_CMD_WRITE_DMA : ATA_CMD_READ_DMA;
else
taskfile.command = m_current_request->request_type() == AsyncBlockDeviceRequest::RequestType::Write ? ATA_CMD_WRITE_DMA_EXT : ATA_CMD_READ_DMA_EXT;
{
auto result = load_taskfile_into_registers(taskfile, lba_mode, 1000);
if (result.is_error()) {
complete_dma_transaction(AsyncDeviceRequest::Failure);
return;
}
}
if (m_current_request->request_type() == AsyncBlockDeviceRequest::RequestType::Write) {
auto result = start_busmastering(TransactionDirection::Write);
if (result.is_error()) {
complete_dma_transaction(AsyncDeviceRequest::Failure);
return;
}
}
else {
auto result = start_busmastering(TransactionDirection::Read);
if (result.is_error()) {
complete_dma_transaction(AsyncDeviceRequest::Failure);
return;
}
}
});
if (work_item_creation_result.is_error()) {
auto current_request = m_current_request;
m_current_request.clear();
current_request->complete(AsyncDeviceRequest::OutOfMemory);
return Error::from_errno(ENOMEM);
}
return {};
}
ErrorOr<void> ATAPort::prepare_and_initiate_pio_transaction(ATADevice const& associated_device)
{
VERIFY(m_lock.is_locked());
VERIFY(!m_current_request.is_null());
VERIFY(m_current_request->block_count() <= 256);
dbgln_if(ATA_DEBUG, "ATAPort::prepare_and_initiate_pio_transaction ({} x {})", m_current_request->block_index(), m_current_request->block_count());
// Note: We might be called here from an interrupt handler (like the page fault handler), so queue a read afterwards.
auto work_item_creation_result = g_ata_work->try_queue([this, &associated_device]() -> void {
MutexLocker locker(m_lock);
{
auto result = device_select(associated_device.ata_address().subport);
if (result.is_error()) {
complete_pio_transaction(AsyncDeviceRequest::Failure);
return;
}
}
for (size_t block_index = 0; block_index < m_current_request->block_count(); block_index++) {
TaskFile taskfile;
LBAMode lba_mode = LBAMode::TwentyEightBit;
auto lba = m_current_request->block_index() + block_index;
if (lba >= 0x10000000) {
lba_mode = LBAMode::FortyEightBit;
}
memset(&taskfile, 0, sizeof(TaskFile));
taskfile.lba_low[0] = (lba & 0x000000FF) >> 0;
taskfile.lba_low[1] = (lba & 0x0000FF00) >> 8;
taskfile.lba_low[2] = (lba & 0x00FF0000) >> 16;
taskfile.lba_high[0] = (lba & 0xFF000000) >> 24;
taskfile.lba_high[1] = (lba & 0xFF00000000ull) >> 32;
taskfile.lba_high[2] = (lba & 0xFF0000000000ull) >> 40;
taskfile.count = 1;
if (lba_mode == LBAMode::TwentyEightBit)
taskfile.command = m_current_request->request_type() == AsyncBlockDeviceRequest::RequestType::Write ? ATA_CMD_WRITE_PIO : ATA_CMD_READ_PIO;
else
taskfile.command = m_current_request->request_type() == AsyncBlockDeviceRequest::RequestType::Write ? ATA_CMD_WRITE_PIO_EXT : ATA_CMD_READ_PIO_EXT;
if (m_current_request->request_type() == AsyncBlockDeviceRequest::RequestType::Read) {
auto result = execute_polled_command(TransactionDirection::Read, lba_mode, taskfile, m_current_request->buffer(), block_index, 256, 100, 100);
if (result.is_error()) {
complete_pio_transaction(AsyncDeviceRequest::Failure);
return;
}
} else {
auto result = execute_polled_command(TransactionDirection::Write, lba_mode, taskfile, m_current_request->buffer(), block_index, 256, 100, 100);
if (result.is_error()) {
complete_pio_transaction(AsyncDeviceRequest::Failure);
return;
}
}
}
complete_pio_transaction(AsyncDeviceRequest::Success);
});
if (work_item_creation_result.is_error()) {
auto current_request = m_current_request;
m_current_request.clear();
current_request->complete(AsyncDeviceRequest::OutOfMemory);
return Error::from_errno(ENOMEM);
}
return {};
}
ErrorOr<void> ATAPort::execute_polled_command(TransactionDirection direction, LBAMode lba_mode, TaskFile const& taskfile, UserOrKernelBuffer& buffer, size_t block_offset, size_t words_count, size_t preparation_timeout_in_milliseconds, size_t completion_timeout_in_milliseconds)
{
// Disable interrupts temporarily, just in case we have that enabled,
// remember the value to re-enable (and clean) later if needed.
ATAPortInterruptDisabler disabler(*this);
ATAPortInterruptCleaner cleaner(*this);
MutexLocker locker(m_lock);
{
SpinlockLocker hard_locker(m_hard_lock);
// Wait for device to be not busy or timeout
TRY(wait_if_busy_until_timeout(preparation_timeout_in_milliseconds));
// Send command, wait for result or timeout
TRY(load_taskfile_into_registers(taskfile, lba_mode, preparation_timeout_in_milliseconds));
size_t milliseconds_elapsed = 0;
for (;;) {
if (milliseconds_elapsed > completion_timeout_in_milliseconds)
break;
u8 status = task_file_status().release_value();
if (status & ATA_SR_ERR) {
return Error::from_errno(EINVAL);
}
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRQ)) {
break;
}
microseconds_delay(1000);
milliseconds_elapsed++;
}
if (milliseconds_elapsed > completion_timeout_in_milliseconds) {
critical_dmesgln("ATAPort: device state unknown. Timeout exceeded.");
return Error::from_errno(EINVAL);
}
}
VERIFY_INTERRUPTS_ENABLED();
if (direction == TransactionDirection::Read)
TRY(read_pio_data_to_buffer(buffer, block_offset, words_count));
else
TRY(write_pio_data_from_buffer(buffer, block_offset, words_count));
return {};
}
}

View file

@ -1,156 +0,0 @@
/*
* Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <Kernel/Devices/Storage/ATA/ATADevice.h>
namespace Kernel {
class AsyncBlockDeviceRequest;
class ATAPort
: public AtomicRefCounted<ATAPort>
, public LockWeakable<ATAPort> {
friend class ATAPortInterruptDisabler;
friend class ATAPortInterruptCleaner;
public:
struct TaskFile {
u8 command;
u8 lba_low[3];
u8 device;
u8 lba_high[3];
u8 features_high;
u16 count;
u8 icc;
u8 control;
u32 reserved;
};
enum class TransactionDirection : u8 {
Read,
Write,
};
struct [[gnu::packed]] PhysicalRegionDescriptor {
u32 offset;
u16 size { 0 };
u16 end_of_table { 0 };
};
enum class LBAMode : u8 {
None,
TwentyEightBit,
FortyEightBit,
};
public:
LockRefPtr<StorageDevice> connected_device(size_t device_index) const;
virtual ~ATAPort() = default;
virtual ErrorOr<void> disable() = 0;
virtual ErrorOr<void> power_on() = 0;
ErrorOr<void> detect_connected_devices();
ErrorOr<bool> handle_interrupt_after_dma_transaction();
ErrorOr<void> start_request(ATADevice const& associated_device, AsyncBlockDeviceRequest&);
// Note: Generic (P)ATA IDE "ports" are tied to the IDE channel link (cable), and trying to
// reset the master port or slave port and vice versa requires to actually reset
// both at once...
// This is due to the fact that IDE devices can be connected together (master-slave)
// with one 80 pin cable which forms one (primary/secondary) "ATA bus".
// Intel AHCI controllers generally allow individual phy port reset. The caller
// of this method should know this in advance...
// Note: ATAPI devices are an exception to this, so even if we initiate a
// a port reset, there's no guarantee that ATAPI devices will reset anyway,
// so resetting them requires to actually send the ATA "DEVICE RESET" command.
virtual ErrorOr<void> port_phy_reset() = 0;
// Note: Software reset means individual reset to a selected device on the "bus" (port).
// This means that this will likely work for devices that indicate support for
// PACKET commands (ATAPI devices) that also support DEVICE RESET. For other devices
// there's no other method to reset them besides (full) PHY reset.
// For devices that don't support this feature, just return ENOTSUP.
virtual ErrorOr<void> soft_reset() { return Error::from_errno(ENOTSUP); }
ErrorOr<void> execute_polled_command(TransactionDirection direction, LBAMode lba_mode, TaskFile const& taskfile, UserOrKernelBuffer&, size_t block_offset, size_t words_count, size_t preparation_timeout_in_milliseconds, size_t completion_timeout_in_milliseconds);
virtual bool has_sata_capabilities() { return false; }
virtual bool pio_capable() const = 0;
virtual bool dma_capable() const = 0;
virtual size_t max_possible_devices_connected() const = 0;
private:
ErrorOr<void> prepare_and_initiate_dma_transaction(ATADevice const& associated_device);
ErrorOr<void> prepare_and_initiate_pio_transaction(ATADevice const& associated_device);
void complete_dma_transaction(AsyncDeviceRequest::RequestResult result);
void complete_pio_transaction(AsyncDeviceRequest::RequestResult result);
void fix_name_string_in_identify_device_block();
protected:
virtual ErrorOr<u8> task_file_status() = 0;
virtual ErrorOr<u8> task_file_error() = 0;
virtual ErrorOr<void> wait_if_busy_until_timeout(size_t timeout_in_milliseconds) = 0;
virtual ErrorOr<void> device_select(size_t device_index) = 0;
virtual ErrorOr<bool> detect_presence_on_selected_device() = 0;
virtual ErrorOr<void> enable_interrupts() = 0;
virtual ErrorOr<void> disable_interrupts() = 0;
virtual ErrorOr<void> stop_busmastering() = 0;
virtual ErrorOr<void> start_busmastering(TransactionDirection) = 0;
virtual ErrorOr<void> force_busmastering_status_clean() = 0;
virtual ErrorOr<u8> busmastering_status() = 0;
virtual ErrorOr<void> prepare_transaction_with_busmastering(TransactionDirection, PhysicalAddress prdt_buffer) = 0;
virtual ErrorOr<void> initiate_transaction(TransactionDirection) = 0;
virtual ErrorOr<void> force_clear_interrupts() = 0;
// Note: This method assume we already selected the correct device!
virtual ErrorOr<void> load_taskfile_into_registers(TaskFile const&, LBAMode lba_mode, size_t completion_timeout_in_milliseconds) = 0;
virtual ErrorOr<void> read_pio_data_to_buffer(UserOrKernelBuffer&, size_t block_offset, size_t words_count) = 0;
virtual ErrorOr<void> write_pio_data_from_buffer(UserOrKernelBuffer const&, size_t block_offset, size_t words_count) = 0;
PhysicalRegionDescriptor& prdt() { return *reinterpret_cast<PhysicalRegionDescriptor*>(m_prdt_region->vaddr().as_ptr()); }
ATAPort(ATAController const& parent_controller, u8 port_index, NonnullOwnPtr<KBuffer> ata_identify_data_buffer)
: m_port_index(port_index)
, m_ata_identify_data_buffer(move(ata_identify_data_buffer))
, m_parent_ata_controller(parent_controller)
{
}
mutable Mutex m_lock;
Spinlock<LockRank::None> m_hard_lock {};
EntropySource m_entropy_source;
LockRefPtr<AsyncBlockDeviceRequest> m_current_request;
u64 m_current_request_block_index { 0 };
bool m_current_request_flushing_cache { false };
OwnPtr<Memory::Region> m_prdt_region;
OwnPtr<Memory::Region> m_dma_buffer_region;
RefPtr<Memory::PhysicalPage> m_prdt_page;
RefPtr<Memory::PhysicalPage> m_dma_buffer_page;
u8 const m_port_index;
Vector<NonnullLockRefPtr<ATADevice>> m_ata_devices;
NonnullOwnPtr<KBuffer> m_ata_identify_data_buffer;
NonnullLockRefPtr<ATAController> m_parent_ata_controller;
};
}

View file

@ -1,212 +0,0 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Types.h>
namespace Kernel::ATA {
enum DeviceSignature : u32 {
ATA = 0x00000101,
ATAPI = 0xEB140101,
EnclosureManagementBridge = 0xC33C0101,
PortMultiplier = 0x96690101,
Unconnected = 0xFFFFFFFF
};
}
#define ATA_SR_BSY 0x80
#define ATA_SR_DRDY 0x40
#define ATA_SR_DF 0x20
#define ATA_SR_DSC 0x10
#define ATA_SR_DRQ 0x08
#define ATA_SR_CORR 0x04
#define ATA_SR_IDX 0x02
#define ATA_SR_ERR 0x01
#define ATA_ER_BBK 0x80
#define ATA_ER_UNC 0x40
#define ATA_ER_MC 0x20
#define ATA_ER_IDNF 0x10
#define ATA_ER_MCR 0x08
#define ATA_ER_ABRT 0x04
#define ATA_ER_TK0NF 0x02
#define ATA_ER_AMNF 0x01
#define ATA_CMD_READ_PIO 0x20
#define ATA_CMD_READ_PIO_EXT 0x24
#define ATA_CMD_READ_DMA 0xC8
#define ATA_CMD_READ_DMA_EXT 0x25
#define ATA_CMD_WRITE_PIO 0x30
#define ATA_CMD_WRITE_PIO_EXT 0x34
#define ATA_CMD_WRITE_DMA 0xCA
#define ATA_CMD_WRITE_DMA_EXT 0x35
#define ATA_CMD_CACHE_FLUSH 0xE7
#define ATA_CMD_CACHE_FLUSH_EXT 0xEA
#define ATA_CMD_PACKET 0xA0
#define ATA_CMD_IDENTIFY_PACKET 0xA1
#define ATA_CMD_IDENTIFY 0xEC
#define ATAPI_CMD_READ 0xA8
#define ATAPI_CMD_EJECT 0x1B
#define ATA_IDENT_DEVICETYPE 0
#define ATA_IDENT_CYLINDERS 2
#define ATA_IDENT_HEADS 6
#define ATA_IDENT_SECTORS 12
#define ATA_IDENT_SERIAL 20
#define ATA_IDENT_MODEL 54
#define ATA_IDENT_CAPABILITIES 98
#define ATA_IDENT_FIELDVALID 106
#define ATA_IDENT_MAX_LBA 120
#define ATA_IDENT_COMMANDSETS 164
#define ATA_IDENT_MAX_LBA_EXT 200
#define ATA_USE_LBA_ADDRESSING (1 << 6)
#define IDE_ATA 0x00
#define IDE_ATAPI 0x01
#define ATA_REG_DATA 0x00
#define ATA_REG_ERROR 0x01
#define ATA_REG_FEATURES 0x01
#define ATA_REG_SECCOUNT0 0x02
#define ATA_REG_LBA0 0x03
#define ATA_REG_LBA1 0x04
#define ATA_REG_LBA2 0x05
#define ATA_REG_HDDEVSEL 0x06
#define ATA_REG_COMMAND 0x07
#define ATA_REG_STATUS 0x07
#define ATA_REG_SECCOUNT1 0x08
#define ATA_REG_LBA3 0x09
#define ATA_REG_LBA4 0x0A
#define ATA_REG_LBA5 0x0B
#define ATA_CTL_CONTROL 0x00
#define ATA_CTL_ALTSTATUS 0x00
#define ATA_CTL_DEVADDRESS 0x01
#define ATA_CAP_LBA 0x200
namespace Kernel {
struct [[gnu::packed]] ATAIdentifyBlock {
u16 general_configuration;
u16 obsolete;
u16 specific_configuration;
u16 obsolete2;
u16 retired[2];
u16 obsolete3;
u16 reserved_for_cfa[2];
u16 retired2;
u16 serial_number[10];
u16 retired3[2];
u16 obsolete4;
u16 firmware_revision[4];
u16 model_number[20];
u16 maximum_logical_sectors_per_drq;
u16 trusted_computing_features;
u16 capabilities[2];
u16 obsolete5[2];
u16 validity_flags;
u16 obsolete6[5];
u16 security_features;
u32 max_28_bit_addressable_logical_sector;
u16 obsolete7;
u16 dma_modes;
u16 pio_modes;
u16 minimum_multiword_dma_transfer_cycle;
u16 recommended_multiword_dma_transfer_cycle;
u16 minimum_multiword_pio_transfer_cycle_without_flow_control;
u16 minimum_multiword_pio_transfer_cycle_with_flow_control;
u16 additional_supported;
u16 reserved3[5];
u16 queue_depth;
u16 serial_ata_capabilities;
u16 serial_ata_additional_capabilities;
u16 serial_ata_features_supported;
u16 serial_ata_features_enabled;
u16 major_version_number;
u16 minor_version_number;
u16 commands_and_feature_sets_supported[3];
u16 commands_and_feature_sets_supported_or_enabled[3];
u16 ultra_dma_modes;
u16 timing_for_security_features[2];
u16 apm_level;
u16 master_password_id;
u16 hardware_reset_results;
u16 obsolete8;
u16 stream_minimum_request_time;
u16 streaming_transfer_time_for_dma;
u16 streaming_access_latency;
u16 streaming_performance_granularity[2];
u64 user_addressable_logical_sectors_count;
u16 streaming_transfer_time_for_pio;
u16 max_512_byte_blocks_per_data_set_management_command;
u16 physical_sector_size_to_logical_sector_size;
u16 inter_seek_delay_for_acoustic_testing;
u16 world_wide_name[4];
u16 reserved4[4];
u16 obsolete9;
u32 logical_sector_size;
u16 commands_and_feature_sets_supported2;
u16 commands_and_feature_sets_supported_or_enabled2;
u16 reserved_for_expanded_supported_and_enabled_settings[6];
u16 obsolete10;
u16 security_status;
u16 vendor_specific[31];
u16 reserved_for_cfa2[8];
u16 device_nominal_form_factor;
u16 data_set_management_command_support;
u16 additional_product_id[4];
u16 reserved5[2];
u16 current_media_serial_number[30];
u16 sct_command_transport;
u16 reserved6[2];
u16 logical_sectors_alignment_within_physical_sector;
u32 write_read_verify_sector_mode_3_count;
u32 write_read_verify_sector_mode_2_count;
u16 obsolete11[3];
u16 nominal_media_rotation_rate;
u16 reserved7;
u16 obsolete12;
u16 write_read_verify_feature_set_current_mode;
u16 reserved8;
u16 transport_major_version_number;
u16 transport_minor_version_number;
u16 reserved9[6];
u64 extended_user_addressable_logical_sectors_count;
u16 minimum_512_byte_data_blocks_per_download_microcode_operation;
u16 max_512_byte_data_blocks_per_download_microcode_operation;
u16 reserved10[19];
u16 integrity;
};
};

View file

@ -1,335 +0,0 @@
/*
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/ByteBuffer.h>
#include <AK/Singleton.h>
#include <AK/StringView.h>
#include <Kernel/Arch/Delay.h>
#include <Kernel/Bus/PCI/API.h>
#include <Kernel/Devices/Storage/ATA/ATADiskDevice.h>
#include <Kernel/Devices/Storage/ATA/Definitions.h>
#include <Kernel/Devices/Storage/ATA/GenericIDE/Channel.h>
#include <Kernel/Devices/Storage/ATA/GenericIDE/Controller.h>
#include <Kernel/Library/IOWindow.h>
#include <Kernel/Memory/MemoryManager.h>
#include <Kernel/Sections.h>
#include <Kernel/Tasks/Process.h>
#include <Kernel/Tasks/WorkQueue.h>
namespace Kernel {
#define PATA_PRIMARY_IRQ 14
#define PATA_SECONDARY_IRQ 15
UNMAP_AFTER_INIT ErrorOr<NonnullRefPtr<IDEChannel>> IDEChannel::create(IDEController const& controller, IOWindowGroup io_window_group, ChannelType type)
{
auto ata_identify_data_buffer = KBuffer::try_create_with_size("ATA Identify Page"sv, 4096, Memory::Region::Access::ReadWrite, AllocationStrategy::AllocateNow).release_value();
return adopt_nonnull_ref_or_enomem(new (nothrow) IDEChannel(controller, move(io_window_group), type, move(ata_identify_data_buffer)));
}
UNMAP_AFTER_INIT ErrorOr<NonnullRefPtr<IDEChannel>> IDEChannel::create(IDEController const& controller, u8 irq, IOWindowGroup io_window_group, ChannelType type)
{
auto ata_identify_data_buffer = KBuffer::try_create_with_size("ATA Identify Page"sv, 4096, Memory::Region::Access::ReadWrite, AllocationStrategy::AllocateNow).release_value();
return adopt_nonnull_ref_or_enomem(new (nothrow) IDEChannel(controller, irq, move(io_window_group), type, move(ata_identify_data_buffer)));
}
StringView IDEChannel::channel_type_string() const
{
if (m_channel_type == ChannelType::Primary)
return "Primary"sv;
return "Secondary"sv;
}
bool IDEChannel::select_device_and_wait_until_not_busy(DeviceType device_type, size_t milliseconds_timeout)
{
microseconds_delay(20);
u8 slave = device_type == DeviceType::Slave;
m_io_window_group.io_window().write8(ATA_REG_HDDEVSEL, 0xA0 | (slave << 4)); // First, we need to select the drive itself
microseconds_delay(20);
size_t time_elapsed = 0;
while (m_io_window_group.control_window().read8(0) & ATA_SR_BSY && time_elapsed <= milliseconds_timeout) {
microseconds_delay(1000);
time_elapsed++;
}
return time_elapsed <= milliseconds_timeout;
}
ErrorOr<void> IDEChannel::port_phy_reset()
{
MutexLocker locker(m_lock);
SpinlockLocker hard_locker(m_hard_lock);
// reset the channel
u8 device_control = m_io_window_group.control_window().read8(0);
// Wait 30 milliseconds
microseconds_delay(30000);
m_io_window_group.control_window().write8(0, device_control | (1 << 2));
// Wait 30 milliseconds
microseconds_delay(30000);
m_io_window_group.control_window().write8(0, device_control);
// Wait up to 30 seconds before failing
if (!select_device_and_wait_until_not_busy(DeviceType::Master, 30000)) {
dbgln("IDEChannel: reset failed, busy flag on master stuck");
return Error::from_errno(EBUSY);
}
// Wait up to 30 seconds before failing
if (!select_device_and_wait_until_not_busy(DeviceType::Slave, 30000)) {
dbgln("IDEChannel: reset failed, busy flag on slave stuck");
return Error::from_errno(EBUSY);
}
return {};
}
#if ARCH(X86_64)
ErrorOr<void> IDEChannel::allocate_resources_for_pci_ide_controller(Badge<PCIIDELegacyModeController>, bool force_pio)
{
return allocate_resources(force_pio);
}
ErrorOr<void> IDEChannel::allocate_resources_for_isa_ide_controller(Badge<ISAIDEController>)
{
return allocate_resources(true);
}
#endif
UNMAP_AFTER_INIT ErrorOr<void> IDEChannel::allocate_resources(bool force_pio)
{
dbgln_if(PATA_DEBUG, "IDEChannel: {} IO base: {}", channel_type_string(), m_io_window_group.io_window());
dbgln_if(PATA_DEBUG, "IDEChannel: {} control base: {}", channel_type_string(), m_io_window_group.control_window());
if (m_io_window_group.bus_master_window())
dbgln_if(PATA_DEBUG, "IDEChannel: {} bus master base: {}", channel_type_string(), m_io_window_group.bus_master_window());
else
dbgln_if(PATA_DEBUG, "IDEChannel: {} bus master base disabled", channel_type_string());
if (!force_pio) {
m_dma_enabled = true;
VERIFY(m_io_window_group.bus_master_window());
// Let's try to set up DMA transfers.
m_prdt_region = TRY(MM.allocate_dma_buffer_page("IDE PRDT"sv, Memory::Region::Access::ReadWrite, m_prdt_page));
VERIFY(!m_prdt_page.is_null());
m_dma_buffer_region = TRY(MM.allocate_dma_buffer_page("IDE DMA region"sv, Memory::Region::Access::ReadWrite, m_dma_buffer_page));
VERIFY(!m_dma_buffer_page.is_null());
prdt().end_of_table = 0x8000;
// clear bus master interrupt status
m_io_window_group.bus_master_window()->write8(2, m_io_window_group.bus_master_window()->read8(2) | 4);
}
return {};
}
UNMAP_AFTER_INIT IDEChannel::IDEChannel(IDEController const& controller, u8 irq, IOWindowGroup io_group, ChannelType type, NonnullOwnPtr<KBuffer> ata_identify_data_buffer)
: ATAPort(controller, (type == ChannelType::Primary ? 0 : 1), move(ata_identify_data_buffer))
, IRQHandler(irq)
, m_channel_type(type)
, m_io_window_group(move(io_group))
{
}
UNMAP_AFTER_INIT IDEChannel::IDEChannel(IDEController const& controller, IOWindowGroup io_group, ChannelType type, NonnullOwnPtr<KBuffer> ata_identify_data_buffer)
: ATAPort(controller, (type == ChannelType::Primary ? 0 : 1), move(ata_identify_data_buffer))
, IRQHandler(type == ChannelType::Primary ? PATA_PRIMARY_IRQ : PATA_SECONDARY_IRQ)
, m_channel_type(type)
, m_io_window_group(move(io_group))
{
}
UNMAP_AFTER_INIT IDEChannel::~IDEChannel() = default;
bool IDEChannel::handle_irq(RegisterState const&)
{
auto result = handle_interrupt_after_dma_transaction();
// FIXME: Propagate errors properly
VERIFY(!result.is_error());
return result.release_value();
}
ErrorOr<void> IDEChannel::stop_busmastering()
{
VERIFY(m_lock.is_locked());
VERIFY(m_io_window_group.bus_master_window());
m_io_window_group.bus_master_window()->write8(0, 0);
return {};
}
ErrorOr<void> IDEChannel::start_busmastering(TransactionDirection direction)
{
VERIFY(m_lock.is_locked());
VERIFY(m_io_window_group.bus_master_window());
m_io_window_group.bus_master_window()->write8(0, (direction != TransactionDirection::Write ? 0x9 : 0x1));
return {};
}
ErrorOr<void> IDEChannel::force_busmastering_status_clean()
{
VERIFY(m_lock.is_locked());
VERIFY(m_io_window_group.bus_master_window());
m_io_window_group.bus_master_window()->write8(2, m_io_window_group.bus_master_window()->read8(2) | 4);
return {};
}
ErrorOr<u8> IDEChannel::busmastering_status()
{
VERIFY(m_io_window_group.bus_master_window());
return m_io_window_group.bus_master_window()->read8(2);
}
ErrorOr<void> IDEChannel::prepare_transaction_with_busmastering(TransactionDirection direction, PhysicalAddress prdt_buffer)
{
VERIFY(m_lock.is_locked());
m_io_window_group.bus_master_window()->write32(4, prdt_buffer.get());
m_io_window_group.bus_master_window()->write8(0, direction != TransactionDirection::Write ? 0x8 : 0);
// Turn on "Interrupt" and "Error" flag. The error flag should be cleared by hardware.
m_io_window_group.bus_master_window()->write8(2, m_io_window_group.bus_master_window()->read8(2) | 0x6);
return {};
}
ErrorOr<void> IDEChannel::initiate_transaction(TransactionDirection)
{
VERIFY(m_lock.is_locked());
return {};
}
ErrorOr<u8> IDEChannel::task_file_status()
{
VERIFY(m_lock.is_locked());
return m_io_window_group.control_window().read8(0);
}
ErrorOr<u8> IDEChannel::task_file_error()
{
VERIFY(m_lock.is_locked());
return m_io_window_group.io_window().read8(ATA_REG_ERROR);
}
ErrorOr<bool> IDEChannel::detect_presence_on_selected_device()
{
VERIFY(m_lock.is_locked());
m_io_window_group.io_window().write8(ATA_REG_SECCOUNT0, 0x55);
m_io_window_group.io_window().write8(ATA_REG_LBA0, 0xAA);
m_io_window_group.io_window().write8(ATA_REG_SECCOUNT0, 0xAA);
m_io_window_group.io_window().write8(ATA_REG_LBA0, 0x55);
m_io_window_group.io_window().write8(ATA_REG_SECCOUNT0, 0x55);
m_io_window_group.io_window().write8(ATA_REG_LBA0, 0xAA);
auto nsectors_value = m_io_window_group.io_window().read8(ATA_REG_SECCOUNT0);
auto lba0 = m_io_window_group.io_window().read8(ATA_REG_LBA0);
if (lba0 == 0xAA && nsectors_value == 0x55)
return true;
return false;
}
ErrorOr<void> IDEChannel::wait_if_busy_until_timeout(size_t timeout_in_milliseconds)
{
size_t time_elapsed = 0;
while (m_io_window_group.control_window().read8(0) & ATA_SR_BSY && time_elapsed <= timeout_in_milliseconds) {
microseconds_delay(1000);
time_elapsed++;
}
if (time_elapsed <= timeout_in_milliseconds)
return {};
return Error::from_errno(EBUSY);
}
ErrorOr<void> IDEChannel::force_clear_interrupts()
{
VERIFY(m_lock.is_locked());
m_io_window_group.io_window().read8(ATA_REG_STATUS);
return {};
}
ErrorOr<void> IDEChannel::load_taskfile_into_registers(ATAPort::TaskFile const& task_file, LBAMode lba_mode, size_t completion_timeout_in_milliseconds)
{
VERIFY(m_lock.is_locked());
VERIFY(m_hard_lock.is_locked());
u8 head = 0;
if (lba_mode == LBAMode::FortyEightBit) {
head = 0;
} else if (lba_mode == LBAMode::TwentyEightBit) {
head = (task_file.lba_high[0] & 0x0F);
}
// Note: Preserve the selected drive, always use LBA addressing
auto driver_register = ((m_io_window_group.io_window().read8(ATA_REG_HDDEVSEL) & (1 << 4)) | (head | (1 << 5) | (1 << 6)));
m_io_window_group.io_window().write8(ATA_REG_HDDEVSEL, driver_register);
microseconds_delay(50);
if (lba_mode == LBAMode::FortyEightBit) {
m_io_window_group.io_window().write8(ATA_REG_SECCOUNT1, (task_file.count >> 8) & 0xFF);
m_io_window_group.io_window().write8(ATA_REG_LBA3, task_file.lba_high[0]);
m_io_window_group.io_window().write8(ATA_REG_LBA4, task_file.lba_high[1]);
m_io_window_group.io_window().write8(ATA_REG_LBA5, task_file.lba_high[2]);
}
m_io_window_group.io_window().write8(ATA_REG_SECCOUNT0, task_file.count & 0xFF);
m_io_window_group.io_window().write8(ATA_REG_LBA0, task_file.lba_low[0]);
m_io_window_group.io_window().write8(ATA_REG_LBA1, task_file.lba_low[1]);
m_io_window_group.io_window().write8(ATA_REG_LBA2, task_file.lba_low[2]);
// FIXME: Set a timeout here?
size_t time_elapsed = 0;
for (;;) {
if (time_elapsed > completion_timeout_in_milliseconds)
return Error::from_errno(EBUSY);
// FIXME: Use task_file_status method
auto status = m_io_window_group.control_window().read8(0);
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY))
break;
microseconds_delay(1000);
time_elapsed++;
}
m_io_window_group.io_window().write8(ATA_REG_COMMAND, task_file.command);
return {};
}
ErrorOr<void> IDEChannel::device_select(size_t device_index)
{
VERIFY(m_lock.is_locked());
if (device_index > 1)
return Error::from_errno(EINVAL);
microseconds_delay(20);
m_io_window_group.io_window().write8(ATA_REG_HDDEVSEL, (0xA0 | ((device_index) << 4)));
microseconds_delay(20);
return {};
}
ErrorOr<void> IDEChannel::enable_interrupts()
{
VERIFY(m_lock.is_locked());
m_io_window_group.control_window().write8(0, 0);
m_interrupts_enabled = true;
return {};
}
ErrorOr<void> IDEChannel::disable_interrupts()
{
VERIFY(m_lock.is_locked());
m_io_window_group.control_window().write8(0, 1 << 1);
m_interrupts_enabled = false;
return {};
}
ErrorOr<void> IDEChannel::read_pio_data_to_buffer(UserOrKernelBuffer& buffer, size_t block_offset, size_t words_count)
{
VERIFY(m_lock.is_locked());
VERIFY(words_count == 256);
for (u32 i = 0; i < 256; ++i) {
u16 data = m_io_window_group.io_window().read16(ATA_REG_DATA);
// FIXME: Don't assume 512 bytes sector
TRY(buffer.write(&data, block_offset * 512 + (i * 2), 2));
}
return {};
}
ErrorOr<void> IDEChannel::write_pio_data_from_buffer(UserOrKernelBuffer const& buffer, size_t block_offset, size_t words_count)
{
VERIFY(m_lock.is_locked());
VERIFY(words_count == 256);
for (u32 i = 0; i < 256; ++i) {
u16 buf;
// FIXME: Don't assume 512 bytes sector
TRY(buffer.read(&buf, block_offset * 512 + (i * 2), 2));
m_io_window_group.io_window().write16(ATA_REG_DATA, buf);
}
return {};
}
}

View file

@ -1,156 +0,0 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
//
// Parallel ATA (PATA) controller driver
//
// This driver describes a logical PATA Channel. Each channel can connect up to 2
// IDE Hard Disk Drives. The drives themselves can be either the master drive (hd0)
// or the slave drive (hd1).
//
// More information about the ATA spec for PATA can be found here:
// ftp://ftp.seagate.com/acrobat/reference/111-1c.pdf
//
#pragma once
#include <AK/Error.h>
#include <Kernel/Devices/Device.h>
#include <Kernel/Devices/Storage/ATA/ATADevice.h>
#include <Kernel/Devices/Storage/ATA/ATAPort.h>
#include <Kernel/Devices/Storage/StorageDevice.h>
#include <Kernel/Interrupts/IRQHandler.h>
#include <Kernel/Library/IOWindow.h>
#include <Kernel/Library/LockRefPtr.h>
#include <Kernel/Locking/Mutex.h>
#include <Kernel/Memory/PhysicalAddress.h>
#include <Kernel/Memory/PhysicalPage.h>
#include <Kernel/Security/Random.h>
#include <Kernel/Tasks/WaitQueue.h>
namespace Kernel {
class AsyncBlockDeviceRequest;
class IDEController;
#if ARCH(X86_64)
class PCIIDELegacyModeController;
class ISAIDEController;
#endif
class IDEChannel
: public ATAPort
, public IRQHandler {
friend class IDEController;
public:
enum class ChannelType : u8 {
Primary,
Secondary
};
enum class DeviceType : u8 {
Master,
Slave,
};
struct IOWindowGroup {
IOWindowGroup(NonnullOwnPtr<IOWindow> io_window, NonnullOwnPtr<IOWindow> control_window, NonnullOwnPtr<IOWindow> m_bus_master_window)
: m_io_window(move(io_window))
, m_control_window(move(control_window))
, m_bus_master_window(move(m_bus_master_window))
{
}
IOWindowGroup(NonnullOwnPtr<IOWindow> io_window, NonnullOwnPtr<IOWindow> control_window)
: m_io_window(move(io_window))
, m_control_window(move(control_window))
{
}
// Disable default implementations that would use surprising integer promotion.
bool operator==(IOWindowGroup const&) const = delete;
bool operator<=(IOWindowGroup const&) const = delete;
bool operator>=(IOWindowGroup const&) const = delete;
bool operator<(IOWindowGroup const&) const = delete;
bool operator>(IOWindowGroup const&) const = delete;
IOWindow& io_window() const { return *m_io_window; }
IOWindow& control_window() const { return *m_control_window; }
IOWindow* bus_master_window() const { return m_bus_master_window.ptr(); }
private:
mutable NonnullOwnPtr<IOWindow> m_io_window;
mutable NonnullOwnPtr<IOWindow> m_control_window;
mutable OwnPtr<IOWindow> m_bus_master_window;
};
public:
static ErrorOr<NonnullRefPtr<IDEChannel>> create(IDEController const&, IOWindowGroup, ChannelType type);
static ErrorOr<NonnullRefPtr<IDEChannel>> create(IDEController const&, u8 irq, IOWindowGroup, ChannelType type);
virtual ~IDEChannel() override;
virtual StringView purpose() const override { return "PATA Channel"sv; }
#if ARCH(X86_64)
ErrorOr<void> allocate_resources_for_pci_ide_controller(Badge<PCIIDELegacyModeController>, bool force_pio);
ErrorOr<void> allocate_resources_for_isa_ide_controller(Badge<ISAIDEController>);
#endif
private:
static constexpr size_t m_logical_sector_size = 512;
ErrorOr<void> allocate_resources(bool force_pio);
StringView channel_type_string() const;
virtual ErrorOr<void> disable() override { TODO(); }
virtual ErrorOr<void> power_on() override { TODO(); }
virtual ErrorOr<void> port_phy_reset() override;
bool select_device_and_wait_until_not_busy(DeviceType, size_t milliseconds_timeout);
virtual bool pio_capable() const override { return true; }
virtual bool dma_capable() const override { return m_dma_enabled; }
virtual size_t max_possible_devices_connected() const override { return 2; }
virtual ErrorOr<void> stop_busmastering() override;
virtual ErrorOr<void> start_busmastering(TransactionDirection) override;
virtual ErrorOr<void> force_busmastering_status_clean() override;
virtual ErrorOr<u8> busmastering_status() override;
virtual ErrorOr<void> prepare_transaction_with_busmastering(TransactionDirection, PhysicalAddress prdt_buffer) override;
virtual ErrorOr<void> initiate_transaction(TransactionDirection) override;
virtual ErrorOr<u8> task_file_status() override;
virtual ErrorOr<u8> task_file_error() override;
virtual ErrorOr<void> wait_if_busy_until_timeout(size_t timeout_in_milliseconds) override;
virtual ErrorOr<void> device_select(size_t device_index) override;
virtual ErrorOr<bool> detect_presence_on_selected_device() override;
virtual ErrorOr<void> enable_interrupts() override;
virtual ErrorOr<void> disable_interrupts() override;
virtual ErrorOr<void> force_clear_interrupts() override;
virtual ErrorOr<void> load_taskfile_into_registers(TaskFile const&, LBAMode lba_mode, size_t completion_timeout_in_milliseconds) override;
virtual ErrorOr<void> read_pio_data_to_buffer(UserOrKernelBuffer&, size_t block_offset, size_t words_count) override;
virtual ErrorOr<void> write_pio_data_from_buffer(UserOrKernelBuffer const&, size_t block_offset, size_t words_count) override;
IDEChannel(IDEController const&, IOWindowGroup, ChannelType type, NonnullOwnPtr<KBuffer> ata_identify_data_buffer);
IDEChannel(IDEController const&, u8 irq, IOWindowGroup, ChannelType type, NonnullOwnPtr<KBuffer> ata_identify_data_buffer);
//^ IRQHandler
virtual bool handle_irq(RegisterState const&) override;
// Data members
ChannelType m_channel_type { ChannelType::Primary };
bool m_dma_enabled { false };
bool m_interrupts_enabled { true };
IOWindowGroup m_io_window_group;
};
}

View file

@ -1,95 +0,0 @@
/*
* Copyright (c) 2020-2022, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/OwnPtr.h>
#include <AK/Types.h>
#include <Kernel/Bus/PCI/API.h>
#include <Kernel/Devices/Storage/ATA/ATADiskDevice.h>
#include <Kernel/Devices/Storage/ATA/GenericIDE/Channel.h>
#include <Kernel/Devices/Storage/ATA/GenericIDE/Controller.h>
#include <Kernel/Library/LockRefPtr.h>
#include <Kernel/Sections.h>
namespace Kernel {
ErrorOr<void> IDEController::reset()
{
return Error::from_errno(ENOTIMPL);
}
ErrorOr<void> IDEController::shutdown()
{
return Error::from_errno(ENOTIMPL);
}
size_t IDEController::devices_count() const
{
size_t count = 0;
for (u32 index = 0; index < 4; index++) {
if (!device(index).is_null())
count++;
}
return count;
}
void IDEController::start_request(ATADevice const& device, AsyncBlockDeviceRequest& request)
{
auto& address = device.ata_address();
VERIFY(address.subport < 2);
switch (address.port) {
case 0: {
auto result = m_channels[0]->start_request(device, request);
// FIXME: Propagate errors properly
VERIFY(!result.is_error());
return;
}
case 1: {
auto result = m_channels[1]->start_request(device, request);
// FIXME: Propagate errors properly
VERIFY(!result.is_error());
return;
}
}
VERIFY_NOT_REACHED();
}
void IDEController::complete_current_request(AsyncDeviceRequest::RequestResult)
{
VERIFY_NOT_REACHED();
}
UNMAP_AFTER_INIT IDEController::IDEController() = default;
UNMAP_AFTER_INIT IDEController::~IDEController() = default;
LockRefPtr<StorageDevice> IDEController::device_by_channel_and_position(u32 index) const
{
switch (index) {
case 0:
return m_channels[0]->connected_device(0);
case 1:
return m_channels[0]->connected_device(1);
case 2:
return m_channels[1]->connected_device(0);
case 3:
return m_channels[1]->connected_device(1);
}
VERIFY_NOT_REACHED();
}
LockRefPtr<StorageDevice> IDEController::device(u32 index) const
{
Vector<NonnullLockRefPtr<StorageDevice>> connected_devices;
for (size_t index = 0; index < 4; index++) {
auto checked_device = device_by_channel_and_position(index);
if (checked_device.is_null())
continue;
connected_devices.append(checked_device.release_nonnull());
}
if (index >= connected_devices.size())
return nullptr;
return connected_devices[index];
}
}

View file

@ -1,36 +0,0 @@
/*
* Copyright (c) 2020-2022, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/OwnPtr.h>
#include <AK/Types.h>
#include <Kernel/Devices/Storage/ATA/ATAController.h>
#include <Kernel/Devices/Storage/StorageDevice.h>
#include <Kernel/Library/LockRefPtr.h>
namespace Kernel {
class AsyncBlockDeviceRequest;
class IDEChannel;
class IDEController : public ATAController {
public:
virtual ~IDEController() override;
virtual LockRefPtr<StorageDevice> device(u32 index) const override final;
virtual ErrorOr<void> reset() override final;
virtual ErrorOr<void> shutdown() override final;
virtual size_t devices_count() const override final;
virtual void start_request(ATADevice const&, AsyncBlockDeviceRequest&) override final;
virtual void complete_current_request(AsyncDeviceRequest::RequestResult) override final;
protected:
IDEController();
LockRefPtr<StorageDevice> device_by_channel_and_position(u32 index) const;
Array<RefPtr<IDEChannel>, 2> m_channels;
};
}

View file

@ -9,10 +9,6 @@
#include <AK/Singleton.h>
#include <AK/StringView.h>
#include <AK/UUID.h>
#if ARCH(X86_64)
# include <Kernel/Arch/x86_64/ISABus/IDEController.h>
# include <Kernel/Arch/x86_64/PCI/IDELegacyModeController.h>
#endif
#if ARCH(AARCH64)
# include <Kernel/Arch/aarch64/RPi/SDHostController.h>
#endif
@ -22,8 +18,7 @@
#include <Kernel/Bus/PCI/Controller/VolumeManagementDevice.h>
#include <Kernel/Devices/BlockDevice.h>
#include <Kernel/Devices/DeviceManagement.h>
#include <Kernel/Devices/Storage/ATA/AHCI/Controller.h>
#include <Kernel/Devices/Storage/ATA/GenericIDE/Controller.h>
#include <Kernel/Devices/Storage/AHCI/Controller.h>
#include <Kernel/Devices/Storage/NVMe/NVMeController.h>
#include <Kernel/Devices/Storage/SD/PCISDHostController.h>
#include <Kernel/Devices/Storage/SD/SDHostController.h>
@ -42,7 +37,7 @@ static Atomic<u32> s_storage_device_minor_number;
static Atomic<u32> s_partition_device_minor_number;
static Atomic<u32> s_controller_id;
static Atomic<u32> s_relative_ata_controller_id;
static Atomic<u32> s_relative_ahci_controller_id;
static Atomic<u32> s_relative_nvme_controller_id;
static Atomic<u32> s_relative_sd_controller_id;
@ -51,7 +46,7 @@ static constexpr StringView partition_uuid_prefix = "PARTUUID:"sv;
static constexpr StringView partition_number_prefix = "part"sv;
static constexpr StringView block_device_prefix = "block"sv;
static constexpr StringView ata_device_prefix = "ata"sv;
static constexpr StringView ahci_device_prefix = "ahci"sv;
static constexpr StringView nvme_device_prefix = "nvme"sv;
static constexpr StringView logical_unit_number_device_prefix = "lun"sv;
static constexpr StringView sd_device_prefix = "sd"sv;
@ -67,10 +62,10 @@ u32 StorageManagement::generate_relative_nvme_controller_id(Badge<NVMeController
return controller_id;
}
u32 StorageManagement::generate_relative_ata_controller_id(Badge<ATAController>)
u32 StorageManagement::generate_relative_ahci_controller_id(Badge<AHCIController>)
{
auto controller_id = s_relative_ata_controller_id.load();
s_relative_ata_controller_id++;
auto controller_id = s_relative_ahci_controller_id.load();
s_relative_ahci_controller_id++;
return controller_id;
}
@ -93,7 +88,7 @@ void StorageManagement::remove_device(StorageDevice& device)
m_storage_devices.remove(device);
}
UNMAP_AFTER_INIT void StorageManagement::enumerate_pci_controllers(bool force_pio, bool nvme_poll)
UNMAP_AFTER_INIT void StorageManagement::enumerate_pci_controllers(bool nvme_poll)
{
VERIFY(m_controllers.is_empty());
@ -112,25 +107,6 @@ UNMAP_AFTER_INIT void StorageManagement::enumerate_pci_controllers(bool force_pi
using SubclassID = PCI::MassStorage::SubclassID;
auto subclass_code = static_cast<SubclassID>(device_identifier.subclass_code().value());
#if ARCH(X86_64)
if (subclass_code == SubclassID::IDEController && kernel_command_line().is_ide_enabled()) {
if (auto ide_controller_or_error = PCIIDELegacyModeController::initialize(device_identifier, force_pio); !ide_controller_or_error.is_error())
m_controllers.append(ide_controller_or_error.release_value());
else
dmesgln("Unable to initialize IDE controller: {}", ide_controller_or_error.error());
}
#elif ARCH(AARCH64)
(void)force_pio;
TODO_AARCH64();
#elif ARCH(RISCV64)
(void)force_pio;
if (subclass_code == SubclassID::IDEController && kernel_command_line().is_ide_enabled()) {
TODO_RISCV64();
}
#else
# error Unknown architecture
#endif
if (subclass_code == SubclassID::SATAController
&& device_identifier.prog_if() == PCI::MassStorage::SATAProgIF::AHCI) {
if (auto ahci_controller_or_error = AHCIController::initialize(device_identifier); !ahci_controller_or_error.is_error())
@ -326,7 +302,7 @@ UNMAP_AFTER_INIT void StorageManagement::determine_hardware_relative_boot_device
UNMAP_AFTER_INIT void StorageManagement::determine_ata_boot_device()
{
determine_hardware_relative_boot_device(ata_device_prefix, [](StorageDevice const& device) -> bool {
determine_hardware_relative_boot_device(ahci_device_prefix, [](StorageDevice const& device) -> bool {
return device.command_set() == StorageDevice::CommandSet::ATA;
});
}
@ -400,7 +376,7 @@ UNMAP_AFTER_INIT bool StorageManagement::determine_boot_device(StringView boot_a
return m_boot_block_device;
}
if (m_boot_argument.starts_with(ata_device_prefix)) {
if (m_boot_argument.starts_with(ahci_device_prefix)) {
determine_ata_boot_device();
return m_boot_block_device;
}
@ -481,18 +457,11 @@ NonnullRefPtr<FileSystem> StorageManagement::root_filesystem() const
return file_system;
}
UNMAP_AFTER_INIT void StorageManagement::initialize(bool force_pio, bool poll)
UNMAP_AFTER_INIT void StorageManagement::initialize(bool poll)
{
VERIFY(s_storage_device_minor_number == 0);
if (PCI::Access::is_disabled()) {
#if ARCH(X86_64)
// Note: If PCI is disabled, we assume that at least we have an ISA IDE controller
// to probe and use
auto isa_ide_controller = MUST(ISAIDEController::initialize());
m_controllers.append(isa_ide_controller);
#endif
} else {
enumerate_pci_controllers(force_pio, poll);
if (!PCI::Access::is_disabled()) {
enumerate_pci_controllers(poll);
}
#if ARCH(AARCH64)

View file

@ -18,13 +18,13 @@
namespace Kernel {
class ATAController;
class AHCIController;
class NVMeController;
class StorageManagement {
public:
StorageManagement();
void initialize(bool force_pio, bool nvme_poll);
void initialize(bool nvme_poll);
static StorageManagement& the();
bool determine_boot_device(StringView boot_argument);
@ -38,14 +38,14 @@ public:
static u32 generate_controller_id();
static u32 generate_relative_nvme_controller_id(Badge<NVMeController>);
static u32 generate_relative_ata_controller_id(Badge<ATAController>);
static u32 generate_relative_ahci_controller_id(Badge<AHCIController>);
static u32 generate_relative_sd_controller_id(Badge<SDHostController>);
void add_device(StorageDevice&);
void remove_device(StorageDevice&);
private:
void enumerate_pci_controllers(bool force_pio, bool nvme_poll);
void enumerate_pci_controllers(bool nvme_poll);
void enumerate_storage_devices();
ErrorOr<void> enumerate_device_partitions(StorageDevice&);
void enumerate_disk_partitions();

View file

@ -319,7 +319,6 @@ source_set("arch_sources") {
"Arch/x86_64/I8042Reboot.cpp",
"Arch/x86_64/ISABus/HID/VMWareMouseDevice.cpp",
"Arch/x86_64/ISABus/I8042Controller.cpp",
"Arch/x86_64/ISABus/IDEController.cpp",
"Arch/x86_64/ISABus/SerialDevice.cpp",
"Arch/x86_64/InterruptEntry.cpp",
"Arch/x86_64/InterruptManagement.cpp",
@ -328,7 +327,6 @@ source_set("arch_sources") {
"Arch/x86_64/Interrupts/IOAPIC.cpp",
"Arch/x86_64/Interrupts/PIC.cpp",
"Arch/x86_64/PCI/Controller/HostBridge.cpp",
"Arch/x86_64/PCI/IDELegacyModeController.cpp",
"Arch/x86_64/PCI/Initializer.cpp",
"Arch/x86_64/PCI/MSI.cpp",
"Arch/x86_64/PCSpeaker.cpp",
@ -556,15 +554,12 @@ executable("Kernel_bin") {
"Devices/KCOVInstance.cpp",
"Devices/PCISerialDevice.cpp",
"Devices/SerialDevice.cpp",
"Devices/Storage/ATA/AHCI/Controller.cpp",
"Devices/Storage/ATA/AHCI/InterruptHandler.cpp",
"Devices/Storage/ATA/AHCI/Port.cpp",
"Devices/Storage/ATA/ATAController.cpp",
"Devices/Storage/ATA/ATADevice.cpp",
"Devices/Storage/ATA/ATADiskDevice.cpp",
"Devices/Storage/ATA/ATAPort.cpp",
"Devices/Storage/ATA/GenericIDE/Channel.cpp",
"Devices/Storage/ATA/GenericIDE/Controller.cpp",
"Devices/Storage/AHCI/ATAController.cpp",
"Devices/Storage/AHCI/ATADevice.cpp",
"Devices/Storage/AHCI/ATADiskDevice.cpp",
"Devices/Storage/AHCI/Controller.cpp",
"Devices/Storage/AHCI/InterruptHandler.cpp",
"Devices/Storage/AHCI/Port.cpp",
"Devices/Storage/DiskPartition.cpp",
"Devices/Storage/NVMe/NVMeController.cpp",
"Devices/Storage/NVMe/NVMeInterruptQueue.cpp",