none
Write DMA Not Able to Write whole Drive Capacity RRS feed

  • Question

  • I am trying to write a pattern on my target SATA drive with a capacity of 512GB using IOCTL_ATA_PASS_THROUGH_DIRECT utilizing the Write DMA EXT Raw ATA/ATAPI command.

    Similarly, I also use the IOCTL_DISK_GET_DRIVE_GEOMETRY_EX control code in order to get the actual capacity of the target SATA drive.

    Next, I also use the IOCTL_STORAGE_QUERY_PROPERTY control code in order to get the maximum transfer length that my hardware controller can support. This maximum transfer length will use to allocate the buffer size to which the buffer will be initialize to my preferred data pattern.

    My issue here is when I use the maximum transfer length as the buffer size, the Write DMA EXT is not able to write the whole capacity with my preferred pattern.

    But when I lower the transfer length to 8KB (this will be the size of the data buffer), Write DMA EXT is able to write a data pattern on the entire drive.

    I am clueless as to what's wrong with it, someone in the community might be able to provide me an answer.

    Below are my snippet code.

    DevObj.DeviceHandle = CreateFile(WchPtr, GENERIC_READ | GENERIC_WRITE,
                                     FILE_SHARE_READ | FILE_SHARE_WRITE,
                                     NULL, OPEN_EXISTING,
                                     FILE_FLAG_NO_BUFFERING, NULL);
    
    if (DevObj.DeviceHandle == INVALID_HANDLE_VALUE)
    {
    	return RetStatus = DEVIO_INVALID_DEVICE_HANDLE;
    }
    
    DevObj.CurrentTaskFile[ATA_SECTOR_COUNT_REG] = (SectorCount & LBA_BYTE_MASK);
    DevObj.CurrentTaskFile[ATA_COMMAND_REG] = DevObj.OpCode;
    
    if (DeviceInfoPtr->Lba48Bit)
    {
    	DevObj.PreviousTaskFile[ATA_SECTOR_COUNT_REG] = (SectorCount >> RIGHT_SHIFT_BY_8) & LBA_BYTE_MASK;
    }
    
    Lba = NUMB_ZERO;
    CDefines::CurLbaCount = Lba;
    CDefines::MaxLbaCount = DeviceInfoPtr->CapacityInLba;
    
    IoControlCode = DEVIO_ATA_PASS_THROUGH_DIRECT_CTL_CODE;
    
    do {  
    	devio_ata_read_write_dma_exec(DeviceInfoPtr->Lba48Bit, 
    								  DevIdx, Lba, &DevObj);
    
    	devio_ata_prepare_command(&DevObj, &AptDirect);
    
    	DevIoCtlResult = DeviceIoControl(DevObj.DeviceHandle,
    									 IoControlCode,
    									 (void *)&AptDirect,
    									 sizeof(ATA_PASS_THROUGH_DIRECT_STRUCT),
    									 (void *)&AptDirect,
    									 sizeof(ATA_PASS_THROUGH_DIRECT_STRUCT),
    									 &BytesReturned,
    									 NULL);
    
    	if (CDefines::CancelOperation == OPER_SECU_ERASE_ABORTED)
    	{
    		RetStatus = UNSUCCESSFUL;
    		break;
    	}
    
    	Err = GetLastError();
    	if (Err != SUCCESSFUL)
    	{
    		RetStatus = UNSUCCESSFUL;
    		break;
    	}
    
    	if (DevIoCtlResult == DEVIO_CNTRL_STATUS_ERR)
    	{
    		RetStatus = UNSUCCESSFUL;
    		break;
    	}
    
    	Lba += SectorCount; 
    	CDefines::CurLbaCount = Lba;
    } while (Lba < DeviceInfoPtr->CapacityInLba);
    

    Monday, May 4, 2015 1:25 PM

Answers

  • I believe the problem is that you're effectively trying to write past the end of the drive. If it stopped at LBA 1000215040 and there are 1000215215 sectors on the drive, the difference is 87.5KB, so trying to write more than 87.5KB at that position will cause it to fail.

     -Brian


    Azius Developer Training www.azius.com Windows device driver, internals, security, & forensics training and consulting. Blog at www.azius.com/blog

    Tuesday, May 5, 2015 6:36 PM
    Moderator

