forked from Bananymous/banan-os
update main #1
|
@ -53,6 +53,8 @@ set(KERNEL_SOURCES
|
|||
kernel/Semaphore.cpp
|
||||
kernel/SpinLock.cpp
|
||||
kernel/SSP.cpp
|
||||
kernel/Storage/ATA/AHCI/Controller.cpp
|
||||
kernel/Storage/ATA/AHCI/Device.cpp
|
||||
kernel/Storage/ATA/ATABus.cpp
|
||||
kernel/Storage/ATA/ATAController.cpp
|
||||
kernel/Storage/ATA/ATADevice.cpp
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
|
||||
#include <BAN/Array.h>
|
||||
#include <BAN/RefPtr.h>
|
||||
#include <kernel/InterruptController.h>
|
||||
#include <kernel/Memory/DMARegion.h>
|
||||
#include <kernel/PCI.h>
|
||||
#include <kernel/Storage/ATA/AHCI/Definitions.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class AHCIController final : public StorageController, public Interruptable
|
||||
{
|
||||
BAN_NON_COPYABLE(AHCIController);
|
||||
BAN_NON_MOVABLE(AHCIController);
|
||||
|
||||
public:
|
||||
~AHCIController();
|
||||
|
||||
virtual void handle_irq() override;
|
||||
|
||||
uint32_t command_slot_count() const { return m_command_slot_count; }
|
||||
|
||||
private:
|
||||
AHCIController(PCI::Device& pci_device)
|
||||
: m_pci_device(pci_device)
|
||||
{ }
|
||||
BAN::ErrorOr<void> initialize();
|
||||
BAN::Optional<AHCIPortType> check_port_type(volatile HBAPortMemorySpace&);
|
||||
|
||||
private:
|
||||
PCI::Device& m_pci_device;
|
||||
BAN::UniqPtr<PCI::BarRegion> m_abar;
|
||||
|
||||
BAN::Array<AHCIDevice*, 32> m_devices;
|
||||
|
||||
uint32_t m_command_slot_count { 0 };
|
||||
|
||||
friend class ATAController;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,294 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define FIS_TYPE_REGISTER_H2D 0x27
|
||||
#define FIS_TYPE_REGISTER_D2H 0x34
|
||||
#define FIS_TYPE_DMA_ACT 0x39
|
||||
#define FIS_TYPE_DMA_SETUP 0x41
|
||||
#define FIS_TYPE_DATA 0x46
|
||||
#define FIS_TYPE_BIST 0x58
|
||||
#define FIS_TYPE_PIO_SETUP 0x5F
|
||||
#define FIS_TYPE_SET_DEVIVE_BITS 0xA1
|
||||
|
||||
#define SATA_CAP_SUPPORTS64 (1 << 31)
|
||||
|
||||
#define SATA_GHC_AHCI_ENABLE (1 << 31)
|
||||
#define SATA_GHC_INTERRUPT_ENABLE (1 << 1)
|
||||
|
||||
#define SATA_SIG_ATA 0x00000101
|
||||
#define SATA_SIG_ATAPI 0xEB140101
|
||||
#define SATA_SIG_SEMB 0xC33C0101
|
||||
#define SATA_SIG_PM 0x96690101
|
||||
|
||||
#define HBA_PORT_IPM_ACTIVE 1
|
||||
#define HBA_PORT_DET_PRESENT 3
|
||||
|
||||
#define HBA_PxCMD_ST 0x0001
|
||||
#define HBA_PxCMD_FRE 0x0010
|
||||
#define HBA_PxCMD_FR 0x4000
|
||||
#define HBA_PxCMD_CR 0x8000
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
static constexpr uint32_t s_hba_prdt_count { 8 };
|
||||
|
||||
struct FISRegisterH2D
|
||||
{
|
||||
uint8_t fis_type; // FIS_TYPE_REGISTER_H2D
|
||||
|
||||
uint8_t pm_port : 4; // Port multiplier
|
||||
uint8_t __reserved0 : 3;
|
||||
uint8_t c : 1; // 1: Command, 0: Control
|
||||
|
||||
uint8_t command;
|
||||
uint8_t feature_lo; // Feature register, 7:0
|
||||
|
||||
uint8_t lba0; // LBA low register, 7:0
|
||||
uint8_t lba1; // LBA mid register, 15:8
|
||||
uint8_t lba2; // LBA high register, 23:16
|
||||
uint8_t device;
|
||||
|
||||
uint8_t lba3; // LBA register, 31:24
|
||||
uint8_t lba4; // LBA register, 39:32
|
||||
uint8_t lba5; // LBA register, 47:40
|
||||
uint8_t feature_hi; // Feature register, 15:8
|
||||
|
||||
uint8_t count_lo; // Count register, 7:0
|
||||
uint8_t count_hi; // Count register, 15:8
|
||||
uint8_t icc; // Isochronous command completion
|
||||
uint8_t control;
|
||||
|
||||
uint8_t __reserved1[4];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct FISRegisterD2H
|
||||
{
|
||||
uint8_t fis_type; // FIS_TYPE_REGISTER_D2H
|
||||
|
||||
uint8_t pm_port : 4; // Port multiplier
|
||||
uint8_t __reserved0 : 2;
|
||||
uint8_t i : 1; // Interrupt bit
|
||||
uint8_t __reserved1 : 1;
|
||||
|
||||
uint8_t status;
|
||||
uint8_t error;
|
||||
|
||||
uint8_t lba0; // LBA low register, 7:0
|
||||
uint8_t lba1; // LBA mid register, 15:8
|
||||
uint8_t lba2; // LBA high register, 23:16
|
||||
uint8_t device;
|
||||
|
||||
uint8_t lba3; // LBA register, 31:24
|
||||
uint8_t lba4; // LBA register, 39:32
|
||||
uint8_t lba5; // LBA register, 47:40
|
||||
uint8_t __reserved2;
|
||||
|
||||
uint8_t count_lo; // Count register, 7:0
|
||||
uint8_t count_hi; // Count register, 15:8
|
||||
uint8_t __reserved3[2];
|
||||
|
||||
uint8_t __reserved4[4];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct FISDataBI
|
||||
{
|
||||
uint8_t fis_type; // FIS_TYPE_DATA
|
||||
|
||||
uint8_t pm_port : 4; // Port multiplier
|
||||
uint8_t __reserved0 : 4;
|
||||
|
||||
uint8_t __reserved1[2];
|
||||
|
||||
uint32_t data[0]; // Payload (1 - 2048 dwords)
|
||||
} __attribute__((packed));
|
||||
|
||||
struct SetDeviceBitsD2H
|
||||
{
|
||||
uint8_t fis_type; // FIS_TYPE_SET_DEVICE_BITS
|
||||
|
||||
uint8_t pm_port : 4; // Port multiplier
|
||||
uint8_t __reserved0 : 2;
|
||||
uint8_t i : 1; // Interrupt bit
|
||||
uint8_t n : 1; // Notification bit
|
||||
|
||||
uint8_t status;
|
||||
uint8_t error;
|
||||
|
||||
uint32_t __reserved1;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct PIOSetupD2H
|
||||
{
|
||||
uint8_t fis_type; // FIS_TYPE_PIO_SETUP
|
||||
|
||||
uint8_t pm_port : 4; // Port multiplier
|
||||
uint8_t __reserved0 : 1;
|
||||
uint8_t d : 1; // Data transfer direction, 1 - device to host
|
||||
uint8_t i : 1; // Interrupt bit
|
||||
uint8_t __reserved1 : 1;
|
||||
|
||||
uint8_t status;
|
||||
uint8_t error;
|
||||
|
||||
uint8_t lba0; // LBA low register, 7:0
|
||||
uint8_t lba1; // LBA mid register, 15:8
|
||||
uint8_t lba2; // LBA high register, 23:16
|
||||
uint8_t device;
|
||||
|
||||
uint8_t lba3; // LBA register, 31:24
|
||||
uint8_t lba4; // LBA register, 39:32
|
||||
uint8_t lba5; // LBA register, 47:40
|
||||
uint8_t __reserved2;
|
||||
|
||||
uint8_t count_lo; // Count register, 7:0
|
||||
uint8_t count_hi; // Count register, 15:8
|
||||
uint8_t __reserved3;
|
||||
uint8_t e_status; // New value of status register
|
||||
|
||||
uint16_t tc; // Transfer count
|
||||
uint8_t __reserved4[2];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct DMASetupBI
|
||||
{
|
||||
uint8_t fis_type; // FIS_TYPE_DMA_SETUP
|
||||
|
||||
uint8_t pm_port : 4; // Port multiplier
|
||||
uint8_t __reserved0 : 1;
|
||||
uint8_t d : 1; // Data transfer direction, 1 - device to host
|
||||
uint8_t i : 1; // Interrupt bit
|
||||
uint8_t a : 1; // Auto-activate. Specifies if DMA Activate FIS is needed
|
||||
|
||||
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 __reserved2;
|
||||
|
||||
uint32_t dma_buffer_offset; // Byte offset into buffer. First 2 bits must be 0
|
||||
|
||||
uint32_t dma_transfer_count; // Number of bytes to transfer. Bit 0 must be 0
|
||||
|
||||
uint32_t __reserved3;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct HBAPortMemorySpace
|
||||
{
|
||||
uint32_t clb; // command list base address, 1K-byte aligned
|
||||
uint32_t clbu; // command list base address upper 32 bits
|
||||
uint32_t fb; // FIS base address, 256-byte aligned
|
||||
uint32_t fbu; // FIS base address upper 32 bits
|
||||
uint32_t is; // interrupt status
|
||||
uint32_t ie; // interrupt enable
|
||||
uint32_t cmd; // command and status
|
||||
uint32_t __reserved0;
|
||||
uint32_t tfd; // task file data
|
||||
uint32_t sig; // signature
|
||||
uint32_t ssts; // SATA status (SCR0:SStatus)
|
||||
uint32_t sctl; // SATA control (SCR2:SControl)
|
||||
uint32_t serr; // SATA error (SCR1:SError)
|
||||
uint32_t sact; // SATA active (SCR3:SActive)
|
||||
uint32_t ci; // command issue
|
||||
uint32_t sntf; // SATA notification (SCR4:SNotification)
|
||||
uint32_t fbs; // FIS-based switch control
|
||||
uint32_t __reserved1[11];
|
||||
uint32_t vendor[4];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct HBAGeneralMemorySpace
|
||||
{
|
||||
uint32_t cap; // Host capability
|
||||
uint32_t ghc; // Global host control
|
||||
uint32_t is; // Interrupt status
|
||||
uint32_t pi; // Port implemented
|
||||
uint32_t vs; // Version
|
||||
uint32_t ccc_ctl; // Command completion coalescing control
|
||||
uint32_t ccc_pts; // Command completion coalescing ports
|
||||
uint32_t em_loc; // 0x1C, Enclosure management location
|
||||
uint32_t em_ctl; // 0x20, Enclosure management control
|
||||
uint32_t cap2; // 0x24, Host capabilities extended
|
||||
uint32_t bohc; // 0x28, BIOS/OS handoff control and status
|
||||
|
||||
uint8_t __reserved0[0xA0-0x2C];
|
||||
|
||||
uint8_t vendor[0x100-0xA0];
|
||||
|
||||
HBAPortMemorySpace ports[0]; // 1 - 32 ports
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ReceivedFIS
|
||||
{
|
||||
DMASetupBI dsfis;
|
||||
uint8_t pad0[4];
|
||||
|
||||
PIOSetupD2H psfis;
|
||||
uint8_t pad1[12];
|
||||
|
||||
FISRegisterD2H rfis;
|
||||
uint8_t pad2[4];
|
||||
|
||||
SetDeviceBitsD2H sdbfis;
|
||||
|
||||
uint8_t ufis[64];
|
||||
|
||||
uint8_t __reserved[0x100-0xA0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct HBACommandHeader
|
||||
{
|
||||
uint8_t cfl : 5; // Command FIS length in DWORDS, 2 ~ 16
|
||||
uint8_t a : 1; // ATAPI
|
||||
uint8_t w : 1; // Write, 1: H2D, 0: D2H
|
||||
uint8_t p : 1; // Prefetchable
|
||||
|
||||
uint8_t r : 1; // Reset
|
||||
uint8_t b : 1; // BIST
|
||||
uint8_t c : 1; // Clear busy upon R_OK
|
||||
uint8_t __reserved0 : 1;
|
||||
uint8_t pmp : 4; // Port multiplier port
|
||||
|
||||
uint16_t prdtl; // Physical region descriptor table length in entries
|
||||
|
||||
volatile 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));
|
||||
|
||||
struct HBAPRDTEntry
|
||||
{
|
||||
uint32_t dba; // Data base address
|
||||
uint32_t dbau; // Data base address upper 32 bits
|
||||
uint32_t __reserved0;
|
||||
|
||||
uint32_t dbc : 22; // Byte count, 4M max
|
||||
uint32_t __reserved1 : 9;
|
||||
uint32_t i : 1; // Interrupt on completion
|
||||
} __attribute__((packed));
|
||||
|
||||
struct HBACommandTable
|
||||
{
|
||||
uint8_t cfis[64];
|
||||
uint8_t acmd[16];
|
||||
uint8_t __reserved[48];
|
||||
HBAPRDTEntry prdt_entry[s_hba_prdt_count];
|
||||
} __attribute__((packed));
|
||||
|
||||
enum class AHCIPortType
|
||||
{
|
||||
NONE,
|
||||
SATA,
|
||||
SATAPI,
|
||||
SEMB,
|
||||
PM
|
||||
};
|
||||
|
||||
class AHCIController;
|
||||
class AHCIDevice;
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
|
||||
#include <kernel/Semaphore.h>
|
||||
#include <kernel/Storage/ATA/AHCI/Definitions.h>
|
||||
#include <kernel/Storage/ATA/ATADevice.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class AHCIDevice final : public detail::ATABaseDevice
|
||||
{
|
||||
public:
|
||||
static BAN::ErrorOr<BAN::RefPtr<AHCIDevice>> create(BAN::RefPtr<AHCIController>, volatile HBAPortMemorySpace*);
|
||||
~AHCIDevice() = default;
|
||||
|
||||
private:
|
||||
AHCIDevice(BAN::RefPtr<AHCIController> controller, volatile HBAPortMemorySpace* port)
|
||||
: m_controller(controller)
|
||||
, m_port(port)
|
||||
{ }
|
||||
BAN::ErrorOr<void> initialize();
|
||||
BAN::ErrorOr<void> allocate_buffers();
|
||||
BAN::ErrorOr<void> rebase();
|
||||
BAN::ErrorOr<void> read_identify_data();
|
||||
|
||||
virtual BAN::ErrorOr<void> read_sectors_impl(uint64_t lba, uint64_t sector_count, uint8_t* buffer) override;
|
||||
virtual BAN::ErrorOr<void> write_sectors_impl(uint64_t lba, uint64_t sector_count, const uint8_t* buffer) override;
|
||||
BAN::ErrorOr<void> send_command_and_block(uint64_t lba, uint64_t sector_count, Command command);
|
||||
|
||||
BAN::Optional<uint32_t> find_free_command_slot();
|
||||
|
||||
void handle_irq();
|
||||
void block_until_irq();
|
||||
|
||||
private:
|
||||
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;
|
||||
|
||||
volatile bool m_has_got_irq { false };
|
||||
|
||||
friend class AHCIController;
|
||||
};
|
||||
|
||||
}
|
|
@ -33,7 +33,11 @@
|
|||
#define ATA_STATUS_BSY 0x80
|
||||
|
||||
#define ATA_COMMAND_READ_SECTORS 0x20
|
||||
#define ATA_COMMAND_READ_DMA 0xC8
|
||||
#define ATA_COMMAND_READ_DMA_EXT 0x25
|
||||
#define ATA_COMMAND_WRITE_SECTORS 0x30
|
||||
#define ATA_COMMAND_WRITE_DMA 0xCA
|
||||
#define ATA_COMMAND_WRITE_DMA_EXT 0x35
|
||||
#define ATA_COMMAND_IDENTIFY_PACKET 0xA1
|
||||
#define ATA_COMMAND_CACHE_FLUSH 0xE7
|
||||
#define ATA_COMMAND_IDENTIFY 0xEC
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
#include <kernel/Memory/Heap.h>
|
||||
#include <kernel/Memory/PageTable.h>
|
||||
#include <kernel/Storage/ATA/AHCI/Controller.h>
|
||||
#include <kernel/Storage/ATA/AHCI/Definitions.h>
|
||||
#include <kernel/Storage/ATA/AHCI/Device.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
BAN::ErrorOr<void> AHCIController::initialize()
|
||||
{
|
||||
m_abar = TRY(m_pci_device.allocate_bar_region(5));
|
||||
if (m_abar->type() != PCI::BarType::MEM)
|
||||
{
|
||||
dprintln("ABAR not MMIO");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
auto& abar_mem = *(volatile HBAGeneralMemorySpace*)m_abar->vaddr();
|
||||
if (!(abar_mem.ghc & SATA_GHC_AHCI_ENABLE))
|
||||
{
|
||||
dprintln("Controller not in AHCI mode");
|
||||
return BAN::Error::from_errno(EINVAL);
|
||||
}
|
||||
|
||||
// Enable interrupts and bus mastering
|
||||
m_pci_device.enable_bus_mastering();
|
||||
m_pci_device.enable_pin_interrupts();
|
||||
set_irq(TRY(m_pci_device.get_irq()));
|
||||
enable_interrupt();
|
||||
abar_mem.ghc = abar_mem.ghc | SATA_GHC_INTERRUPT_ENABLE;
|
||||
|
||||
m_command_slot_count = ((abar_mem.cap >> 8) & 0x1F) + 1;
|
||||
|
||||
uint32_t pi = abar_mem.pi;
|
||||
for (uint32_t i = 0; i < 32 && pi; i++, pi >>= 1)
|
||||
{
|
||||
// Verify we don't access abar outside of its bounds
|
||||
if (sizeof(HBAGeneralMemorySpace) + i * sizeof(HBAPortMemorySpace) > m_abar->size())
|
||||
break;
|
||||
|
||||
if (!(pi & 1))
|
||||
continue;
|
||||
|
||||
auto type = check_port_type(abar_mem.ports[i]);
|
||||
if (!type.has_value())
|
||||
continue;
|
||||
|
||||
if (type.value() != AHCIPortType::SATA)
|
||||
{
|
||||
dprintln("Non-SATA devices not supported");
|
||||
continue;
|
||||
}
|
||||
|
||||
auto device = AHCIDevice::create(this, &abar_mem.ports[i]);
|
||||
if (device.is_error())
|
||||
{
|
||||
dprintln("{}", device.error());
|
||||
continue;
|
||||
}
|
||||
|
||||
m_devices[i] = device.value().ptr();
|
||||
if (auto ret = m_devices[i]->initialize(); ret.is_error())
|
||||
{
|
||||
dprintln("{}", ret.error());
|
||||
m_devices[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
AHCIController::~AHCIController()
|
||||
{
|
||||
}
|
||||
|
||||
void AHCIController::handle_irq()
|
||||
{
|
||||
auto& abar_mem = *(volatile HBAGeneralMemorySpace*)m_abar->vaddr();
|
||||
|
||||
uint32_t is = abar_mem.is;
|
||||
for (uint8_t i = 0; i < 32; i++)
|
||||
{
|
||||
if (is & (1 << i))
|
||||
{
|
||||
ASSERT(m_devices[i]);
|
||||
m_devices[i]->handle_irq();
|
||||
}
|
||||
}
|
||||
|
||||
abar_mem.is = is;
|
||||
}
|
||||
|
||||
BAN::Optional<AHCIPortType> AHCIController::check_port_type(volatile HBAPortMemorySpace& port)
|
||||
{
|
||||
uint32_t ssts = port.ssts;
|
||||
uint8_t ipm = (ssts >> 8) & 0x0F;
|
||||
uint8_t det = (ssts >> 0) & 0x0F;
|
||||
|
||||
if (det != HBA_PORT_DET_PRESENT)
|
||||
return {};
|
||||
if (ipm != HBA_PORT_IPM_ACTIVE)
|
||||
return {};
|
||||
|
||||
switch (port.sig)
|
||||
{
|
||||
case SATA_SIG_ATA:
|
||||
return AHCIPortType::SATA;
|
||||
case SATA_SIG_ATAPI:
|
||||
return AHCIPortType::SATAPI;
|
||||
case SATA_SIG_PM:
|
||||
return AHCIPortType::PM;
|
||||
case SATA_SIG_SEMB:
|
||||
return AHCIPortType::SEMB;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,276 @@
|
|||
#include <kernel/Storage/ATA/AHCI/Controller.h>
|
||||
#include <kernel/Storage/ATA/AHCI/Device.h>
|
||||
#include <kernel/Storage/ATA/ATADefinitions.h>
|
||||
#include <kernel/Thread.h>
|
||||
#include <kernel/Timer/Timer.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
static void start_cmd(volatile HBAPortMemorySpace* port)
|
||||
{
|
||||
while (port->cmd & HBA_PxCMD_CR)
|
||||
continue;
|
||||
port->cmd = port->cmd | HBA_PxCMD_FRE;
|
||||
port->cmd = port->cmd | HBA_PxCMD_ST;
|
||||
}
|
||||
|
||||
static void stop_cmd(volatile HBAPortMemorySpace* port)
|
||||
{
|
||||
port->cmd = port->cmd & ~HBA_PxCMD_ST;
|
||||
port->cmd = port->cmd & ~HBA_PxCMD_FRE;
|
||||
while (port->cmd & (HBA_PxCMD_FR | HBA_PxCMD_CR))
|
||||
continue;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<BAN::RefPtr<AHCIDevice>> AHCIDevice::create(BAN::RefPtr<AHCIController> controller, volatile HBAPortMemorySpace* port)
|
||||
{
|
||||
auto* device_ptr = new AHCIDevice(controller, port);
|
||||
if (device_ptr == nullptr)
|
||||
return BAN::Error::from_errno(ENOMEM);
|
||||
return BAN::RefPtr<AHCIDevice>::adopt(device_ptr);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> AHCIDevice::initialize()
|
||||
{
|
||||
TRY(allocate_buffers());
|
||||
TRY(rebase());
|
||||
|
||||
// enable interrupts
|
||||
m_port->ie = 0xFFFFFFFF;
|
||||
|
||||
TRY(read_identify_data());
|
||||
TRY(detail::ATABaseDevice::initialize({ (const uint16_t*)m_data_dma_region->vaddr(), m_data_dma_region->size() }));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> AHCIDevice::allocate_buffers()
|
||||
{
|
||||
uint32_t command_slot_count = m_controller->command_slot_count();
|
||||
size_t needed_bytes = (sizeof(HBACommandHeader) + sizeof(HBACommandTable)) * command_slot_count + sizeof(ReceivedFIS);
|
||||
|
||||
m_dma_region = TRY(DMARegion::create(needed_bytes));
|
||||
memset((void*)m_dma_region->vaddr(), 0x00, m_dma_region->size());
|
||||
|
||||
m_data_dma_region = TRY(DMARegion::create(PAGE_SIZE));
|
||||
memset((void*)m_data_dma_region->vaddr(), 0x00, m_data_dma_region->size());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> AHCIDevice::rebase()
|
||||
{
|
||||
ASSERT(m_dma_region);
|
||||
|
||||
uint32_t command_slot_count = m_controller->command_slot_count();
|
||||
|
||||
stop_cmd(m_port);
|
||||
|
||||
paddr_t fis_paddr = m_dma_region->paddr();
|
||||
m_port->fb = fis_paddr & 0xFFFFFFFF;
|
||||
m_port->fbu = fis_paddr >> 32;
|
||||
|
||||
paddr_t command_list_paddr = fis_paddr + sizeof(ReceivedFIS);
|
||||
m_port->clb = command_list_paddr & 0xFFFFFFFF;
|
||||
m_port->clbu = command_list_paddr >> 32;
|
||||
|
||||
auto* command_headers = (HBACommandHeader*)m_dma_region->paddr_to_vaddr(command_list_paddr);
|
||||
paddr_t command_table_paddr = command_list_paddr + command_slot_count * sizeof(HBACommandHeader);
|
||||
for (uint32_t i = 0; i < command_slot_count; i++)
|
||||
{
|
||||
uint64_t command_table_entry_paddr = command_table_paddr + i * sizeof(HBACommandTable);
|
||||
command_headers[i].prdtl = s_hba_prdt_count;
|
||||
command_headers[i].ctba = command_table_entry_paddr & 0xFFFFFFFF;
|
||||
command_headers[i].ctbau = command_table_entry_paddr >> 32;
|
||||
}
|
||||
|
||||
start_cmd(m_port);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> AHCIDevice::read_identify_data()
|
||||
{
|
||||
ASSERT(m_data_dma_region);
|
||||
|
||||
m_port->is = ~(uint32_t)0;
|
||||
|
||||
auto slot = find_free_command_slot();
|
||||
ASSERT(slot.has_value());
|
||||
|
||||
auto& command_header = ((HBACommandHeader*)m_dma_region->paddr_to_vaddr(m_port->clb))[slot.value()];
|
||||
command_header.cfl = sizeof(FISRegisterH2D) / sizeof(uint32_t);
|
||||
command_header.w = 0;
|
||||
command_header.prdtl = 1;
|
||||
|
||||
auto& command_table = *(HBACommandTable*)m_dma_region->paddr_to_vaddr(command_header.ctba);
|
||||
memset(&command_table, 0x00, sizeof(HBACommandTable));
|
||||
command_table.prdt_entry[0].dba = m_data_dma_region->paddr();
|
||||
command_table.prdt_entry[0].dbc = 511;
|
||||
command_table.prdt_entry[0].i = 1;
|
||||
|
||||
auto& command = *(FISRegisterH2D*)command_table.cfis;
|
||||
command.fis_type = FIS_TYPE_REGISTER_H2D;
|
||||
command.c = 1;
|
||||
command.command = ATA_COMMAND_IDENTIFY;
|
||||
|
||||
while (m_port->tfd & (ATA_STATUS_BSY | ATA_STATUS_DRQ))
|
||||
continue;
|
||||
|
||||
m_port->ci = 1 << slot.value();
|
||||
|
||||
// FIXME: timeout
|
||||
do { block_until_irq(); } while (m_port->ci & (1 << slot.value()));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static void print_error(uint16_t error)
|
||||
{
|
||||
dprintln("Disk error:");
|
||||
if (error & (1 << 11))
|
||||
dprintln(" Internal Error");
|
||||
if (error & (1 << 10))
|
||||
dprintln(" Protocol Error");
|
||||
if (error & (1 << 9))
|
||||
dprintln(" Persistent Communication or Data Integrity Error");
|
||||
if (error & (1 << 8))
|
||||
dprintln(" Transient Data Integrity Error");
|
||||
if (error & (1 << 1))
|
||||
dprintln(" Recovered Communications Error");
|
||||
if (error & (1 << 0))
|
||||
dprintln(" Recovered Data Integrity Error");
|
||||
}
|
||||
|
||||
void AHCIDevice::handle_irq()
|
||||
{
|
||||
ASSERT(!m_has_got_irq);
|
||||
uint16_t err = m_port->serr & 0xFFFF;
|
||||
if (err)
|
||||
print_error(err);
|
||||
m_has_got_irq = true;
|
||||
}
|
||||
|
||||
void AHCIDevice::block_until_irq()
|
||||
{
|
||||
while (!__sync_bool_compare_and_swap(&m_has_got_irq, true, false))
|
||||
__builtin_ia32_pause();
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> AHCIDevice::read_sectors_impl(uint64_t lba, uint64_t sector_count, uint8_t* buffer)
|
||||
{
|
||||
const size_t sectors_per_page = PAGE_SIZE / sector_size();
|
||||
for (uint64_t sector_off = 0; sector_off < sector_count; sector_off += sectors_per_page)
|
||||
{
|
||||
uint64_t to_read = BAN::Math::min<uint64_t>(sector_count - sector_off, sectors_per_page);
|
||||
|
||||
TRY(send_command_and_block(lba + sector_off, to_read, Command::Read));
|
||||
memcpy(buffer + sector_off * sector_size(), (void*)m_data_dma_region->vaddr(), to_read * sector_size());
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> AHCIDevice::write_sectors_impl(uint64_t lba, uint64_t sector_count, const uint8_t* buffer)
|
||||
{
|
||||
const size_t sectors_per_page = PAGE_SIZE / sector_size();
|
||||
for (uint64_t sector_off = 0; sector_off < sector_count; sector_off += sectors_per_page)
|
||||
{
|
||||
uint64_t to_read = BAN::Math::min<uint64_t>(sector_count - sector_off, sectors_per_page);
|
||||
|
||||
memcpy((void*)m_data_dma_region->vaddr(), buffer + sector_off * sector_size(), to_read * sector_size());
|
||||
TRY(send_command_and_block(lba + sector_off, to_read, Command::Write));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> AHCIDevice::send_command_and_block(uint64_t lba, uint64_t sector_count, Command command)
|
||||
{
|
||||
ASSERT(m_dma_region);
|
||||
ASSERT(m_data_dma_region);
|
||||
ASSERT(0 < sector_count && sector_count <= 0xFFFF + 1);
|
||||
|
||||
ASSERT(sector_count * sector_size() <= m_data_dma_region->size());
|
||||
|
||||
m_port->is = ~(uint32_t)0;
|
||||
|
||||
auto slot = find_free_command_slot();
|
||||
ASSERT(slot.has_value());
|
||||
|
||||
auto& command_header = ((HBACommandHeader*)m_dma_region->paddr_to_vaddr(m_port->clb))[slot.value()];
|
||||
command_header.cfl = sizeof(FISRegisterH2D) / sizeof(uint32_t);
|
||||
command_header.prdtl = 1;
|
||||
switch (command)
|
||||
{
|
||||
case Command::Read:
|
||||
command_header.w = 0;
|
||||
break;
|
||||
case Command::Write:
|
||||
command_header.w = 1;
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
auto& command_table = *(HBACommandTable*)m_dma_region->paddr_to_vaddr(command_header.ctba);
|
||||
memset(&command_table, 0x00, sizeof(HBACommandTable));
|
||||
command_table.prdt_entry[0].dba = m_data_dma_region->paddr() & 0xFFFFFFFF;
|
||||
command_table.prdt_entry[0].dbau = m_data_dma_region->paddr() >> 32;
|
||||
command_table.prdt_entry[0].dbc = sector_count * sector_size() - 1;
|
||||
command_table.prdt_entry[0].i = 1;
|
||||
|
||||
auto& fis_command = *(FISRegisterH2D*)command_table.cfis;
|
||||
memset(&fis_command, 0x00, sizeof(FISRegisterH2D));
|
||||
fis_command.fis_type = FIS_TYPE_REGISTER_H2D;
|
||||
fis_command.c = 1;
|
||||
|
||||
bool need_extended = lba >= (1 << 28) || sector_count > 0xFF;
|
||||
ASSERT (!need_extended || (m_command_set & ATA_COMMANDSET_LBA48_SUPPORTED));
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case Command::Read:
|
||||
fis_command.command = need_extended ? ATA_COMMAND_READ_DMA_EXT : ATA_COMMAND_READ_DMA;
|
||||
break;
|
||||
case Command::Write:
|
||||
fis_command.command = need_extended ? ATA_COMMAND_WRITE_DMA_EXT : ATA_COMMAND_WRITE_DMA;
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
fis_command.lba0 = (lba >> 0) & 0xFF;
|
||||
fis_command.lba1 = (lba >> 8) & 0xFF;
|
||||
fis_command.lba2 = (lba >> 16) & 0xFF;
|
||||
fis_command.device = 1 << 6; // LBA mode
|
||||
|
||||
fis_command.lba3 = (lba >> 24) & 0xFF;
|
||||
fis_command.lba4 = (lba >> 32) & 0xFF;
|
||||
fis_command.lba5 = (lba >> 40) & 0xFF;
|
||||
|
||||
fis_command.count_lo = (sector_count >> 0) & 0xFF;
|
||||
fis_command.count_hi = (sector_count >> 8) & 0xFF;
|
||||
|
||||
while (m_port->tfd & (ATA_STATUS_BSY | ATA_STATUS_DRQ))
|
||||
continue;
|
||||
|
||||
m_port->ci = 1 << slot.value();
|
||||
|
||||
// FIXME: timeout
|
||||
do { block_until_irq(); } while (m_port->ci & (1 << slot.value()));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
BAN::Optional<uint32_t> AHCIDevice::find_free_command_slot()
|
||||
{
|
||||
uint32_t slots = m_port->sact | m_port->ci;
|
||||
for (uint32_t i = 0; i < m_controller->command_slot_count(); i++, slots >>= 1)
|
||||
if (!(slots & 1))
|
||||
return i;
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
#include <kernel/FS/DevFS/FileSystem.h>
|
||||
#include <kernel/Storage/ATA/AHCI/Controller.h>
|
||||
#include <kernel/Storage/ATA/ATABus.h>
|
||||
#include <kernel/Storage/ATA/ATAController.h>
|
||||
#include <kernel/Storage/ATA/ATADefinitions.h>
|
||||
|
@ -20,8 +21,8 @@ namespace Kernel
|
|||
dwarnln("unsupported DMA ATA Controller");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
case 0x06:
|
||||
dwarnln("unsupported SATA Controller");
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
controller_ptr = new AHCIController(pci_device);
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
|
12
qemu.sh
12
qemu.sh
|
@ -1,8 +1,10 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
qemu-system-$BANAN_ARCH \
|
||||
-m 128 \
|
||||
-smp 2 \
|
||||
-drive format=raw,media=disk,file=${DISK_IMAGE_PATH} \
|
||||
$@ \
|
||||
qemu-system-$BANAN_ARCH \
|
||||
-m 128 \
|
||||
-smp 2 \
|
||||
-drive format=raw,id=disk,file=${DISK_IMAGE_PATH},if=none \
|
||||
-device ahci,id=ahci \
|
||||
-device ide-hd,drive=disk,bus=ahci.0 \
|
||||
$@ \
|
||||
|
|
Loading…
Reference in New Issue