AHCI Bus driver interrupt issue?

Now I am writing a simple AHCI bus drier. But i fail to create a DMA transfer interrupt.

Firstly, I map registers address space to system virtual address space.Then create a valid port device and get its base virtual address of the port registers address space.

Secondly, initilize the gloable registers:

  1. Set GHC register as 0x80000002 to enable AHCI mode and gloable interrupt enbale.
    (In fact, the register original value is 0x80000002) .
  2. Allocate command list for this port device and save the table phycial address to
    PxCLB register.
  3. Allocate a recevice FIS buffer and save the buffer phycial address to PxFB register.
  4. Set PxIE register as 0xFFFFFFFF to eanble all interrupt.
  5. Set PxCMD register as 0x00000001 to allow HBA process the command list.

At last, create a read DMA FIS commad.

  1. Allocate a command table with one PRD for commad header0.
  2. Fill the command table to create a read DMA setup FIS, and fill the PRD with read
    buffer.
  3. Set PxCI as 1 to tell HBA that there is a command can be proccessed.

However, i can not see any interrupt by doing these. Are there anybody familar with AHCI bus driver? Would you give me some good ideas?

Are there anybody know these?

Dear all:
When all interrupt enable bits are set , i find a “Interface Non-fatal Error Status(INFS)” interrupt, and this interrupt will be creeated continuously. The following is a DMA setup code segment. If anybody known about this, plealse give me a advice, Thank you!

//allocate memory for command list structure
lowest.QuadPart = 0;
highest.LowPart = 0xffffffff;
highest.HighPart = 0;
alignment.QuadPart = ALIGN_VALUE_64K;//128;
pCommandTableBase = MmAllocateContiguousMemorySpecifyCache(sizeof(COMMAND_TABLE)+sizeof(PRDT), // number of bytes to allocate
lowest, // lowest acceptable address
highest, // highest acceptable address
alignment, // alignment requirement
MmWriteCombined); // caching type

if (pCommandTableBase==NULL)
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
break;
}
//initial command header
pchCommandHead = pPdoDevExt->pVirutalClb;
pchCommandHead[1].dw0.bx.prdtl = 1;
pchCommandHead[1].dw0.bx.pmp = 0;
pchCommandHead[1].dw0.bx.c = 0;
pchCommandHead[1].dw0.bx.b = 0;
pchCommandHead[1].dw0.bx.r = 0;
pchCommandHead[1].dw0.bx.p = 1;
pchCommandHead[1].dw0.bx.w = 0;
pchCommandHead[1].dw0.bx.a = 0;
pchCommandHead[1].dw0.bx.cfl = 28; //???
pchCommandHead[1].prdbc = 1024;
pchCommandHead[1].ctba = MmGetPhysicalAddress(pCommandTableBase);
// fill command FIS
pCommandTable = (PCOMMAND_TABLE)pCommandTableBase;
pCommandTable->cFis.Fis.DmaSetup.DW0.bx.FisType = 0x41;
pCommandTable->cFis.Fis.DmaSetup.DW0.bx.D = 0x1;
pCommandTable->cFis.Fis.DmaSetup.DW0.bx.PmPort = 0;

lbaAddress.QuadPart = 0xFFFFFFFF;
pCommandTable->cFis.Fis.DmaSetup.dwDmaBufAddr = lbaAddress;
pCommandTable->cFis.Fis.DmaSetup.dwBufOffset = 0x0;
pCommandTable->cFis.Fis.DmaSetup.dwTransferCnt = 1024;

// Fill PRD
lowest.QuadPart = 0;
highest.LowPart = 0xffffffff;
highest.HighPart = 0;
/*alignment.QuadPart = 0;

pPRDTableBase = MmAllocateContiguousMemorySpecifyCache(sizeof(PRDT), // number of bytes to allocate
lowest, // lowest acceptable address
highest, // highest acceptable address
alignment, // alignment requirement
MmWriteCombined); // caching type

if (pPRDTableBase==NULL)
{
WdfRequestComplete(Request, STATUS_INSUFFICIENT_RESOURCES);
ExFreePool(pCommandTableBase);
break;
}*/

alignment.QuadPart = ALIGN_VALUE_64K;
pDataBase = MmAllocateContiguousMemorySpecifyCache(1024, // number of bytes to allocate
lowest, // lowest acceptable address
highest, // highest acceptable address
alignment, // alignment requirement
MmWriteCombined); // caching type

