AHCI can't read and write blocks
Posted: Thu Jun 09, 2022 10:27 pm
I try to use AHCI to visit my sata disk in my os. I can find port 0, rebase the hba memory correctly. I had set port 0's command issue to (1 << slot), waiting the device to respond but not. ci keeps 1, sig keeps 0x101, is and serr keep 0, sstr keeps 0x113 and tfd keeps 0x50. I am confused about it.
My code:
ahci.h:
ahci.c
My code:
ahci.h:
Code: Select all
#ifndef AHCI_H
#define AHCI_H
#include <bits/stdint-uintn.h>
#include <stdbool.h>
#define FIS_DEV_BITS uint16_t
#define HBA_PxCMD_ST 0x0001
#define HBA_PxCMD_FRE 0x0010
#define HBA_PxCMD_FR 0x4000
#define HBA_PxCMD_CR 0x8000
#define HBA_PxIS_TFES (1 << 30)
#define ATA_DEV_BUSY 0x80
#define ATA_DEV_DRQ 0x08
#define ATA_CMD_READ_DMA_EX 0x25
#define ATA_CMD_WRITE_DMA_EX 0x35
#define SATA_SIG_ATA 0x00000101 // SATA drive
#define SATA_SIG_ATAPI 0xEB140101 // SATAPI drive
#define SATA_SIG_SEMB 0xC33C0101 // Enclosure management bridge
#define SATA_SIG_PM 0x96690101 // Port multiplier
#define AHCI_DEV_NULL 0
#define AHCI_DEV_SATA 1
#define AHCI_DEV_SEMB 2
#define AHCI_DEV_PM 3
#define AHCI_DEV_SATAPI 4
#define HBA_PORT_IPM_ACTIVE 1
#define HBA_PORT_DET_PRESENT 3
typedef enum
{
FIS_TYPE_REG_H2D = 0x27, // Register FIS - host to device
FIS_TYPE_REG_D2H = 0x34, // Register FIS - device to host
FIS_TYPE_DMA_ACT = 0x39, // DMA activate FIS - device to host
FIS_TYPE_DMA_SETUP = 0x41, // DMA setup FIS - bidirectional
FIS_TYPE_DATA = 0x46, // Data FIS - bidirectional
FIS_TYPE_BIST = 0x58, // BIST activate FIS - bidirectional
FIS_TYPE_PIO_SETUP = 0x5F, // PIO setup FIS - device to host
FIS_TYPE_DEV_BITS = 0xA1, // Set device bits FIS - device to host
} FIS_TYPE;
typedef struct
{
// DWORD 0
uint8_t fis_type; // FIS_TYPE_REG_H2D
uint8_t pmport:4; // Port multiplier
uint8_t rsv0:3; // Reserved
uint8_t c:1; // 1: Command, 0: Control
uint8_t command; // Command register
uint8_t featurel; // Feature register, 7:0
// DWORD 1
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; // Device register
// DWORD 2
uint8_t lba3; // LBA register, 31:24
uint8_t lba4; // LBA register, 39:32
uint8_t lba5; // LBA register, 47:40
uint8_t featureh; // Feature register, 15:8
// DWORD 3
uint8_t countl; // Count register, 7:0
uint8_t counth; // Count register, 15:8
uint8_t icc; // Isochronous command completion
uint8_t control; // Control register
// DWORD 4
uint8_t rsv1[4]; // Reserved
} FIS_REG_H2D;
typedef struct
{
// DWORD 0
uint8_t fis_type; // FIS_TYPE_REG_D2H
uint8_t pmport:4; // Port multiplier
uint8_t rsv0:2; // Reserved
uint8_t i:1; // Interrupt bit
uint8_t rsv1:1; // Reserved
uint8_t status; // Status register
uint8_t error; // Error register
// DWORD 1
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; // Device register
// DWORD 2
uint8_t lba3; // LBA register, 31:24
uint8_t lba4; // LBA register, 39:32
uint8_t lba5; // LBA register, 47:40
uint8_t rsv2; // Reserved
// DWORD 3
uint8_t countl; // Count register, 7:0
uint8_t counth; // Count register, 15:8
uint8_t rsv3[2]; // Reserved
// DWORD 4
uint8_t rsv4[4]; // Reserved
} FIS_REG_D2H;
typedef struct
{
// DWORD 0
uint8_t fis_type; // FIS_TYPE_DATA
uint8_t pmport:4; // Port multiplier
uint8_t rsv0:4; // Reserved
uint8_t rsv1[2]; // Reserved
// DWORD 1 ~ N
uint32_t data[1]; // Payload
} FIS_DATA;
typedef struct
{
// DWORD 0
uint8_t fis_type; // FIS_TYPE_PIO_SETUP
uint8_t pmport:4; // Port multiplier
uint8_t rsv0:1; // Reserved
uint8_t d:1; // Data transfer direction, 1 - device to host
uint8_t i:1; // Interrupt bit
uint8_t rsv1:1;
uint8_t status; // Status register
uint8_t error; // Error register
// DWORD 1
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; // Device register
// DWORD 2
uint8_t lba3; // LBA register, 31:24
uint8_t lba4; // LBA register, 39:32
uint8_t lba5; // LBA register, 47:40
uint8_t rsv2; // Reserved
// DWORD 3
uint8_t countl; // Count register, 7:0
uint8_t counth; // Count register, 15:8
uint8_t rsv3; // Reserved
uint8_t e_status; // New value of status register
// DWORD 4
uint16_t tc; // Transfer count
uint8_t rsv4[2]; // Reserved
} FIS_PIO_SETUP;
typedef struct
{
// DWORD 0
uint8_t fis_type; // FIS_TYPE_DMA_SETUP
uint8_t pmport:4; // Port multiplier
uint8_t rsv0:1; // Reserved
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 rsved[2]; // Reserved
//DWORD 1&2
uint64_t DMAbufferID; // 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.
//DWORD 3
uint32_t rsvd; //More reserved
//DWORD 4
uint32_t DMAbufOffset; //Byte offset into buffer. First 2 bits must be 0
//DWORD 5
uint32_t TransferCount; //Number of bytes to transfer. Bit 0 must be 0
//DWORD 6
uint32_t resvd; //Reserved
} FIS_DMA_SETUP;
typedef volatile struct
{
uint32_t clb; // 0x00, command list base address, 1K-byte aligned
uint32_t clbu; // 0x04, command list base address upper 32 bits
uint32_t fb; // 0x08, FIS base address, 256-byte aligned
uint32_t fbu; // 0x0C, FIS base address upper 32 bits
uint32_t is; // 0x10, interrupt status
uint32_t ie; // 0x14, interrupt enable
uint32_t cmd; // 0x18, command and status
uint32_t rsv0; // 0x1C, Reserved
uint32_t tfd; // 0x20, task file data
uint32_t sig; // 0x24, signature
uint32_t ssts; // 0x28, SATA status (SCR0:SStatus)
uint32_t sctl; // 0x2C, SATA control (SCR2:SControl)
uint32_t serr; // 0x30, SATA error (SCR1:SError)
uint32_t sact; // 0x34, SATA active (SCR3:SActive)
uint32_t ci; // 0x38, command issue
uint32_t sntf; // 0x3C, SATA notification (SCR4:SNotification)
uint32_t fbs; // 0x40, FIS-based switch control
uint32_t rsv1[11]; // 0x44 ~ 0x6F, Reserved
uint32_t vendor[4]; // 0x70 ~ 0x7F, vendor specific
} HBA_PORT;
typedef volatile struct
{
// 0x00 - 0x2B, Generic Host Control
uint32_t cap; // 0x00, Host capability
uint32_t ghc; // 0x04, Global host control
uint32_t is; // 0x08, Interrupt status
uint32_t pi; // 0x0C, Port implemented
uint32_t vs; // 0x10, Version
uint32_t ccc_ctl; // 0x14, Command completion coalescing control
uint32_t ccc_pts; // 0x18, 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
// 0x2C - 0x9F, Reserved
uint8_t rsv[0xA0-0x2C];
// 0xA0 - 0xFF, Vendor specific registers
uint8_t vendor[0x100-0xA0];
// 0x100 - 0x10FF, Port control registers
HBA_PORT ports[1]; // 1 ~ 32
} HBA_MEM;
// typedef volatile struct
// {
// // 0x00
// FIS_DMA_SETUP dsfis; // DMA Setup FIS
// uint8_t pad0[4];
//
// // 0x20
// FIS_PIO_SETUP psfis; // PIO Setup FIS
// uint8_t pad1[12];
//
// // 0x40
// FIS_REG_D2H rfis; // Register – Device to Host FIS
// uint8_t pad2[4];
//
// // 0x58
// FIS_DEV_BITS sdbfis; // Set Device Bit FIS
//
// // 0x60
// uint8_t ufis[64];
//
// // 0xA0
// uint8_t rsv[0x100-0xA0];
// } HBA_FIS;
typedef struct
{
// DW0
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 rsv0:1; // Reserved
uint8_t pmp:4; // Port multiplier port
uint16_t prdtl; // Physical region descriptor table length in entries
// DW1
volatile
uint32_t prdbc; // Physical region descriptor byte count transferred
// DW2, 3
uint32_t ctba; // Command table descriptor base address
uint32_t ctbau; // Command table descriptor base address upper 32 bits
// DW4 - 7
uint32_t rsv1[4]; // Reserved
} HBA_CMD_HEADER;
typedef struct
{
uint32_t dba; // Data base address
uint32_t dbau; // Data base address upper 32 bits
uint32_t rsv0; // Reserved
// DW3
uint32_t dbc:22; // Byte count, 4M max
uint32_t rsv1:9; // Reserved
uint32_t i:1; // Interrupt on completion
} HBA_PRDT_ENTRY;
typedef struct tagHBA_CMD_TBL
{
// 0x00
uint8_t cfis[64]; // Command FIS
// 0x40
uint8_t acmd[16]; // ATAPI command, 12 or 16 bytes
// 0x50
uint8_t rsv[48]; // Reserved
// 0x80
HBA_PRDT_ENTRY prdt_entry[1]; // Physical region descriptor table entries, 0 ~ 65535
} HBA_CMD_TBL;
void start_cmd();
void stop_cmd();
void ahci_init(uint64 base);
int read_port(uint32 startl, uint32 starth, uint32 count, uint64 buf);
int write_port(uint32 startl, uint32 starth, uint32 count, uint64 buf);
int find_cmdslot();
void rebase_port();
void start_cmd();
void stop_cmd();
void probe_port(HBA_MEM *abar_temp);
#endif
Code: Select all
#include "kernel/include/xtos.h"
#include "kernel/include/ahci.h"
static HBA_MEM *abar;
static int portno = -1;
#define PORT (abar -> ports + portno)
#define AHCI_BASE (uint64) abar
void ahci_init(uint64 ahci_base) {
probe_port((HBA_MEM *) ahci_base);
}
// /*
// Code Reference : OSDEV
// */
// void handler_ahci_routine()
// {
// printf("AHCI INTERRUPT HANDLER\n");
// if (abar->ports[0].is & HBA_PxIS_TFES)
// printf("Read disk error\n");
// printf("\n TFD=[%d]", ((HBA_PORT *) &abar->ports[0])->tfd);
// printf("\nSSTS =[%d]", ((HBA_PORT *) &abar->ports[0])->ssts);
// printf("\nIE=[%d]", ((HBA_PORT *) &abar->ports[0])->ie);
// printf("\nSERR%d", ((HBA_PORT *) &abar->ports[0])->serr);
// printf("\nIS%d", ((HBA_PORT *) &abar->ports[0])->is);
// abar->ports[0].is = 0xffff;
// while (1)
// ;
// }
/*
Code Reference : OSDEV
*/
int read_port(uint32 startl, uint32 starth, uint32 count,
uint64 buf)
{
PORT->is = 0xffff; // Clear pending interrupt bits
int slot = find_cmdslot(PORT);
if (slot == -1)
return -1;
HBA_CMD_HEADER *cmdheader = (HBA_CMD_HEADER*)(PORT->clb | DMWIN_MASK);
cmdheader += slot;
cmdheader->cfl = sizeof(FIS_REG_H2D) / sizeof(uint32); // Command FIS size
cmdheader->w = 0; // Read from device
cmdheader->c = 1; // Read from device
cmdheader->p = 1; // Read from device
cmdheader->prdtl = (uint16) ((count - 1) >> 4) + 1; // PRDT entries count
HBA_CMD_TBL *cmdtbl = (HBA_CMD_TBL*)(cmdheader->ctba | DMWIN_MASK);
int i = 0;
for (i = 0; i < cmdheader->prdtl - 1; i++)
{
cmdtbl->prdt_entry[i].dba = (uint32) (buf & 0xFFFFFFFF);
cmdtbl->prdt_entry[i].dbau = (uint32) ((buf >> 32) & 0xFFFFFFFF);
cmdtbl->prdt_entry[i].dbc = 8 * 1024 - 1; // 8K bytes
cmdtbl->prdt_entry[i].i = 0;
buf += 4 * 1024; // 4K words
count -= 16; // 16 sectors
}
cmdtbl->prdt_entry[i].dba = (uint32) (buf & 0xFFFFFFFF);
cmdtbl->prdt_entry[i].dbau = (uint32) ((buf >> 32) & 0xFFFFFFFF);
cmdtbl->prdt_entry[i].dbc = count << 9; // 512 bytes per sector
cmdtbl->prdt_entry[i].i = 0;
FIS_REG_H2D *cmdfis = (FIS_REG_H2D*) (&cmdtbl->cfis);
cmdfis->fis_type = FIS_TYPE_REG_H2D;
cmdfis->c = 1; // Command
cmdfis->command = ATA_CMD_READ_DMA_EX;
cmdfis->lba0 = (uint8) startl;
cmdfis->lba1 = (uint8) (startl >> 8);
cmdfis->lba2 = (uint8) (startl >> 16);
cmdfis->device = 1 << 6; // LBA mode
cmdfis->lba3 = (uint8) (startl >> 24);
cmdfis->lba4 = (uint8) starth;
cmdfis->lba5 = (uint8) (starth >> 8);
cmdfis->countl = count & 0xff;
cmdfis->counth = count >> 8;
int spin = 0;
while ((PORT->tfd & (ATA_DEV_BUSY | ATA_DEV_DRQ)) && spin < 1000000)
{
spin++;
}
if (spin == 1000000)
{
printf("Port is hung\n");
return -1;
}
PORT->ci = (1 << slot);
printf("0x%x 0x%x\n", PORT->ci, PORT->is);
while (1)
{
if ((PORT->ci & (1 << slot)) == 0)
break;
if (PORT->is & HBA_PxIS_TFES)
{ // Task file error
printf("Read disk error\n");
return -1;
}
}
if (PORT->is & HBA_PxIS_TFES)
{
printf("Read disk error\n");
return -1;
}
while (PORT->ci != 0)
{
printf("Read disk error\n");
}
return 0;
}
/*
Code Reference : OSDEV
*/
int write_port(uint32 startl, uint32 starth, uint32 count,
uint64 buf)
{
PORT->is = 0xffff; // Clear pending interrupt bits
int slot = find_cmdslot(PORT);
if (slot == -1)
return -1;
HBA_CMD_HEADER *cmdheader = (HBA_CMD_HEADER*)(PORT->clb | DMWIN_MASK);
cmdheader += slot;
cmdheader->cfl = sizeof(FIS_REG_H2D) / sizeof(uint32); // Command FIS size
cmdheader->w = 1; // Read from device
cmdheader->c = 1; // Read from device
cmdheader->p = 1; // Read from device
cmdheader->prdtl = (uint16) ((count - 1) >> 4) + 1; // PRDT entries count
HBA_CMD_TBL *cmdtbl = (HBA_CMD_TBL*)(cmdheader->ctba | DMWIN_MASK);
int i = 0;
for (i = 0; i < cmdheader->prdtl - 1; i++)
{
cmdtbl->prdt_entry[i].dba = (uint32) (buf & 0xFFFFFFFF);
cmdtbl->prdt_entry[i].dbau = (uint32) ((buf >> 32) & 0xFFFFFFFF);
cmdtbl->prdt_entry[i].dbc = 8 * 1024 - 1; // 8K bytes
cmdtbl->prdt_entry[i].i = 0;
buf += 4 * 1024; // 4K words
count -= 16; // 16 sectors
}
cmdtbl->prdt_entry[i].dba = (uint32) (buf & 0xFFFFFFFF);
cmdtbl->prdt_entry[i].dbau = (uint32) ((buf >> 32) & 0xFFFFFFFF);
cmdtbl->prdt_entry[i].dbc = count << 9; // 512 bytes per sector
cmdtbl->prdt_entry[i].i = 0;
FIS_REG_H2D *cmdfis = (FIS_REG_H2D*) (&cmdtbl->cfis);
cmdfis->fis_type = FIS_TYPE_REG_H2D;
cmdfis->c = 1; // Command
cmdfis->command = ATA_CMD_WRITE_DMA_EX;
cmdfis->lba0 = (uint8) startl;
cmdfis->lba1 = (uint8) (startl >> 8);
cmdfis->lba2 = (uint8) (startl >> 16);
cmdfis->device = 1 << 6; // LBA mode
cmdfis->lba3 = (uint8) (startl >> 24);
cmdfis->lba4 = (uint8) starth;
cmdfis->lba5 = (uint8) (starth >> 8);
cmdfis->countl = count & 0xff;
cmdfis->counth = count >> 8;
int spin = 0;
while ((PORT->tfd & (ATA_DEV_BUSY | ATA_DEV_DRQ)) && spin < 1000000)
{
spin++;
}
if (spin == 1000000)
{
printf("Port is hung\n");
return -1;
}
PORT->ci = (1 << slot);
while (1)
{
// In some longer duration reads, it may be helpful to spin on the DPS bit
// in the PxIS port field as well (1 << 5)
if ((PORT->ci & (1 << slot)) == 0)
break;
if (PORT->is & HBA_PxIS_TFES)
{ // Task file error
printf("Read disk error\n");
return -1;
}
}
if (PORT->is & HBA_PxIS_TFES)
{
printf("Read disk error\n");
return -1;
}
while (PORT->ci != 0)
{
printf("Read disk error\n");
}
return 0;
}
// To setup command fing a free command list slot
int find_cmdslot()
{
uint32 slots = (PORT->sact | PORT->ci);
int num_of_slots = (abar->cap & 0x0f00) >> 8; // Bit 8-12
int i;
for (i = 0; i < num_of_slots; i++)
{
if ((slots & 1) == 0)
{
// printf("[slot=%d]", i);
// if(i==0)
return i;
// break;
}
slots >>= 1;
}
printf("Cannot find free command list entry\n");
return -1;
}
// Check device type
static int check_type(HBA_PORT * port)
{
uint32 ssts = port->ssts;
uint8 ipm = (ssts >> 8) & 0x0F;
uint8 det = ssts & 0x0F;
//printf ("\n ipm %d det %d sig %d", ipm, det, PORT->sig);
if (det != HBA_PORT_DET_PRESENT) // Check drive status
return AHCI_DEV_NULL;
if (ipm != HBA_PORT_IPM_ACTIVE)
return AHCI_DEV_NULL;
switch (port->sig)
{
case SATA_SIG_ATAPI:
return AHCI_DEV_SATAPI;
case SATA_SIG_SEMB:
return AHCI_DEV_SEMB;
case SATA_SIG_PM:
return AHCI_DEV_PM;
default:
return AHCI_DEV_SATA;
}
return 0;
}
/*
Code Reference : OSDEV
*/
void rebase_port()
{
stop_cmd(PORT); // Stop command engine
// Command list offset: 1K*portno
// Command list entry size = 32
// Command list entry maxim count = 32
// Command list maxim size = 32*32 = 1K per port
PORT->clb = AHCI_BASE + (portno<<10);
PORT->clbu = 0;
memset((void*)((PORT->clb) | DMWIN_MASK) , 0, 1024);
// FIS offset: 32K+256*portno
// FIS entry size = 256 bytes per port
PORT->fb = AHCI_BASE + (32<<10) + (portno<<8);
PORT->fbu = 0;
memset((void*)((PORT->fb) | DMWIN_MASK), 0, 256);
// Command table offset: 40K + 8K*portno
// Command table size = 256*32 = 8K per port
HBA_CMD_HEADER *cmdheader = (HBA_CMD_HEADER*)((PORT->clb) | DMWIN_MASK);
for (int i=0; i<32; i++)
{
cmdheader[i].prdtl = 8; // 8 prdt entries per command table
// 256 bytes per command table, 64+16+48+16*8
// Command table offset: 40K + 8K*portno + cmdheader_index*256
cmdheader[i].ctba = AHCI_BASE + (40<<10) + (portno<<13) + (i<<8);
cmdheader[i].ctbau = 0;
memset((void*)(cmdheader[i].ctba | DMWIN_MASK) , 0, 256);
}
start_cmd(PORT); // Start command engine
}
// Start command engine
void start_cmd()
{
// Wait until CR (bit15) is cleared
while (PORT->cmd & HBA_PxCMD_CR)
;
// Set FRE (bit4) and ST (bit0)
PORT->cmd |= HBA_PxCMD_FRE;
PORT->cmd |= HBA_PxCMD_ST;
}
// Stop command engine
void stop_cmd()
{
// Clear ST (bit0)
PORT->cmd &= ~HBA_PxCMD_ST;
// Clear FRE (bit4)
PORT->cmd &= ~HBA_PxCMD_FRE;
// Wait until FR (bit14), CR (bit15) are cleared
while(1)
{
if (PORT->cmd & HBA_PxCMD_FR)
continue;
if (PORT->cmd & HBA_PxCMD_CR)
continue;
break;
}
}
/*
Code Reference : OSDEV
*/
void probe_port(HBA_MEM *abar_temp)
{
printf("Inside probe_port\n");
// Search disk in impelemented ports
uint32 pi = abar_temp->pi;
int i = 0;
while (i < 32)
{
if (pi & 1)
{
int dt = check_type((HBA_PORT *) &abar_temp->ports[i]);
if (dt == AHCI_DEV_SATA)
{
printf("\nSATA drive found at port %d\n", i);
abar = abar_temp;
portno = i;
rebase_port();
printf("DONE AHCI INITIALISATION :: PORT REBASE\n");
return;
}
else if (dt == AHCI_DEV_SATAPI)
{
printf("\nSATAPI drive found at port %d\n", i);
}
else if (dt == AHCI_DEV_SEMB)
{
printf("\nSEMB drive found at port %d\n", i);
}
else if (dt == AHCI_DEV_PM)
{
printf("\nPM drive found at port %d\n", i);
}
else
{
printf("\nNo drive found at port %d\n", i);
}
}
pi >>= 1;
i++;
}
printf("probe_port complete\n");
}