summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLiav A <liavalb@gmail.com>2021-02-01 19:12:08 +0200
committerAndreas Kling <kling@serenityos.org>2021-02-03 10:18:16 +0100
commit36f6351edcee67963e80363f9f9f94a1d7db1919 (patch)
tree3e12d62508c668ad1cf46e5c06a5bfb1412b0858
parenta0c773da9ee77c7603e39c40524046b1b6c3c60d (diff)
downloadserenity-36f6351edcee67963e80363f9f9f94a1d7db1919.zip
Kernel: Restore IDE PIO functionality
This change can be actually seen as two logical changes, the first change is about to ensure we only read the ATA Status register only once, because if we read it multiple times, we acknowledge interrupts unintentionally. To solve this issue, we always use the alternate Status register and only read the original status register in the IRQ handler. The second change is how we handle interrupts - if we use DMA, we can just complete the request and return from the IRQ handler. For PIO mode, it's more complicated. For PIO write operation, after setting the ATA registers, we send out the data to IO port, and wait for an interrupt. For PIO read operation, we set the ATA registers, and wait for an interrupt to fire, then we just read from the data IO port.
-rw-r--r--Kernel/Storage/IDEChannel.cpp87
1 files changed, 41 insertions, 46 deletions
diff --git a/Kernel/Storage/IDEChannel.cpp b/Kernel/Storage/IDEChannel.cpp
index 6127124b9a..e7dc66846c 100644
--- a/Kernel/Storage/IDEChannel.cpp
+++ b/Kernel/Storage/IDEChannel.cpp
@@ -292,8 +292,6 @@ void IDEChannel::handle_irq(const RegisterState&)
return;
}
- bool received_all_irqs = m_current_request_uses_dma || m_current_request_block_index + 1 >= m_current_request->block_count();
-
if (status & ATA_SR_ERR) {
print_ide_status(status);
m_device_error = m_io_group.io_base().offset(ATA_REG_ERROR).in<u8>();
@@ -302,45 +300,44 @@ void IDEChannel::handle_irq(const RegisterState&)
complete_current_request(AsyncDeviceRequest::Failure);
return;
}
-
m_device_error = 0;
- if (received_all_irqs) {
+ if (m_current_request_uses_dma) {
complete_current_request(AsyncDeviceRequest::Success);
- } else {
- ASSERT(!m_current_request_uses_dma);
-
- // Now schedule reading/writing the buffer as soon as we leave the irq handler.
- // This is important so that we can safely access the buffers, which could
- // trigger page faults
- Processor::deferred_call_queue([this]() {
- if (m_current_request->request_type() == AsyncBlockDeviceRequest::Read) {
- dbgln("IDEChannel: Read block {}/{}", m_current_request_block_index, m_current_request->block_count());
- if (ata_do_read_sector()) {
- if (++m_current_request_block_index >= m_current_request->block_count()) {
- complete_current_request(AsyncDeviceRequest::Success);
- return;
- }
- // Wait for the next block
- enable_irq();
+ return;
+ }
+
+ // Now schedule reading/writing the buffer as soon as we leave the irq handler.
+ // This is important so that we can safely access the buffers, which could
+ // trigger page faults
+ Processor::deferred_call_queue([this]() {
+ ScopedSpinLock lock(m_request_lock);
+ if (m_current_request->request_type() == AsyncBlockDeviceRequest::Read) {
+ dbgln<PATA_DEBUG>("IDEChannel: Read block {}/{}", m_current_request_block_index, m_current_request->block_count());
+ if (ata_do_read_sector()) {
+ if (++m_current_request_block_index >= m_current_request->block_count()) {
+ complete_current_request(AsyncDeviceRequest::Success);
+ return;
}
- } else {
- if (!m_current_request_flushing_cache) {
- dbgln("IDEChannel: Wrote block {}/{}", m_current_request_block_index, m_current_request->block_count());
- if (++m_current_request_block_index >= m_current_request->block_count()) {
- // We read the last block, flush cache
- ASSERT(!m_current_request_flushing_cache);
- m_current_request_flushing_cache = true;
- m_io_group.io_base().offset(ATA_REG_COMMAND).out<u8>(ATA_CMD_CACHE_FLUSH);
- } else {
- // Read next block
- ata_do_write_sector();
- }
+ // Wait for the next block
+ enable_irq();
+ }
+ } else {
+ if (!m_current_request_flushing_cache) {
+ dbgln<PATA_DEBUG>("IDEChannel: Wrote block {}/{}", m_current_request_block_index, m_current_request->block_count());
+ if (++m_current_request_block_index >= m_current_request->block_count()) {
+ // We read the last block, flush cache
+ ASSERT(!m_current_request_flushing_cache);
+ m_current_request_flushing_cache = true;
+ m_io_group.io_base().offset(ATA_REG_COMMAND).out<u8>(ATA_CMD_CACHE_FLUSH);
} else {
- complete_current_request(AsyncDeviceRequest::Success);
+ // Read next block
+ ata_do_write_sector();
}
+ } else {
+ complete_current_request(AsyncDeviceRequest::Success);
}
- });
- }
+ }
+ });
}
static void io_delay()
@@ -351,7 +348,7 @@ static void io_delay()
void IDEChannel::wait_until_not_busy()
{
- while (m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>() & ATA_SR_BSY)
+ while (m_io_group.control_base().in<u8>() & ATA_SR_BSY)
;
}
@@ -379,10 +376,10 @@ void IDEChannel::detect_disks()
m_io_group.io_base().offset(ATA_REG_COMMAND).out<u8>(ATA_CMD_IDENTIFY); // Send the ATA_IDENTIFY command
// Wait for the BSY flag to be reset
- while (m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>() & ATA_SR_BSY)
+ while (m_io_group.control_base().in<u8>() & ATA_SR_BSY)
;
- if (m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>() == 0x00) {
+ if (m_io_group.control_base().in<u8>() == 0x00) {
dbgln<PATA_DEBUG>("IDEChannel: No {} {} disk detected!", channel_type_string().to_lowercase(), channel_string(i));
continue;
}
@@ -391,7 +388,7 @@ void IDEChannel::detect_disks()
PATADiskDevice::InterfaceType interface_type = PATADiskDevice::InterfaceType::ATA;
for (;;) {
- u8 status = m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>();
+ u8 status = m_io_group.control_base().in<u8>();
if (status & ATA_SR_ERR) {
dbgln<PATA_DEBUG>("IDEChannel: {} {} device is not ATA. Will check for ATAPI.", channel_type_string(), channel_string(i));
check_for_atapi = true;
@@ -499,7 +496,7 @@ void IDEChannel::ata_access(Direction direction, bool slave_request, u32 lba, u8
}
for (;;) {
- auto status = m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>();
+ auto status = m_io_group.control_base().in<u8>();
if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY))
break;
}
@@ -515,7 +512,6 @@ void IDEChannel::ata_access(Direction direction, bool slave_request, u32 lba, u8
else
m_io_group.io_base().offset(ATA_REG_COMMAND).out<u8>(direction == Direction::Read ? ATA_CMD_READ_PIO_EXT : ATA_CMD_WRITE_PIO_EXT);
}
-
enable_irq();
}
@@ -550,6 +546,7 @@ void IDEChannel::ata_read_sectors_with_dma(bool slave_request, u16 capabilities)
bool IDEChannel::ata_do_read_sector()
{
+ dbgln<PATA_DEBUG>("IDEChannel::ata_do_read_sector");
auto& request = *m_current_request;
auto out_buffer = request.buffer().offset(m_current_request_block_index * 512);
ssize_t nwritten = request.write_to_buffer_buffered<512>(out_buffer, 512, [&](u8* buffer, size_t buffer_bytes) {
@@ -576,7 +573,6 @@ void IDEChannel::ata_read_sectors(bool slave_request, u16 capabilities)
dbgln<PATA_DEBUG>("IDEChannel: Reading {} sector(s) @ LBA {}", request.block_count(), lba);
ata_access(Direction::Read, slave_request, lba, request.block_count(), capabilities, false);
- ata_do_read_sector();
}
void IDEChannel::ata_write_sectors_with_dma(bool slave_request, u16 capabilities)
@@ -615,10 +611,10 @@ void IDEChannel::ata_do_write_sector()
auto& request = *m_current_request;
io_delay();
- while ((m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>() & ATA_SR_BSY) || !(m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>() & ATA_SR_DRQ))
+ while ((m_io_group.control_base().in<u8>() & ATA_SR_BSY) || !(m_io_group.control_base().in<u8>() & ATA_SR_DRQ))
;
- u8 status = m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>();
+ u8 status = m_io_group.control_base().in<u8>();
ASSERT(status & ATA_SR_DRQ);
auto in_buffer = request.buffer().offset(m_current_request_block_index * 512);
@@ -642,8 +638,7 @@ void IDEChannel::ata_write_sectors(bool slave_request, u16 capabilities)
u32 count = request.block_count();
dbgln<PATA_DEBUG>("IDEChannel: Writing {} sector(s) @ LBA {}", count, start_sector);
- ata_access(Direction::Write, slave_request, start_sector, request.block_count(), capabilities, true);
+ ata_access(Direction::Write, slave_request, start_sector, request.block_count(), capabilities, false);
ata_do_write_sector();
}
-
}