if (pDataBase==NULL)
{
ntStatus =STATUS_INSUFFICIENT_RESOURCES;
ExFreePool(pCommandTableBase);
//ExFreePool(pPRDTableBase);
break;
}

// Test
/*pPRDTable = (PPRDT)pPRDTableBase;
// Save data buffer address.
pPRDTable->DBA = MmGetPhysicalAddress(pDataBase);
pPRDTable->DW3.bx.bit0 = 0x01;
pPRDTable->DW3.bx.bit1 = 0x01;
pPRDTable->DW3.bx.DBC = 1024;*/

// Test
//pCommandTable->pPrdTable = pPRDTable;

pCommandTable->PrdTable[0].DBA = MmGetPhysicalAddress(pDataBase);
//pCommandTable->PrdTable[0].DW3.bx.bit0 = 0x01;
pCommandTable->PrdTable[0].DW3.bx.DBC = 513;
pCommandTable->PrdTable[0].DW3.bx.bit1 = 0x01;

// Allow HBA Send DMA setup FIS
pPdoDevExt->papdPortRegBase->ci |= 0x02;
pPdoDevExt->papdPortRegBase->sact |= 0x02;

Just a comment, you should be creating a DMA Adapter object and allocating
common memory, not calling
MmAllocateContiguousMemorySpecifyCache+MmGetPhysicalAddress.

It also looks like you are using a pointer into device register space at
pPdoDevExt->papdPortRegBase, and directly accessing device registers. This
is very bad, as modern processors are out-of-order execution processors and
you may be telling the hardware to do an operation before it’s command block
has been setup. On modern processors, the way you have things,
pPdoDevExt->papdPortRegBase->ci |= 0x02; may get physically written to
before pCommandTable->PrdTable[0].DW3.bx.bit1 = 0x01;, even though in
the source code they are in order. This makes for VERY hard to find bugs.
Actual execution order is indeterminate unless there are barrier
instructions, which you get automatically by using the correct device
register access functions.

Jan

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:bounce-403356-
xxxxx@lists.osr.com] On Behalf Of xxxxx@gmail.com
Sent: Sunday, February 28, 2010 12:48 AM
To: Windows System Software Devs Interest List
Subject: RE:[ntdev] AHCI Bus driver interrupt issue?

Dear all:
When all interrupt enable bits are set , i find a “Interface
Non-fatal Error Status(INFS)” interrupt, and this interrupt will be
creeated continuously. The following is a DMA setup code segment. If
anybody known about this, plealse give me a advice, Thank you!

//allocate memory for command list structure
lowest.QuadPart = 0;
highest.LowPart = 0xffffffff;
highest.HighPart = 0;
alignment.QuadPart = ALIGN_VALUE_64K;//128;
pCommandTableBase =
MmAllocateContiguousMemorySpecifyCache(sizeof(COMMAND_TABLE)+sizeof(PRD
T), // number of bytes to allocate

lowest, // lowest acceptable address

highest, // highest acceptable address

alignment, // alignment requirement

MmWriteCombined); // caching type

if (pCommandTableBase==NULL)
{
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
break;
}
//initial command header
pchCommandHead = pPdoDevExt->pVirutalClb;
pchCommandHead[1].dw0.bx.prdtl = 1;
pchCommandHead[1].dw0.bx.pmp = 0;
pchCommandHead[1].dw0.bx.c = 0;
pchCommandHead[1].dw0.bx.b = 0;
pchCommandHead[1].dw0.bx.r = 0;
pchCommandHead[1].dw0.bx.p = 1;
pchCommandHead[1].dw0.bx.w = 0;
pchCommandHead[1].dw0.bx.a = 0;
pchCommandHead[1].dw0.bx.cfl = 28; //???
pchCommandHead[1].prdbc = 1024;
pchCommandHead[1].ctba =
MmGetPhysicalAddress(pCommandTableBase);
// fill command FIS
pCommandTable = (PCOMMAND_TABLE)pCommandTableBase;
pCommandTable->cFis.Fis.DmaSetup.DW0.bx.FisType =
0x41;
pCommandTable->cFis.Fis.DmaSetup.DW0.bx.D =
0x1;
pCommandTable->cFis.Fis.DmaSetup.DW0.bx.PmPort = 0;

