Page 1 of 1

Questions about AHCI

Posted: Fri Feb 28, 2020 12:03 am
by shore
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.

Code: Select all

wrmsr(0x80b, 0UL);
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:

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);
}

Re: Questions about AHCI

Posted: Fri Feb 28, 2020 4:35 am
by Octocontrabass
shore wrote:4. How to deal with cached memory.
On x86, you don't have to. The cache is automatically invalidated when DMA updates the contents of memory.

Don't use INVD. It's intended for things like cache tests during power-on, and you should never see a reason to use it when developing an OS.

Re: Questions about AHCI

Posted: Fri Feb 28, 2020 9:53 am
by shore
Octocontrabass wrote:
shore wrote:4. How to deal with cached memory.
On x86, you don't have to. The cache is automatically invalidated when DMA updates the contents of memory.

Don't use INVD. It's intended for things like cache tests during power-on, and you should never see a reason to use it when developing an OS.
Thanks, where I can find the documentation about this???? Thanks a lot!!

Re: Questions about AHCI

Posted: Fri Feb 28, 2020 12:09 pm
by Octocontrabass
You can find the documentation from Intel and AMD.
Intel SDM Volume 3A section 11.3.2 wrote:An I/O agent can perform direct memory access (DMA) to write-back memory and the cache protocol maintains cache coherency.
Equivalent text can be found in the AMD APM Volume 2 section 7.3. (I'm not quoting it here because AMD doesn't provide such a nice summary.)

Re: Questions about AHCI

Posted: Fri Feb 28, 2020 7:41 pm
by shore
Octocontrabass wrote:You can find the documentation from Intel and AMD.
Intel SDM Volume 3A section 11.3.2 wrote:An I/O agent can perform direct memory access (DMA) to write-back memory and the cache protocol maintains cache coherency.
Equivalent text can be found in the AMD APM Volume 2 section 7.3. (I'm not quoting it here because AMD doesn't provide such a nice summary.)
Thanks a lot. May I also ask which AHCI interrupt I should use..........