Questions about AHCI
Posted: Fri Feb 28, 2020 12:03 am
Hi I'm a beginner, and trying to followed https://wiki.osdev.org/AHCI to write a AHCI driver.
I actually successfully read the data from AHCI controller and caused a interrupt from MSI.
But still I have a few questions for detail:
1. Which interrupt I should use.
From "serial-ata-ahci-spec-rev1-3-1" it is said I should enable the relevant bit on PxIE, and if the according bit on PxIS is set by HBA, a interrupt would be issue.
With a read operation, I observed the PxIS and the only bit it sets is bit 0. From the documentation it is "Device to Host Register FIS Interrupt (DHRS)" which indicating there is an FIS sent from HBA to host.
But how should I establish a interrupt when "the data from HBA is copied into system memory".
2. How should I clear the interrupt
With x2APIC enabled, I use the following code to send a "end of interruption" signal.
However, it does not clear the relevant PxIS bit (bit 0 of read operation). So how should I correctly clear the interruption.
3. What is the use of DATA FIS
It is said "used for transporting payload data". But from the example, I can not find where I should plot this structure.
4. How to deal with cached memory.
Since the read & write use DMA, it may cause mismatch of memory and catch. Currently the only 2 way I may use are: a) allocate UC page; b) use INVD instruction to invalid cache.
Since I'm using 2mb paging, I would prefer to use method b). But I can not find references on multi-cores. If INVD would flush all cores' cache or just the core reviving the instruction.
Thanks a lot for anyone who would help me on this. I'll attach my code here for reference:
I actually successfully read the data from AHCI controller and caused a interrupt from MSI.
But still I have a few questions for detail:
1. Which interrupt I should use.
From "serial-ata-ahci-spec-rev1-3-1" it is said I should enable the relevant bit on PxIE, and if the according bit on PxIS is set by HBA, a interrupt would be issue.
With a read operation, I observed the PxIS and the only bit it sets is bit 0. From the documentation it is "Device to Host Register FIS Interrupt (DHRS)" which indicating there is an FIS sent from HBA to host.
But how should I establish a interrupt when "the data from HBA is copied into system memory".
2. How should I clear the interrupt
With x2APIC enabled, I use the following code to send a "end of interruption" signal.
Code: Select all
wrmsr(0x80b, 0UL);
3. What is the use of DATA FIS
It is said "used for transporting payload data". But from the example, I can not find where I should plot this structure.
4. How to deal with cached memory.
Since the read & write use DMA, it may cause mismatch of memory and catch. Currently the only 2 way I may use are: a) allocate UC page; b) use INVD instruction to invalid cache.
Since I'm using 2mb paging, I would prefer to use method b). But I can not find references on multi-cores. If INVD would flush all cores' cache or just the core reviving the instruction.
Thanks a lot for anyone who would help me on this. I'll attach my code here for reference:
Code: Select all
void AHCI_Disk_Init(void)
{
int i;
pg_attr ATTR;
memset(&ATTR, 0, sizeof(pg_attr));
ATTR.PML4E_Attr.RW = 1;
ATTR.PML4E_Attr.P = 1;
ATTR.PDPTE_Attr.P = 1;
ATTR.PDPTE_Attr.RW = 1;
ATTR.PDE_Attr.PS = 1;
ATTR.PDE_Attr.P = 1;
ATTR.PDE_Attr.RW = 1;
//Choose PAT[3]: Uncacheable memory type
ATTR.PDE_Attr.PCD = 1;
ATTR.PDE_Attr.PCD = 1;
pagetable_init(PML4E,
((unsigned long)AHCI.ABAR - PAGE_OFFSET) & PAGE_2M_MASK,
((unsigned long)AHCI.ABAR & PAGE_2M_MASK) + PAGE_OFFSET, 1,
&ATTR, 0);
AHCI.ABAR->ghc = AHCI.ABAR->ghc | SATA_GHC_AE;
AHCI.No_slot = (AHCI.ABAR->cap >> 8) & 0x1f;
for(i=0;i<32;i++){
if(AHCI.ABAR->pi & 1<<i){
if(
(AHCI.ABAR->ports[i].ssts & 0xf) == 3 &&
((AHCI.ABAR->ports[i].ssts >> 8) & 0x0F) == 1 &&
AHCI.ABAR->ports[i].sig == SATA_SIG_ATA
){
AHCI.ABAR->ports[i].cmd &= ~(HBA_PxCMD_ST | HBA_PxCMD_FRE);
while(1)
{
if (AHCI.ABAR->ports[i].cmd & HBA_PxCMD_FR)
continue;
if (AHCI.ABAR->ports[i].cmd & HBA_PxCMD_CR)
continue;
break;
}
AHCI.ABAR->ports[i].clb = kmalloc(sizeof(HBA_CMD_HEADER)*AHCI.No_slot, 0) - PAGE_OFFSET;
AHCI.ABAR->ports[i].fb = kmalloc(sizeof(HBA_FIS),0) - PAGE_OFFSET;
memset((void *)((unsigned long)AHCI.ABAR->ports[i].clb + PAGE_OFFSET), 0, sizeof(HBA_CMD_HEADER)*AHCI.No_slot);
memset((void *)((unsigned long)AHCI.ABAR->ports[i].fb + PAGE_OFFSET), 0, sizeof(HBA_FIS));
AHCI.ABAR->ports[i].cmd |= HBA_PxCMD_FRE;
AHCI.ABAR->ports[i].cmd |= HBA_PxCMD_ST;
AHCI.ABAR->ports[i].ie |= 1;
color_printk(WHITE, ORANGE, "AHCI.ABAR->ports[%d].is: 0x%08X\n", i, AHCI.ABAR->ports[i].is);
color_printk(WHITE, ORANGE, "AHCI.ABAR->ports[%d].ie: 0x%08X\n", i, AHCI.ABAR->ports[i].ie);
}
}
}
AHCI.ABAR->ghc |= SATA_GHC_IE;
AHCI.MSI._64->MAR.Pre_fix = 0xFEE; // Fixed head address
AHCI.MSI._64->MAR.Dest_ID = 0; // Send to APIC ID = 0
AHCI.MSI._64->MAR.zero = 0;
AHCI.MSI._64->MAR.DM = 0; // Pysical destination mode
AHCI.MSI._64->MAR.RH = 0; // Interrupt direcet to processor listed in the Destination ID field
AHCI.MSI._64->MDR.Del_Mode = 0; // Fixed mode
AHCI.MSI._64->MDR.Vector = 0x2e; // IDT[0x2E]
AHCI.MSI._64->MDR.Trg_Mode = 0; // Edge trigger
AHCI.MSI._64->MDR.Level = 0; // No use for edge triggered interrupt
AHCI.MSI._64->MDR.zero = 0;
AHCI.MSI._64->MCR.MSI_Enable = 1; // Enable MSI
}
int AHCI_Read_1st_sec(int port)
{
int i, j, slot = 0;
unsigned char *buff1 = kmalloc(512, 0);
memset(buff1, 0, 512);
AHCI.ABAR->ports[port].is = (unsigned int) - 1;
slot = AHCI.ABAR->ports[port].sact | AHCI.ABAR->ports[port].ci;
for(i =0; i<AHCI.No_slot; i++){
if(!(slot&1)){
break;
}
}
if(i==AHCI.No_slot){
color_printk(WHITE,RED,"All slot for port[%d] are busy!\n", port);
}
AHCI.ABAR->ports[port].clb[i].cfl = sizeof(FIS_REG_H2D)/sizeof(int);
AHCI.ABAR->ports[port].clb[i].w = 0;
AHCI.ABAR->ports[port].clb[i].prdtl = 1;
AHCI.ABAR->ports[port].clb[i].ctba = kmalloc(sizeof(HBA_CMD_TBL), 0) - PAGE_OFFSET;
HBA_CMD_TBL * cmdtbl = (HBA_CMD_TBL *)((unsigned long)AHCI.ABAR->ports[port].clb[i].ctba + PAGE_OFFSET);
memset(cmdtbl, 0, sizeof(HBA_CMD_TBL));
cmdtbl->prdt_entry[0].dba = ((unsigned long)buff1 - PAGE_OFFSET);
cmdtbl->prdt_entry[0].dbc = 512 - 1;
// cmdtbl->prdt_entry[0].i = 1;
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 = 0;
cmdfis->lba1 = 0;
cmdfis->lba2 = 0;
cmdfis->device = 1<<6; // LBA mode
cmdfis->lba3 = 0;
cmdfis->lba4 = 0;
cmdfis->lba5 = 0;
cmdfis->countl = 1;
cmdfis->counth = 0;
color_printk(WHITE, ORANGE, "AHCI.ABAR->ports[%d].is: 0x%08X\n", port, AHCI.ABAR->ports[port].is);
while(AHCI.ABAR->ports[port].tfd & (ATA_DEV_BUSY | ATA_DEV_DRQ));
AHCI.ABAR->ports[port].ci |= 1<<i;
while(AHCI.ABAR->ports[port].ci & 1<<i);
color_printk(WHITE, ORANGE, "AHCI.ABAR->ports[%d].is: 0x%08X\n", port, AHCI.ABAR->ports[port].is);
for(i = 0; i < 512; i++){
color_printk(BLACK,WHITE,"%02x",buff1[i]);
}
color_printk(BLACK,WHITE,"\n",buff1[i]);
color_printk(WHITE, ORANGE, "AHCI.ABAR->ports[%d].is: 0x%08X\n", port, AHCI.ABAR->ports[port].is);
}