lbaAddress.QuadPart = 0xFFFFFFFF;
pCommandTable->cFis.Fis.DmaSetup.dwDmaBufAddr =
lbaAddress;
pCommandTable->cFis.Fis.DmaSetup.dwBufOffset =
0x0;
pCommandTable->cFis.Fis.DmaSetup.dwTransferCnt =
1024;

// Fill PRD
lowest.QuadPart = 0;
highest.LowPart = 0xffffffff;
highest.HighPart = 0;
/*alignment.QuadPart = 0;

pPRDTableBase =
MmAllocateContiguousMemorySpecifyCache(sizeof(PRDT), // number of
bytes to allocate

lowest, // lowest acceptable address

highest, // highest acceptable address

alignment, // alignment requirement

MmWriteCombined); // caching type

if (pPRDTableBase==NULL)
{
WdfRequestComplete(Request,
STATUS_INSUFFICIENT_RESOURCES);
ExFreePool(pCommandTableBase);
break;
}*/

alignment.QuadPart = ALIGN_VALUE_64K;
pDataBase =
MmAllocateContiguousMemorySpecifyCache(1024, // number of bytes to
allocate

lowest, // lowest acceptable address

highest, // highest acceptable address

alignment, // alignment requirement

MmWriteCombined); // caching type

if (pDataBase==NULL)
{
ntStatus =STATUS_INSUFFICIENT_RESOURCES;
ExFreePool(pCommandTableBase);
//ExFreePool(pPRDTableBase);
break;
}

// Test
/*pPRDTable = (PPRDT)pPRDTableBase;
// Save data buffer address.
pPRDTable->DBA = MmGetPhysicalAddress(pDataBase);
pPRDTable->DW3.bx.bit0 = 0x01;
pPRDTable->DW3.bx.bit1 = 0x01;
pPRDTable->DW3.bx.DBC = 1024;*/

// Test
//pCommandTable->pPrdTable = pPRDTable;

pCommandTable->PrdTable[0].DBA =
MmGetPhysicalAddress(pDataBase);
//pCommandTable->PrdTable[0].DW3.bx.bit0 = 0x01;

pCommandTable->PrdTable[0].DW3.bx.DBC = 513;
pCommandTable->PrdTable[0].DW3.bx.bit1 = 0x01;

// Allow HBA Send DMA setup FIS
pPdoDevExt->papdPortRegBase->ci |= 0x02;
pPdoDevExt->papdPortRegBase->sact |= 0x02;


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer

> pCommandTableBase = MmAllocateContiguousMemorySpecifyCache(sizeof(COMMAND_TABLE)

Forget this all.

First of all, storage controller driver must be a miniport to SCSIPORT or STORPORT.

Second, MmAllocateContiguousMemorySpecifyCache is a wrong call, IoGetDmaAdapter+AllocateCommonBuffer is correct.


Maxim S. Shatskih
Windows DDK MVP
xxxxx@storagecraft.com
http://www.storagecraft.com

Dear Maxim S. Shatskih
thank you very much for your answer. Could you answer my following questions?

>Forget this all.
>First of all, storage controller driver must be a miniport to SCSIPORT or
>STORPORT.
Do you mean my AHCI controller driver should be miniport to scsiport or storport? or port driver enumerated by AHCI controller driver should be miniport to scsiport or storport?

>Second, MmAllocateContiguousMemorySpecifyCache is a wrong call,
>IoGetDmaAdapter+AllocateCommonBuffer is correct.

Because AHCI controller have 32 ports and 2 DMA engines in every port.
How could I get DMA Adapter by calling IoGetDmaAdapter ? In AHCI’s IRP_MN_PNP_START or prot driver’s PNP START routine?

thanks

Denny

> Do you mean my AHCI controller driver should be miniport to scsiport or storport?

Yes.

or port driver enumerated by AHCI controller driver should be miniport to scsiport or storport?

I don’t know what is “port driver enumerated by AHCI controller”.

Look at MSAHCI.SYS and on what APIs it uses. I think that WDK even has the source for it (for Vista+).

How could I get DMA Adapter by calling IoGetDmaAdapter ? In AHCI’s IRP_MN_PNP_START or

Forget IRP_MN_PNP_START. Forget WDM.

The storage controller driver must be a miniport to SCSIPORT (pre-2003) or STORPORT.


Maxim S. Shatskih
Windows DDK MVP
xxxxx@storagecraft.com
http://www.storagecraft.com