All replies

  • What is the error status you're getting?

     -Brian


    Azius Developer Training www.azius.com Windows device driver, internals, security, & forensics training and consulting. Blog at www.azius.com/blog

    Monday, May 4, 2015 9:43 PM
    Moderator
  • Hi Brian,

    I added this code to trap if their is an error in the Write DMA EXT command.

    if ((AptDirect.CurrentTaskFile[ATA_COMMAND_REG] & ERROR_BIT_MASK) == ERROR_BIT_MASK)
    {
        RetStatus = DEVIO_SEND_CMD_BLK_ERR;
        break;
    }

    At almost at the last section of the drive capacity, an error was hit on the trap for ATA command error bit. When I check the current and previous task file register for the sector address for the occurrence of the error.

    After decoding, the sector address (or LBA) is 0x3b9e1200 (or 1000215040) and I check the error register, it says that IDNF is set, meaning if a user-accessible address could not be found IDNF shall be set to one if an address outside of the range of user-accessible addresses is requested if command aborted is
    not returned.

    The capacity of the target SATA is 512GB (512110190592), this also mean that my last addressable sector is,

    LastLBA = (DrvCapacity / 512) - 1 = 1000215215

    If you compare the LastLBA and the LBA reported from the error register it seems that my mathematics is wrong.

    The IOCTL_STORAGE_QUERY_PROPERTY control code returned a maximum transfer length of 128KB for my SATA controller. Given all this information, this how my function work in pseudo code.

    get_last_lba(DevPath, &LastLBA); // this is thru IOCTL_DISK_GET_DRIVE_GEOMETRY_EX get_adapter_property(DevPath, &MaxTransferLen); // this is thru IOCTL_STORAGE_QUERY_PROPERTY SectorCount = MaxTransferLen / HALF_KB; DataBufferPtr = new unsigned char[(MaxTransferLen + HALF_KB)]; Data2BufferPtr = new unsigned char[(MaxTransferLen + HALF_KB)]; Lba = 0; do { // fill-up the fields needed for write dma ata_write_dma_prepare(...); // setup the ata pass through structure ata_setup_structure(...);

    // send the ATA command Status = DeviceIoControl(...);

    Lba += SectorCount; } while (Lba <= LastLBA);

    Basing from my calculation, I think their is nothing wrong unless I miss small details. And I need help from the community.

    Below is the snippet for setting-up the sector count and the ata pass through structure.

            DevObj.CurrentTaskFile[ATA_SECTOR_COUNT_REG] = (SectorCount & LBA_BYTE_MASK);
            DevObj.CurrentTaskFile[ATA_COMMAND_REG] = DevObj.OpCode;
    
            // for 48-bit addressing
            if (DeviceInfoPtr->Lba48Bit)
            {
                DevObj.PreviousTaskFile[ATA_SECTOR_COUNT_REG] = (SectorCount >> RIGHT_SHIFT_BY_8) & LBA_BYTE_MASK;
            }

        void CDeviceIo::devio_ata_prepare_command (DEVICE_OBJECT_STRUCT *DevObjPtr,
                                                   ATA_PASS_THROUGH_DIRECT_STRUCT *AptDirectPtr)
        {
            // setup the transfer length and the structure size
            AptDirectPtr->Length = sizeof(ATA_PASS_THROUGH_DIRECT_STRUCT);
            AptDirectPtr->TimeOutValue = NUMB_FOUR;
    
            // set the ata flags required for the operation
            AptDirectPtr->DataTransferLength = DevObjPtr->TransferLen;
            AptDirectPtr->AtaFlags = DEVIO_ATA_FLAGS_DRDY_REQUIRED;
    
            // set the data direction
            if (DevObjPtr->Direction == DEVIO_ATA_FLAGS_DATA_IN)
            {
                AptDirectPtr->AtaFlags = AptDirectPtr->AtaFlags | DEVIO_ATA_FLAGS_DATA_IN;
            }
            else if (DevObjPtr->Direction == DEVIO_ATA_FLAGS_DATA_OUT)
            {
                AptDirectPtr->AtaFlags = AptDirectPtr->AtaFlags | DEVIO_ATA_FLAGS_DATA_OUT;
            }
            else
            {
                ;;;  // this is for invalid data direction
            }
    
            if (    DevObjPtr->OpCode == ATA_READ_DMA_EXT_COMMAND
                 || DevObjPtr->OpCode == ATA_WRITE_DMA_EXT_COMMAND
                 || DevObjPtr->OpCode == ATA_READ_NATIVE_MAX_ADDRESS_EXT_COMMAND)
            {
                AptDirectPtr->AtaFlags = AptDirectPtr->AtaFlags | DEVIO_ATA_FLAGS_48BIT_COMMAND;
            }
    
            // check if required to have DMA transfer
            if (    DevObjPtr->OpCode == ATA_READ_DMA_EXT_COMMAND
                 || DevObjPtr->OpCode == ATA_WRITE_DMA_EXT_COMMAND
                 || DevObjPtr->OpCode == ATA_READ_DMA_COMMAND
                 || DevObjPtr->OpCode == ATA_WRITE_DMA_COMMAND)
            {
                AptDirectPtr->AtaFlags = AptDirectPtr->AtaFlags | DEVIO_ATA_FLAGS_USE_DMA;
            }
    
            // setup the buffer and the task file register
            AptDirectPtr->DataBufferPtr = (void *)DevObjPtr->BufferPtr;
    
            // setup task file register for 28-bit operation
            AptDirectPtr->CurrentTaskFile[ATA_FEATURE_REG] = DevObjPtr->CurrentTaskFile[ATA_FEATURE_REG];
            AptDirectPtr->CurrentTaskFile[ATA_SECTOR_COUNT_REG] = DevObjPtr->CurrentTaskFile[ATA_SECTOR_COUNT_REG];
            AptDirectPtr->CurrentTaskFile[ATA_SECTOR_NUMBER_REG] = DevObjPtr->CurrentTaskFile[ATA_SECTOR_NUMBER_REG];
            AptDirectPtr->CurrentTaskFile[ATA_CYLLOW_REG] = DevObjPtr->CurrentTaskFile[ATA_CYLLOW_REG];
            AptDirectPtr->CurrentTaskFile[ATA_CYLHIGH_REG] = DevObjPtr->CurrentTaskFile[ATA_CYLHIGH_REG];
            AptDirectPtr->CurrentTaskFile[ATA_DRIVE_HEAD_REG] = DevObjPtr->CurrentTaskFile[ATA_DRIVE_HEAD_REG];
            AptDirectPtr->CurrentTaskFile[ATA_COMMAND_REG] = DevObjPtr->CurrentTaskFile[ATA_COMMAND_REG];
            AptDirectPtr->CurrentTaskFile[ATA_RESERVED_REG] = DevObjPtr->CurrentTaskFile[ATA_RESERVED_REG];
    
            if (    DevObjPtr->OpCode == ATA_READ_DMA_EXT_COMMAND
                 || DevObjPtr->OpCode == ATA_WRITE_DMA_EXT_COMMAND
                 || DevObjPtr->OpCode == ATA_READ_NATIVE_MAX_ADDRESS_EXT_COMMAND)
            {
                // setup the prev task file register for 48bit command
                AptDirectPtr->PreviousTaskFile[ATA_FEATURE_REG] = DevObjPtr->PreviousTaskFile[ATA_FEATURE_REG];
                AptDirectPtr->PreviousTaskFile[ATA_SECTOR_COUNT_REG] = DevObjPtr->PreviousTaskFile[ATA_SECTOR_COUNT_REG];
                AptDirectPtr->PreviousTaskFile[ATA_SECTOR_NUMBER_REG] = DevObjPtr->PreviousTaskFile[ATA_SECTOR_NUMBER_REG];
                AptDirectPtr->PreviousTaskFile[ATA_CYLLOW_REG] = DevObjPtr->PreviousTaskFile[ATA_CYLLOW_REG];
                AptDirectPtr->PreviousTaskFile[ATA_CYLHIGH_REG] = DevObjPtr->PreviousTaskFile[ATA_CYLHIGH_REG];
                AptDirectPtr->PreviousTaskFile[ATA_DRIVE_HEAD_REG] = DevObjPtr->PreviousTaskFile[ATA_DRIVE_HEAD_REG];
                AptDirectPtr->PreviousTaskFile[ATA_COMMAND_REG] = DevObjPtr->PreviousTaskFile[ATA_COMMAND_REG];
                AptDirectPtr->PreviousTaskFile[ATA_RESERVED_REG] = DevObjPtr->PreviousTaskFile[ATA_RESERVED_REG];
            }
    
            return;
        }

    Tuesday, May 5, 2015 1:44 PM
  • I believe the problem is that you're effectively trying to write past the end of the drive. If it stopped at LBA 1000215040 and there are 1000215215 sectors on the drive, the difference is 87.5KB, so trying to write more than 87.5KB at that position will cause it to fail.

     -Brian


    Azius Developer Training www.azius.com Windows device driver, internals, security, & forensics training and consulting. Blog at www.azius.com/blog

    Tuesday, May 5, 2015 6:36 PM
    Moderator
  • Thanks Brian for the help.

    I made changes on the code in order to access within the boundary.


    • Edited by rrb011270 Saturday, May 9, 2015 2:13 AM
    Saturday, May 9, 2015 2:13 AM