Question on ATA/SCSI IOCTLs

Sorry - please note the correction.

Hi All,

I am running into another issue and wondered if you might have the answer.

On large ATA drives (> 137.4 GB) the LBA addressing I use does not seem to be the same as the small drives.

I have a program that uses the WinAPI read file and multiplies the LBA address with the sector size and sets that as the file pointer offset (for the physical drive 0) and reads a sector.
Using the IOCTL call (both SCSI and ATA) I read the sector with the LBA.
They both match upto LBA 0x3f (also the number of sectors per track is 0x3f or 63). After that the IOCTL calls return arbitrary results. I am using WinHex to see what exactly the returned bytes match to.

My code is as follows:

"
bool ReadMBRUsingScsiPassThruDirect10withSense(int i, std::vector& vIoctlSector)
{
BOOL status = 0;
DWORD accessMode = 0, shareMode = 0;
ULONG alignmentMask = 0; // default == no alignment requirement
PUCHAR dataBuffer = NULL;
PUCHAR pUnAlignedBuffer = NULL;
SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sptwd;
int sectorSize = 512;

HANDLE phDisk = NULL;
phDisk= CreateFile(L"\\.\physicaldrive0",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);
if(phDisk == INVALID_HANDLE_VALUE)
return false;

GetAlignmentMaskForDevice(phDisk,&alignmentMask);

ZeroMemory(&sptwd, sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER));
dataBuffer = AllocateAlignedBuffer(sectorSize,alignmentMask, &pUnAlignedBuffer);

ZeroMemory(dataBuffer,sectorSize);

sptwd.sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
sptwd.sptd.CdbLength = CDB10GENERIC_LENGTH;
sptwd.sptd.DataIn = SCSI_IOCTL_DATA_IN;
sptwd.sptd.SenseInfoLength = SPT_SENSE_LENGTH;
sptwd.sptd.DataTransferLength = sectorSize;
sptwd.sptd.TimeOutValue = 50;
sptwd.sptd.DataBuffer = dataBuffer;
sptwd.sptd.SenseInfoOffset =
offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER,ucSenseBuf);;

sptwd.sptd.Cdb[0] = 0x28;
sptwd.sptd.Cdb[1] = 0x04; //Set to force unit access so that cached data is not used.
sptwd.sptd.Cdb[2] = (i >> 24 ) & 0xFF;
sptwd.sptd.Cdb[3] = (i >> 16 ) & 0xFF;
sptwd.sptd.Cdb[4] = (i >> 8 ) & 0xFF;
sptwd.sptd.Cdb[5] = (i>> 0 ) & 0xFF;
sptwd.sptd.Cdb[6] = 0;
sptwd.sptd.Cdb[7] = (UCHAR)(1 >> 8);
sptwd.sptd.Cdb[8] = 1;
sptwd.sptd.Cdb[9] = 0;
ULONG length = sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER);
ULONG returned = 0;
status = DeviceIoControl(phDisk,
IOCTL_SCSI_PASS_THROUGH_DIRECT,
&sptwd,
length,
&sptwd,
length,
&returned,
FALSE);

if ((sptwd.sptd.ScsiStatus == 0) && (status != 0)) {
printf(“Using IOCTL SCSI 10\n”);
PrintDataBuffer(dataBuffer,sptwd.sptd.DataTransferLength);
vIoctlSector.resize(sptwd.sptd.DataTransferLength);
memcpy(vIoctlSector.data(), dataBuffer, sptwd.sptd.DataTransferLength);
}
// free(pUnAlignedBuffer);
DWORD error = GetLastError();
CloseHandle(phDisk);
return true;
}

ATA:

