IDELegacyModeController.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. /*
  2. * Copyright (c) 2020-2022, Liav A. <liavalb@hotmail.co.il>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/OwnPtr.h>
  7. #include <AK/Types.h>
  8. #include <Kernel/Arch/x86_64/PCI/IDELegacyModeController.h>
  9. #include <Kernel/Bus/PCI/API.h>
  10. #include <Kernel/Library/LockRefPtr.h>
  11. #include <Kernel/Sections.h>
  12. #include <Kernel/Storage/ATA/ATADiskDevice.h>
  13. #include <Kernel/Storage/ATA/GenericIDE/Channel.h>
  14. namespace Kernel {
  15. UNMAP_AFTER_INIT ErrorOr<NonnullLockRefPtr<PCIIDELegacyModeController>> PCIIDELegacyModeController::initialize(PCI::DeviceIdentifier const& device_identifier, bool force_pio)
  16. {
  17. auto controller = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) PCIIDELegacyModeController(device_identifier)));
  18. PCI::enable_io_space(device_identifier);
  19. PCI::enable_memory_space(device_identifier);
  20. PCI::enable_bus_mastering(device_identifier);
  21. ArmedScopeGuard disable_interrupts_on_failure([&] {
  22. controller->disable_pin_based_interrupts();
  23. });
  24. controller->enable_pin_based_interrupts();
  25. TRY(controller->initialize_and_enumerate_channels(force_pio));
  26. disable_interrupts_on_failure.disarm();
  27. return controller;
  28. }
  29. UNMAP_AFTER_INIT PCIIDELegacyModeController::PCIIDELegacyModeController(PCI::DeviceIdentifier const& device_identifier)
  30. : PCI::Device(const_cast<PCI::DeviceIdentifier&>(device_identifier))
  31. , m_prog_if(device_identifier.prog_if())
  32. , m_interrupt_line(device_identifier.interrupt_line())
  33. {
  34. }
  35. bool PCIIDELegacyModeController::is_pci_native_mode_enabled() const
  36. {
  37. return (m_prog_if.value() & 0x05) != 0;
  38. }
  39. bool PCIIDELegacyModeController::is_pci_native_mode_enabled_on_primary_channel() const
  40. {
  41. return (m_prog_if.value() & 0x1) == 0x1;
  42. }
  43. bool PCIIDELegacyModeController::is_pci_native_mode_enabled_on_secondary_channel() const
  44. {
  45. return (m_prog_if.value() & 0x4) == 0x4;
  46. }
  47. bool PCIIDELegacyModeController::is_bus_master_capable() const
  48. {
  49. return m_prog_if.value() & (1 << 7);
  50. }
  51. static char const* detect_controller_type(u8 programming_value)
  52. {
  53. switch (programming_value) {
  54. case 0x00:
  55. return "ISA Compatibility mode-only controller";
  56. case 0x05:
  57. return "PCI native mode-only controller";
  58. case 0x0A:
  59. return "ISA Compatibility mode controller, supports both channels switched to PCI native mode";
  60. case 0x0F:
  61. return "PCI native mode controller, supports both channels switched to ISA compatibility mode";
  62. case 0x80:
  63. return "ISA Compatibility mode-only controller, supports bus mastering";
  64. case 0x85:
  65. return "PCI native mode-only controller, supports bus mastering";
  66. case 0x8A:
  67. return "ISA Compatibility mode controller, supports both channels switched to PCI native mode, supports bus mastering";
  68. case 0x8F:
  69. return "PCI native mode controller, supports both channels switched to ISA compatibility mode, supports bus mastering";
  70. default:
  71. VERIFY_NOT_REACHED();
  72. }
  73. VERIFY_NOT_REACHED();
  74. }
  75. UNMAP_AFTER_INIT ErrorOr<void> PCIIDELegacyModeController::initialize_and_enumerate_channels(bool force_pio)
  76. {
  77. dbgln("IDE controller @ {}: interrupt line was set to {}", device_identifier().address(), m_interrupt_line.value());
  78. dbgln("IDE controller @ {}: {}", device_identifier().address(), detect_controller_type(m_prog_if.value()));
  79. {
  80. auto bus_master_base = IOAddress(PCI::get_BAR4(device_identifier()) & (~1));
  81. dbgln("IDE controller @ {}: bus master base was set to {}", device_identifier().address(), bus_master_base);
  82. }
  83. auto initialize_and_enumerate = [&force_pio](IDEChannel& channel) -> ErrorOr<void> {
  84. TRY(channel.allocate_resources_for_pci_ide_controller({}, force_pio));
  85. TRY(channel.detect_connected_devices());
  86. return {};
  87. };
  88. if (!is_bus_master_capable())
  89. force_pio = true;
  90. OwnPtr<IOWindow> primary_base_io_window;
  91. OwnPtr<IOWindow> primary_control_io_window;
  92. if (!is_pci_native_mode_enabled_on_primary_channel()) {
  93. primary_base_io_window = TRY(IOWindow::create_for_io_space(IOAddress(0x1F0), 8));
  94. primary_control_io_window = TRY(IOWindow::create_for_io_space(IOAddress(0x3F6), 4));
  95. } else {
  96. auto primary_base_io_window = TRY(IOWindow::create_for_pci_device_bar(device_identifier(), PCI::HeaderType0BaseRegister::BAR0));
  97. auto pci_primary_control_io_window = TRY(IOWindow::create_for_pci_device_bar(device_identifier(), PCI::HeaderType0BaseRegister::BAR1));
  98. // Note: the PCI IDE specification says we should access the IO address with an offset of 2
  99. // on native PCI IDE controllers.
  100. primary_control_io_window = TRY(pci_primary_control_io_window->create_from_io_window_with_offset(2, 4));
  101. }
  102. VERIFY(primary_base_io_window);
  103. VERIFY(primary_control_io_window);
  104. OwnPtr<IOWindow> secondary_base_io_window;
  105. OwnPtr<IOWindow> secondary_control_io_window;
  106. if (!is_pci_native_mode_enabled_on_primary_channel()) {
  107. secondary_base_io_window = TRY(IOWindow::create_for_io_space(IOAddress(0x170), 8));
  108. secondary_control_io_window = TRY(IOWindow::create_for_io_space(IOAddress(0x376), 4));
  109. } else {
  110. secondary_base_io_window = TRY(IOWindow::create_for_pci_device_bar(device_identifier(), PCI::HeaderType0BaseRegister::BAR2));
  111. auto pci_secondary_control_io_window = TRY(IOWindow::create_for_pci_device_bar(device_identifier(), PCI::HeaderType0BaseRegister::BAR3));
  112. // Note: the PCI IDE specification says we should access the IO address with an offset of 2
  113. // on native PCI IDE controllers.
  114. secondary_control_io_window = TRY(pci_secondary_control_io_window->create_from_io_window_with_offset(2, 4));
  115. }
  116. VERIFY(secondary_base_io_window);
  117. VERIFY(secondary_control_io_window);
  118. auto primary_bus_master_io = TRY(IOWindow::create_for_pci_device_bar(device_identifier(), PCI::HeaderType0BaseRegister::BAR4, 16));
  119. auto secondary_bus_master_io = TRY(primary_bus_master_io->create_from_io_window_with_offset(8));
  120. // FIXME: On IOAPIC based system, this value might be completely wrong
  121. // On QEMU for example, it should be "u8 irq_line = 22;" to actually work.
  122. auto irq_line = m_interrupt_line.value();
  123. if (is_pci_native_mode_enabled()) {
  124. VERIFY(irq_line != 0);
  125. }
  126. 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) };
  127. 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) };
  128. if (is_pci_native_mode_enabled_on_primary_channel()) {
  129. TRY(m_channels.try_append(IDEChannel::create(*this, irq_line, move(primary_channel_io_window_group), IDEChannel::ChannelType::Primary)));
  130. } else {
  131. TRY(m_channels.try_append(IDEChannel::create(*this, move(primary_channel_io_window_group), IDEChannel::ChannelType::Primary)));
  132. }
  133. TRY(initialize_and_enumerate(m_channels[0]));
  134. m_channels[0].enable_irq();
  135. if (is_pci_native_mode_enabled_on_secondary_channel()) {
  136. TRY(m_channels.try_append(IDEChannel::create(*this, irq_line, move(secondary_channel_io_window_group), IDEChannel::ChannelType::Secondary)));
  137. } else {
  138. TRY(m_channels.try_append(IDEChannel::create(*this, move(secondary_channel_io_window_group), IDEChannel::ChannelType::Secondary)));
  139. }
  140. TRY(initialize_and_enumerate(m_channels[1]));
  141. m_channels[1].enable_irq();
  142. return {};
  143. }
  144. }