Kernel: Rewrite the AHCI driver
There was a lot of stuff wrong with this driver and it was only getting speeds of ~2.5 MB/s. Now 4K blocks get ~17 MB/s which is not good but definitely better. I'm pretty sure the issue is qemu's emulation as 85% of time is spent between sending the command and it to complete. Large blocks are now supported too, qemu with block size 1M gets around 600 MB/s read speeds.
This commit is contained in:
@@ -22,6 +22,7 @@ namespace Kernel
|
||||
|
||||
virtual void handle_irq() override;
|
||||
|
||||
bool supports_64bit() const { return m_supports_64bit; }
|
||||
uint32_t command_slot_count() const { return m_command_slot_count; }
|
||||
|
||||
private:
|
||||
@@ -38,9 +39,8 @@ namespace Kernel
|
||||
|
||||
BAN::Array<AHCIDevice*, 32> m_devices;
|
||||
|
||||
bool m_supports_64bit { false };
|
||||
uint32_t m_command_slot_count { 0 };
|
||||
|
||||
friend class ATAController;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -32,8 +32,6 @@
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
static constexpr uint32_t s_hba_prdt_count { 8 };
|
||||
|
||||
struct FISRegisterH2D
|
||||
{
|
||||
uint8_t fis_type; // FIS_TYPE_REGISTER_H2D
|
||||
@@ -61,7 +59,8 @@ namespace Kernel
|
||||
uint8_t control;
|
||||
|
||||
uint8_t __reserved1[4];
|
||||
} __attribute__((packed));
|
||||
};
|
||||
static_assert(sizeof(FISRegisterH2D) == 20);
|
||||
|
||||
struct FISRegisterD2H
|
||||
{
|
||||
@@ -90,7 +89,8 @@ namespace Kernel
|
||||
uint8_t __reserved3[2];
|
||||
|
||||
uint8_t __reserved4[4];
|
||||
} __attribute__((packed));
|
||||
};
|
||||
static_assert(sizeof(FISRegisterD2H) == 20);
|
||||
|
||||
struct FISDataBI
|
||||
{
|
||||
@@ -102,7 +102,8 @@ namespace Kernel
|
||||
uint8_t __reserved1[2];
|
||||
|
||||
uint32_t data[0]; // Payload (1 - 2048 dwords)
|
||||
} __attribute__((packed));
|
||||
};
|
||||
static_assert(sizeof(FISDataBI) == 4);
|
||||
|
||||
struct SetDeviceBitsD2H
|
||||
{
|
||||
@@ -117,7 +118,8 @@ namespace Kernel
|
||||
uint8_t error;
|
||||
|
||||
uint32_t __reserved1;
|
||||
} __attribute__((packed));
|
||||
};
|
||||
static_assert(sizeof(SetDeviceBitsD2H) == 8);
|
||||
|
||||
struct PIOSetupD2H
|
||||
{
|
||||
@@ -149,7 +151,8 @@ namespace Kernel
|
||||
|
||||
uint16_t tc; // Transfer count
|
||||
uint8_t __reserved4[2];
|
||||
} __attribute__((packed));
|
||||
};
|
||||
static_assert(sizeof(PIOSetupD2H) == 20);
|
||||
|
||||
struct DMASetupBI
|
||||
{
|
||||
@@ -163,8 +166,8 @@ namespace Kernel
|
||||
|
||||
uint8_t __reserved1[2];
|
||||
|
||||
uint64_t dma_buffer_id; // DMA Buffer Identifier. Used to Identify DMA buffer in host memory.
|
||||
// SATA Spec says host specific and not in Spec. Trying AHCI spec might work.
|
||||
uint32_t dma_buffer_id_lo; // DMA Buffer Identifier. Used to Identify DMA buffer in host memory.
|
||||
uint32_t dma_buffer_id_hi; // SATA Spec says host specific and not in Spec. Trying AHCI spec might work.
|
||||
|
||||
uint32_t __reserved2;
|
||||
|
||||
@@ -173,7 +176,8 @@ namespace Kernel
|
||||
uint32_t dma_transfer_count; // Number of bytes to transfer. Bit 0 must be 0
|
||||
|
||||
uint32_t __reserved3;
|
||||
} __attribute__((packed));
|
||||
};
|
||||
static_assert(sizeof(DMASetupBI) == 28);
|
||||
|
||||
struct HBAPortMemorySpace
|
||||
{
|
||||
@@ -196,7 +200,8 @@ namespace Kernel
|
||||
uint32_t fbs; // FIS-based switch control
|
||||
uint32_t __reserved1[11];
|
||||
uint32_t vendor[4];
|
||||
} __attribute__((packed));
|
||||
};
|
||||
static_assert(sizeof(HBAPortMemorySpace) == 128);
|
||||
|
||||
struct HBAGeneralMemorySpace
|
||||
{
|
||||
@@ -217,7 +222,8 @@ namespace Kernel
|
||||
uint8_t vendor[0x100-0xA0];
|
||||
|
||||
HBAPortMemorySpace ports[0]; // 1 - 32 ports
|
||||
} __attribute__((packed));
|
||||
};
|
||||
static_assert(sizeof(HBAGeneralMemorySpace) == 256);
|
||||
|
||||
struct ReceivedFIS
|
||||
{
|
||||
@@ -235,7 +241,8 @@ namespace Kernel
|
||||
uint8_t ufis[64];
|
||||
|
||||
uint8_t __reserved[0x100-0xA0];
|
||||
} __attribute__((packed));
|
||||
};
|
||||
static_assert(sizeof(ReceivedFIS) == 256);
|
||||
|
||||
struct HBACommandHeader
|
||||
{
|
||||
@@ -252,13 +259,14 @@ namespace Kernel
|
||||
|
||||
uint16_t prdtl; // Physical region descriptor table length in entries
|
||||
|
||||
volatile uint32_t prdbc; // Physical region descriptor byte count transferred
|
||||
uint32_t prdbc; // Physical region descriptor byte count transferred
|
||||
|
||||
uint32_t ctba; // Command table descriptor base address
|
||||
uint32_t ctbau; // Command table descriptor base address upper 32 bits
|
||||
|
||||
uint32_t __reserved1[4];
|
||||
} __attribute__((packed));
|
||||
};
|
||||
static_assert(sizeof(HBACommandHeader) == 32);
|
||||
|
||||
struct HBAPRDTEntry
|
||||
{
|
||||
@@ -269,15 +277,17 @@ namespace Kernel
|
||||
uint32_t dbc : 22; // Byte count, 4M max
|
||||
uint32_t __reserved1 : 9;
|
||||
uint32_t i : 1; // Interrupt on completion
|
||||
} __attribute__((packed));
|
||||
};
|
||||
static_assert(sizeof(HBAPRDTEntry) == 16);
|
||||
|
||||
struct HBACommandTable
|
||||
{
|
||||
uint8_t cfis[64];
|
||||
uint8_t acmd[16];
|
||||
uint8_t __reserved[48];
|
||||
HBAPRDTEntry prdt_entry[s_hba_prdt_count];
|
||||
} __attribute__((packed));
|
||||
HBAPRDTEntry prdt_entry[0];
|
||||
};
|
||||
static_assert(sizeof(HBACommandTable) == 128);
|
||||
|
||||
enum class AHCIPortType
|
||||
{
|
||||
|
||||
@@ -20,30 +20,36 @@ namespace Kernel
|
||||
, m_port(port)
|
||||
{ }
|
||||
BAN::ErrorOr<void> initialize();
|
||||
BAN::ErrorOr<void> allocate_buffers();
|
||||
BAN::ErrorOr<void> rebase();
|
||||
BAN::ErrorOr<void> read_identify_data();
|
||||
|
||||
paddr_t read_paddr(volatile uint32_t& lo, volatile uint32_t& hi) const;
|
||||
void write_paddr(volatile uint32_t& lo, volatile uint32_t& hi, paddr_t paddr);
|
||||
|
||||
bool can_use_buffer_directly(BAN::ConstByteSpan buffer) const;
|
||||
|
||||
virtual BAN::ErrorOr<void> read_sectors_impl(uint64_t lba, uint64_t sector_count, BAN::ByteSpan) override;
|
||||
virtual BAN::ErrorOr<void> write_sectors_impl(uint64_t lba, uint64_t sector_count, BAN::ConstByteSpan) override;
|
||||
BAN::ErrorOr<void> send_command_and_block(uint64_t lba, uint64_t sector_count, paddr_t paddr, Command command);
|
||||
BAN::ErrorOr<size_t> send_command_sync(uint64_t lba, BAN::ConstByteSpan buffer, Command command);
|
||||
|
||||
uint32_t find_free_command_slot();
|
||||
BAN::ErrorOr<void> send_command_and_wait(uint32_t slot);
|
||||
BAN::ErrorOr<uint32_t> find_free_command_slot();
|
||||
|
||||
void handle_irq();
|
||||
|
||||
private:
|
||||
Mutex m_mutex;
|
||||
static constexpr uint32_t m_max_hba_prdt_count { 64 };
|
||||
|
||||
BAN::Atomic<uint32_t> m_prev_is { 0 };
|
||||
uint32_t m_free_slots { 0 };
|
||||
|
||||
BAN::RefPtr<AHCIController> m_controller;
|
||||
volatile HBAPortMemorySpace* const m_port;
|
||||
|
||||
BAN::UniqPtr<DMARegion> m_dma_region;
|
||||
// Intermediate read/write buffer
|
||||
// TODO: can we read straight to user buffer?
|
||||
BAN::UniqPtr<DMARegion> m_data_dma_region;
|
||||
|
||||
Mutex m_temp_buffer_mutex;
|
||||
BAN::UniqPtr<DMARegion> m_temp_buffer;
|
||||
|
||||
SpinLock m_command_lock;
|
||||
ThreadBlocker m_command_blocker;
|
||||
|
||||
Reference in New Issue
Block a user