bool ReadMBRUsingATAPassthru(int i, std::vector& vATA)
{
HANDLE handle;

UCHAR buffer[512] = {0};

DWORD bytesReturned = 0;

DWORD outBufferSize = sizeof(ATA_PASS_THROUGH_DIRECT);

int bytescopied = 0;

WCHAR fileName = (WCHAR * ) “\\.\PHYSICALDRIVE0”;

int errorCode = 0;

int bResult = 0;

int ioControlCode = IOCTL_ATA_PASS_THROUGH_DIRECT;

unsigned int inputBufferSize = sizeof(ATA_PASS_THROUGH_DIRECT);

ATA_PASS_THROUGH_DIRECT inputBuffer = {0};
memset(&inputBuffer,0,40);
memset(buffer, 0, 512);

inputBuffer.AtaFlags = 03 | ATA_FLAGS_48BIT_COMMAND ;

inputBuffer.Length = 40;

inputBuffer.DataTransferLength = 512;

inputBuffer.TimeOutValue = 500;
inputBuffer.DataBuffer = buffer;
inputBuffer.Lun = 0;
inputBuffer.PathId = 0;
inputBuffer.TargetId = 0;

inputBuffer.PreviousTaskFile[0] = 0;
inputBuffer.PreviousTaskFile[1] = 0;
inputBuffer.PreviousTaskFile[2] = (i >> 24) & 0xFF;;
inputBuffer.PreviousTaskFile[3] = (i >> 32 ) & 0xFF;
inputBuffer.PreviousTaskFile[4] = (i >> 40 ) & 0xFF;;
inputBuffer.PreviousTaskFile[5] = 0xE0;
inputBuffer.PreviousTaskFile[6] = 0;
inputBuffer.PreviousTaskFile[7] = 0;

inputBuffer.CurrentTaskFile[0] = 0;
inputBuffer.CurrentTaskFile[1] = 1;
inputBuffer.CurrentTaskFile[2] = (i >> 0 ) & 0xFF;;
inputBuffer.CurrentTaskFile[3] = (i >> 8 ) & 0xFF;;
inputBuffer.CurrentTaskFile[4] = (i >> 16 ) & 0xFF;;
inputBuffer.CurrentTaskFile[5] = 0xE0;
inputBuffer.CurrentTaskFile[6] = 0x20;
inputBuffer.CurrentTaskFile[7] = 0;

handle= CreateFile(L"\\.\PhysicalDrive0",GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);

printf(“\n Error = %d”, GetLastError());

if(handle)

{

DeviceIoControl(

handle,

ioControlCode,

&inputBuffer,

outBufferSize,

&inputBuffer,

outBufferSize,

&bytesReturned,

NULL //not an overlapped operation

);

printf(“IOCTL ATA\n”);
printf(“\n status is %d”,inputBuffer.CurrentTaskFile[6]);

printf(“\n Error = %d”, GetLastError());
PrintDataBuffer(buffer, 512);
memcpy(&vATA[0],buffer, 512);

}
CloseHandle(handle);
return true;

}

For ATA the inputBuffer.CurrentTaskFile[6] = 0x20;
for 48 bit is 0x24 (or read Ext - but that works only till LBA 2 and 3 returns 0’s with error 81 or invalid arguments).

Via the API:

bool ReadMBRSystemDrive(int i, std::vector& vBytes, DWORD& size)
{
// Open the physical hard drive
HANDLE phDisk = NULL;
phDisk= CreateFile(L"\\.\physicaldrive0",GENERIC_READ,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
DWORD ret = GetLastError();
if(!(phDisk != NULL && GetLastError() ==0) )
return false;
// Retrieve the sector size
DWORD dwReturnLength = 0;
DISK_GEOMETRY infoDiskGeometry;
if( !::DeviceIoControl(phDisk, IOCTL_DISK_GET_DRIVE_GEOMETRY,
NULL, 0,
&infoDiskGeometry, sizeof(infoDiskGeometry),
&dwReturnLength, NULL) )
{
DWORD dwErr = GetLastError();
return false;
}
size = infoDiskGeometry.BytesPerSector;
// A vector large enough to hold one sector
vBytes.resize(infoDiskGeometry.BytesPerSector);
// Read first sector from the disk
if( SetFilePointer(phDisk, (i
infoDiskGeometry.BytesPerSector), NULL,FILE_BEGIN) == INVALID_SET_FILE_POINTER )
return false;
DWORD numberRead = 0;
if(! ReadFile(phDisk, reinterpret_cast(&vBytes[0]),infoDiskGeometry.BytesPerSector,&numberRead,NULL) )
{
return false;
}
CloseHandle(phDisk);
return true;
}

And I am looping

while ( true )
{
DWORD size = 0;
std::vector sysDriveApi;
ReadMBRSystemDrive( i, sysDriveApi, size);
std::vector sysDriveScsi (size);
ReadMBRUsingScsiPassThruDirect10withSense(i, sysDriveScsi);
std::vector sysDriveAta (size );
ReadMBRUsingATAPassthru( i, sysDriveAta);
if( memcmp(&sysDriveApi[0],&sysDriveScsi[0], size) != 0 ||
memcmp(&sysDriveApi[0],&sysDriveAta[0], size) != 0 )
{
printf(" LBA %d does not match \n", i);
PrintDataBuffer(reinterpret_cast(&sysDriveApi[0]), size);
PrintDataBuffer(reinterpret_cast(&sysDriveScsi[0]), size);
PrintDataBuffer(reinterpret_cast(&sysDriveAta[0]), size);
if( memcmp( &sysDriveScsi[0], &sysDriveAta[0], size) != 0)
{
printf(" LBA SCSI/ATA %d does not match \n", i);
}
//break;
}
i++;

}

With 0x24 in the ATA command it breaks at LBA 3 with ATA returning 0’s and error 81.
With 0x20, it fails on LBA 3f where SCSI and ATA match but are both incorrect.

Please note this is just working code (it has not been cleaned up and might have some minor inconsistencies).

I would appreciate any guidance/ test code that might be pointed to.

Thanks,
Sonia.