diff --git a/src/kernel/ahci/port.cpp b/src/kernel/ahci/port.cpp index 393d003..efa90ea 100644 --- a/src/kernel/ahci/port.cpp +++ b/src/kernel/ahci/port.cpp @@ -154,6 +154,10 @@ port::update() rebase(); m_pending.set_size(32); + for (auto &pend : m_pending) { + pend.type = command_type::none; + } + m_data->interrupt_enable = 1; } else { m_state = state::inactive; @@ -195,14 +199,18 @@ port::make_command(size_t length) int slot = -1; uint32_t used_slots = (m_data->serial_active | m_data->cmd_issue); for (int i = 0; i < 32; ++i) { - if ((used_slots & (1 << i)) == 0) { + if (used_slots & (1 << i)) continue; + + if (m_pending[i].type == command_type::none) { slot = i; break; + } else { + log::debug(logs::driver, "Type is %d", m_pending[i].type); } } if (slot < 0) { - log::info(logs::driver, "AHCI could not get a free command slot."); + log::error(logs::driver, "AHCI could not get a free command slot."); return -1; } @@ -240,12 +248,12 @@ port::make_command(size_t length) return slot; } -bool -port::read(uint64_t sector, size_t length, void *dest) +int +port::read_async(uint64_t offset, size_t length, void *dest) { int slot = make_command(length); if (slot < 0) - return false; + return 0; cmd_table &cmdt = m_cmd_table[slot]; @@ -255,6 +263,7 @@ port::read(uint64_t sector, size_t length, void *dest) fis->command = ata_cmd::read_dma_ext; fis->device = 0x40; // ATA8-ACS p.175 + uint64_t sector = offset >> 9; fis->lba0 = (sector ) & 0xff; fis->lba1 = (sector >> 8) & 0xff; fis->lba2 = (sector >> 16) & 0xff; @@ -270,8 +279,29 @@ port::read(uint64_t sector, size_t length, void *dest) count, sector, sector*512); m_pending[slot].type = command_type::read; + m_pending[slot].offset = offset % 512; + m_pending[slot].count = 0; m_pending[slot].data = dest; - return issue_command(slot); + if(issue_command(slot)) + return slot; + else + return -1; +} + +size_t +port::read(uint64_t offset, size_t length, void *dest) +{ + int slot = read_async(offset, length, dest); + while (m_pending[slot].type == command_type::read) + asm("hlt"); + kassert(m_pending[slot].type == command_type::finished, + "Read got unexpected command type"); + + size_t count = m_pending[slot].count; + m_pending[slot].type = command_type::none; + m_pending[slot].count = 0; + + return count; } bool @@ -322,8 +352,6 @@ port::handle_interrupt() default: break; } - p.type = command_type::none; - p.data = nullptr; } m_data->interrupt_status = m_data->interrupt_status; } @@ -335,7 +363,9 @@ port::finish_read(int slot) cmd_table &cmdt = m_cmd_table[slot]; cmd_list_entry &ent = m_cmd_list[slot]; + size_t count = 0; void *p = m_pending[slot].data; + uint8_t offset = m_pending[slot].offset; for (int i = 0; i < ent.prd_table_length; ++i) { size_t prd_len = (cmdt.entries[i].byte_count & 0x7fffffff) + 1; @@ -343,13 +373,18 @@ port::finish_read(int slot) static_cast(cmdt.entries[i].data_base_low) | static_cast(cmdt.entries[i].data_base_high) << 32; - void *mem = pm->offset_virt(phys); + void *mem = kutil::offset_pointer(pm->offset_virt(phys), offset); kutil::memcpy(p, mem, prd_len); - p = kutil::offset_pointer(p, prd_len); + p = kutil::offset_pointer(p, prd_len - offset); + count += (prd_len - offset); + offset = 0; pm->unmap_pages(mem, page_count(prd_len)); } + m_pending[slot].count = count; + m_pending[slot].type = command_type::finished; + m_pending[slot].data = nullptr; } void diff --git a/src/kernel/ahci/port.h b/src/kernel/ahci/port.h index 4b3b914..70841fb 100644 --- a/src/kernel/ahci/port.h +++ b/src/kernel/ahci/port.h @@ -4,6 +4,7 @@ #include #include #include "kutil/vector.h" +#include "block_device.h" namespace ahci { @@ -15,7 +16,8 @@ struct port_data; /// A port on an AHCI HBA -class port +class port : + public block_device { public: /// Constructor. @@ -53,12 +55,19 @@ public: /// Stop command processing from this port void stop_commands(); - /// Read data from the drive. - /// \arg sector Starting sector to read + /// Start a read operation from the drive. + /// \arg offset Offset to start from /// \arg length Number of bytes to read /// \arg dest A buffer where the data will be placed - /// \returns True if the command succeeded - bool read(uint64_t sector, size_t length, void *dest); + /// \returns A handle to the read operation, or -1 on error + int read_async(uint64_t offset, size_t length, void *dest); + + /// Read from the drive, blocking until finished. + /// \arg offset Offset to start from + /// \arg length Number of bytes to read + /// \arg dest A buffer where the data will be placed + /// \returns The number of bytes read + virtual size_t read(uint64_t offset, size_t length, void *dest); /// Handle an incoming interrupt void handle_interrupt(); @@ -97,11 +106,13 @@ private: cmd_list_entry *m_cmd_list; cmd_table *m_cmd_table; - enum class command_type : uint8_t { none, read, write }; + enum class command_type : uint8_t { none, read, write, finished }; struct pending { command_type type; + uint8_t offset; + size_t count; void *data; }; diff --git a/src/kernel/block_device.h b/src/kernel/block_device.h new file mode 100644 index 0000000..3a2ebfb --- /dev/null +++ b/src/kernel/block_device.h @@ -0,0 +1,10 @@ +#pragma once +/// \file block_device.h +/// Interface definition for block devices + +/// Interface for block devices +class block_device +{ +public: + virtual size_t read(size_t offset, size_t length, void *buffer) = 0; +}; diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index 781c1a9..6df8c0d 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -103,8 +103,7 @@ kernel_main(popcorn_data *header) uint8_t buf[512]; kutil::memset(buf, 0, 512); - disk->read(1, sizeof(buf), buf); - while (buf[0] == 0) io_wait(); + disk->read(0x200, sizeof(buf), buf); console *cons = console::get(); uint8_t *p = &buf[0];