windows device trim ioctl ata

¿Enviando comandos ATA directamente al dispositivo en Windows?



device trim (3)

Estoy intentando enviar comandos ATA a un disco físico en Windows y obtener la respuesta del dispositivo.

Nota: En este caso, deseo enviar el comando IDENTIFY DEVICE (0xEC). El dispositivo responderá con un bloque de datos de 512 bytes. (En particular, estoy interesado en el bit 0 de la palabra 119, el soporte del dispositivo para el comando TRIM ).

Sé que necesito usar CreateFile para abrir el dispositivo:

handle = CreateFile( "//./PhysicalDrive0", GENERIC_READ, FILE_SHARE_READ, nil, // no security attributes OPEN_EXISTING, 0, // flags and attributes nil // no template file );

Pero después de esto estoy bloqueado sobre qué hacer.

Pensé en enviar 0xEC usando [DeviceIoControl][4] :

// const ATACommand_IdentifyDevice = 0xEC; uint bytesReturned = 0; DeviceIoControl(handle, 0xEC, // IO Control Code nil, // input buffer not needed 0, // input buffer is zero bytes @buffer, // output buffer to store the returned 512-bytes 512, // output buffer is 512 bytes long out bytesReturned, nil // not an overlapped operation );

Pero esto es completamente incorrecto. Un IoControlCode enviado a DeviceIoControl debe ser un IO_CTL válido, que se construye utilizando la macro :

#define CTL_CODE(DeviceType, Function, Method, Access) ( ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) )

Mirando el SDK, hay varios códigos de control de administración de discos válidos, por ejemplo:

  • IOCTL_DISK_CREATE_DISK
  • IOCTL_DISK_GET_DRIVE_GEOMETRY
  • IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
  • IOCTL_DISK_GET_PARTITION_INFO
  • IOCTL_STORAGE_QUERY_PROPERTY

Pero ninguno de ellos es el comando IDENTIFY DEVICE o devolver nada de lo que devuelve.

Así que creo que tengo que usar algún método "crudo" para enviar comandos.

Buscando alrededor, me encontré con IOCTL sin documentar

#define DFP_RECEIVE_DRIVE_DATA 0x0007c088

Que al desglosar las piezas de IOCTL significa:

Custom: (0) Device Type: (7) FILE_DEVICE_DISK Required Access: (3) METHOD_NEITHER Custom: (0) Function Code: (34) Transfer Type: (0)

Pero no hay documentación en ningún lugar sobre qué debe contener el inputBuffer , su tamaño y qué outputBuffer su outputBuffer , o su requerimiento. Tampoco puedo averiguar qué functionCode 34 (0x22) es.

Mi pregunta: ¿Cómo envío comandos ATA sin procesar (por ejemplo, 0xEC) a un dispositivo ATA y leo su respuesta?

Ver también

Piezas de respuesta

Abra la unidad con acceso de ReadWrite:

handle = CreateFile( "//./PhysicalDrive0", GENERIC_READ or GENERIC_WRITE, // IOCTL_ATA_PASS_THROUGH requires read-write FILE_SHARE_READ, nil, // no security attributes OPEN_EXISTING, 0, // flags and attributes nil // no template file );

Configure una estructura ATA_PASS_THROUGH_EX como nuestro búfer de entrada para usar con el código de control IOCTL_ATA_PASS_THROUGH IO:

ATA_PASS_THROUGH_EX inputBuffer; inputBuffer.Length = sizeof(ATA_PASS_THROUGH_EX); inputBuffer.AtaFlags = ATA_FLAGS_DATA_IN; inputBuffer.DataTransferLength = 0; inputBuffer.DataBufferOffset = 0; // todo: put the ATA command (e.g. 0xEC) somewhere uint inputBufferSize = sizeof(ATA_PASS_THROUGH_EX);

Configure un búfer de salida para mantener nuestra respuesta esperada de 512 bytes desde la unidad:

Byte[] outputBuffer = new Byte[512]; uint outputBufferSize = 512;

Llame a DeviceIoControl :

int ioControlCode = IOCTL_ATA_PASS_THROUGH; // or maybe IOCTL_ATA_PASS_THROUGH_DIRECT uint bytesReturned = 0; DeviceIoControl(handle, ioControlCode, inputBuffer, inputBufferSize, outputBuffer, outputBufferSize, out bytesReturned, nil // not an overlapped operation );

Cierre el identificador de archivo:

handle.Close();


Basado en la respuesta https://.com/a/5071027/15485 por , escribí el siguiente código independiente. Lo probé en una computadora portátil DELL con un disco SSD y ejecutando Windows 7.

// Sending ATA commands directly to device in Windows? // https://.com/questions/5070987/sending-ata-commands-directly-to-device-in-windows #include <Windows.h> #include <ntddscsi.h> // for ATA_PASS_THROUGH_EX #include <iostream> // I have copied the struct declaration from // "IDENTIFY_DEVICE_DATA structure" http://msdn.microsoft.com/en-us/library/windows/hardware/ff559006(v=vs.85).aspx // I think it is better to include the suitable header (MSDN says the header is Ata.h and suggests to include Irb.h) typedef struct _IDENTIFY_DEVICE_DATA { struct { USHORT Reserved1 :1; USHORT Retired3 :1; USHORT ResponseIncomplete :1; USHORT Retired2 :3; USHORT FixedDevice :1; USHORT RemovableMedia :1; USHORT Retired1 :7; USHORT DeviceType :1; } GeneralConfiguration; USHORT NumCylinders; USHORT ReservedWord2; USHORT NumHeads; USHORT Retired1[2]; USHORT NumSectorsPerTrack; USHORT VendorUnique1[3]; UCHAR SerialNumber[20]; USHORT Retired2[2]; USHORT Obsolete1; UCHAR FirmwareRevision[8]; UCHAR ModelNumber[40]; UCHAR MaximumBlockTransfer; UCHAR VendorUnique2; USHORT ReservedWord48; struct { UCHAR ReservedByte49; UCHAR DmaSupported :1; UCHAR LbaSupported :1; UCHAR IordyDisable :1; UCHAR IordySupported :1; UCHAR Reserved1 :1; UCHAR StandybyTimerSupport :1; UCHAR Reserved2 :2; USHORT ReservedWord50; } Capabilities; USHORT ObsoleteWords51[2]; USHORT TranslationFieldsValid :3; USHORT Reserved3 :13; USHORT NumberOfCurrentCylinders; USHORT NumberOfCurrentHeads; USHORT CurrentSectorsPerTrack; ULONG CurrentSectorCapacity; UCHAR CurrentMultiSectorSetting; UCHAR MultiSectorSettingValid :1; UCHAR ReservedByte59 :7; ULONG UserAddressableSectors; USHORT ObsoleteWord62; USHORT MultiWordDMASupport :8; USHORT MultiWordDMAActive :8; USHORT AdvancedPIOModes :8; USHORT ReservedByte64 :8; USHORT MinimumMWXferCycleTime; USHORT RecommendedMWXferCycleTime; USHORT MinimumPIOCycleTime; USHORT MinimumPIOCycleTimeIORDY; USHORT ReservedWords69[6]; USHORT QueueDepth :5; USHORT ReservedWord75 :11; USHORT ReservedWords76[4]; USHORT MajorRevision; USHORT MinorRevision; struct { USHORT SmartCommands :1; USHORT SecurityMode :1; USHORT RemovableMediaFeature :1; USHORT PowerManagement :1; USHORT Reserved1 :1; USHORT WriteCache :1; USHORT LookAhead :1; USHORT ReleaseInterrupt :1; USHORT ServiceInterrupt :1; USHORT DeviceReset :1; USHORT HostProtectedArea :1; USHORT Obsolete1 :1; USHORT WriteBuffer :1; USHORT ReadBuffer :1; USHORT Nop :1; USHORT Obsolete2 :1; USHORT DownloadMicrocode :1; USHORT DmaQueued :1; USHORT Cfa :1; USHORT AdvancedPm :1; USHORT Msn :1; USHORT PowerUpInStandby :1; USHORT ManualPowerUp :1; USHORT Reserved2 :1; USHORT SetMax :1; USHORT Acoustics :1; USHORT BigLba :1; USHORT DeviceConfigOverlay :1; USHORT FlushCache :1; USHORT FlushCacheExt :1; USHORT Resrved3 :2; USHORT SmartErrorLog :1; USHORT SmartSelfTest :1; USHORT MediaSerialNumber :1; USHORT MediaCardPassThrough :1; USHORT StreamingFeature :1; USHORT GpLogging :1; USHORT WriteFua :1; USHORT WriteQueuedFua :1; USHORT WWN64Bit :1; USHORT URGReadStream :1; USHORT URGWriteStream :1; USHORT ReservedForTechReport :2; USHORT IdleWithUnloadFeature :1; USHORT Reserved4 :2; } CommandSetSupport; struct { USHORT SmartCommands :1; USHORT SecurityMode :1; USHORT RemovableMediaFeature :1; USHORT PowerManagement :1; USHORT Reserved1 :1; USHORT WriteCache :1; USHORT LookAhead :1; USHORT ReleaseInterrupt :1; USHORT ServiceInterrupt :1; USHORT DeviceReset :1; USHORT HostProtectedArea :1; USHORT Obsolete1 :1; USHORT WriteBuffer :1; USHORT ReadBuffer :1; USHORT Nop :1; USHORT Obsolete2 :1; USHORT DownloadMicrocode :1; USHORT DmaQueued :1; USHORT Cfa :1; USHORT AdvancedPm :1; USHORT Msn :1; USHORT PowerUpInStandby :1; USHORT ManualPowerUp :1; USHORT Reserved2 :1; USHORT SetMax :1; USHORT Acoustics :1; USHORT BigLba :1; USHORT DeviceConfigOverlay :1; USHORT FlushCache :1; USHORT FlushCacheExt :1; USHORT Resrved3 :2; USHORT SmartErrorLog :1; USHORT SmartSelfTest :1; USHORT MediaSerialNumber :1; USHORT MediaCardPassThrough :1; USHORT StreamingFeature :1; USHORT GpLogging :1; USHORT WriteFua :1; USHORT WriteQueuedFua :1; USHORT WWN64Bit :1; USHORT URGReadStream :1; USHORT URGWriteStream :1; USHORT ReservedForTechReport :2; USHORT IdleWithUnloadFeature :1; USHORT Reserved4 :2; } CommandSetActive; USHORT UltraDMASupport :8; USHORT UltraDMAActive :8; USHORT ReservedWord89[4]; USHORT HardwareResetResult; USHORT CurrentAcousticValue :8; USHORT RecommendedAcousticValue :8; USHORT ReservedWord95[5]; ULONG Max48BitLBA[2]; USHORT StreamingTransferTime; USHORT ReservedWord105; struct { USHORT LogicalSectorsPerPhysicalSector :4; USHORT Reserved0 :8; USHORT LogicalSectorLongerThan256Words :1; USHORT MultipleLogicalSectorsPerPhysicalSector :1; USHORT Reserved1 :2; } PhysicalLogicalSectorSize; USHORT InterSeekDelay; USHORT WorldWideName[4]; USHORT ReservedForWorldWideName128[4]; USHORT ReservedForTlcTechnicalReport; USHORT WordsPerLogicalSector[2]; struct { USHORT ReservedForDrqTechnicalReport :1; USHORT WriteReadVerifySupported :1; USHORT Reserved01 :11; USHORT Reserved1 :2; } CommandSetSupportExt; struct { USHORT ReservedForDrqTechnicalReport :1; USHORT WriteReadVerifyEnabled :1; USHORT Reserved01 :11; USHORT Reserved1 :2; } CommandSetActiveExt; USHORT ReservedForExpandedSupportandActive[6]; USHORT MsnSupport :2; USHORT ReservedWord1274 :14; struct { USHORT SecuritySupported :1; USHORT SecurityEnabled :1; USHORT SecurityLocked :1; USHORT SecurityFrozen :1; USHORT SecurityCountExpired :1; USHORT EnhancedSecurityEraseSupported :1; USHORT Reserved0 :2; USHORT SecurityLevel :1; USHORT Reserved1 :7; } SecurityStatus; USHORT ReservedWord129[31]; struct { USHORT MaximumCurrentInMA2 :12; USHORT CfaPowerMode1Disabled :1; USHORT CfaPowerMode1Required :1; USHORT Reserved0 :1; USHORT Word160Supported :1; } CfaPowerModel; USHORT ReservedForCfaWord161[8]; struct { USHORT SupportsTrim :1; USHORT Reserved0 :15; } DataSetManagementFeature; USHORT ReservedForCfaWord170[6]; USHORT CurrentMediaSerialNumber[30]; USHORT ReservedWord206; USHORT ReservedWord207[2]; struct { USHORT AlignmentOfLogicalWithinPhysical :14; USHORT Word209Supported :1; USHORT Reserved0 :1; } BlockAlignment; USHORT WriteReadVerifySectorCountMode3Only[2]; USHORT WriteReadVerifySectorCountMode2Only[2]; struct { USHORT NVCachePowerModeEnabled :1; USHORT Reserved0 :3; USHORT NVCacheFeatureSetEnabled :1; USHORT Reserved1 :3; USHORT NVCachePowerModeVersion :4; USHORT NVCacheFeatureSetVersion :4; } NVCacheCapabilities; USHORT NVCacheSizeLSW; USHORT NVCacheSizeMSW; USHORT NominalMediaRotationRate; USHORT ReservedWord218; struct { UCHAR NVCacheEstimatedTimeToSpinUpInSeconds; UCHAR Reserved; } NVCacheOptions; USHORT ReservedWord220[35]; USHORT Signature :8; USHORT CheckSum :8; } IDENTIFY_DEVICE_DATA, *PIDENTIFY_DEVICE_DATA; // Taken from smartmontools // Copies n bytes (or n-1 if n is odd) from in to out, but swaps adjacents // bytes. static void swapbytes(char * out, const char * in, size_t n) { for (size_t i = 0; i < n; i += 2) { out[i] = in[i+1]; out[i+1] = in[i]; } } // Taken from smartmontools // Copies in to out, but removes leading and trailing whitespace. static void trim(char * out, const char * in) { // Find the first non-space character (maybe none). int first = -1; int i; for (i = 0; in[i]; i++) if (!isspace((int)in[i])) { first = i; break; } if (first == -1) { // There are no non-space characters. out[0] = ''/0''; return; } // Find the last non-space character. for (i = strlen(in)-1; i >= first && isspace((int)in[i]); i--) ; int last = i; strncpy(out, in+first, last-first+1); out[last-first+1] = ''/0''; } // Taken from smartmontools // Convenience function for formatting strings from ata_identify_device void ata_format_id_string(char * out, const unsigned char * in, int n) { bool must_swap = true; #ifdef __NetBSD__ /* NetBSD kernel delivers IDENTIFY data in host byte order (but all else is LE) */ // TODO: Handle NetBSD case in os_netbsd.cpp if (isbigendian()) must_swap = !must_swap; #endif char tmp[65]; n = n > 64 ? 64 : n; if (!must_swap) strncpy(tmp, (const char *)in, n); else swapbytes(tmp, (const char *)in, n); tmp[n] = ''/0''; trim(out, tmp); } int main(int argc, char* argv[]) { HANDLE handle = ::CreateFileA( "////.//PhysicalDrive0", GENERIC_READ | GENERIC_WRITE, //IOCTL_ATA_PASS_THROUGH requires read-write FILE_SHARE_READ, 0, //no security attributes OPEN_EXISTING, 0, //flags and attributes 0 //no template file ); if ( handle == INVALID_HANDLE_VALUE ) { std::cout << "Invalid handle/n"; } // IDENTIFY command requires a 512 byte buffer for data: const unsigned int IDENTIFY_buffer_size = 512; const BYTE IDENTIFY_command_ID = 0xEC; unsigned char Buffer[IDENTIFY_buffer_size + sizeof(ATA_PASS_THROUGH_EX)] = { 0 }; ATA_PASS_THROUGH_EX & PTE = *(ATA_PASS_THROUGH_EX *) Buffer; PTE.Length = sizeof(PTE); PTE.TimeOutValue = 10; PTE.DataTransferLength = 512; PTE.DataBufferOffset = sizeof(ATA_PASS_THROUGH_EX); // Set up the IDE registers as specified in ATA spec. IDEREGS * ir = (IDEREGS *) PTE.CurrentTaskFile; ir->bCommandReg = IDENTIFY_command_ID; ir->bSectorCountReg = 1; // IDENTIFY is neither 48-bit nor DMA, it reads from the device: PTE.AtaFlags = ATA_FLAGS_DATA_IN | ATA_FLAGS_DRDY_REQUIRED; DWORD BR = 0; BOOL b = ::DeviceIoControl(handle, IOCTL_ATA_PASS_THROUGH, &PTE, sizeof(Buffer), &PTE, sizeof(Buffer), &BR, 0); if ( b == 0 ) { std::cout << "Invalid call/n"; } IDENTIFY_DEVICE_DATA * data = (IDENTIFY_DEVICE_DATA *) (Buffer + sizeof(ATA_PASS_THROUGH_EX)); // Nota Bene: I think some endianness control is needed char model[40+1]; ata_format_id_string(model, data->ModelNumber, sizeof(model)-1); char serial[20+1]; ata_format_id_string(serial, data->SerialNumber, sizeof(serial)-1); char firmware[8+1]; ata_format_id_string(firmware, data->FirmwareRevision, sizeof(firmware)-1); std::cout << "ModelNumber: " << model << "/n"; std::cout << "SerialNumber: " << serial << "/n"; std::cout << "FirmwareRevision: " << firmware << "/n"; return 0; }


Debe utilizar IOCTL_ATA_PASS_THROUGH / IOCTL_ATA_PASS_THROUGH_DIRECT, estos están bastante bien documentados. Además, necesita el acceso GENERIC_READ | GENERIC_WRITE para CreateFile.

Tenga en cuenta que pre XP SP2 no los admite correctamente. Además, si tiene un MB basado en nForce con controladores nvidia, sus unidades SATA aparecerán como SCSI y no podrá usar este passthrough.

En algunos casos, las IOCTL SMART (por ejemplo, SMART_RCV_DRIVE_DATA) funcionarán en los controladores nForce. Puede usarlos para obtener datos de IDENTIFICACIÓN y SMART, pero no mucho más.

El código abierto smartmontools es un buen lugar para comenzar a buscar códigos de muestra.

EDITAR: Ejemplo de una aplicación hablando con dispositivos ATA.

EResult DeviceOperationManagerWin::executeATACommandIndirect(ATACommand & Cmd) { const uint32 FillerSize = 0; Utils::ByteBuffer B; B.reserve(sizeof(ATA_PASS_THROUGH_EX) + 4 + Cmd.bufferSize()); ATA_PASS_THROUGH_EX & PTE = * (ATA_PASS_THROUGH_EX *) B.appendPointer(sizeof(ATA_PASS_THROUGH_EX) + FillerSize + Cmd.bufferSize()); uint8 * DataPtr = ((uint8 *) &PTE) + sizeof(ATA_PASS_THROUGH_EX) + FillerSize; memset(&PTE, 0, sizeof(ATA_PASS_THROUGH_EX) + FillerSize); PTE.Length = sizeof(PTE); PTE.AtaFlags = 0; PTE.AtaFlags |= Cmd.requiresDRDY() ? ATA_FLAGS_DRDY_REQUIRED : 0; switch (Cmd.dataDirection()) { case ddFromDevice: PTE.AtaFlags |= ATA_FLAGS_DATA_IN; break; case ddToDevice: PTE.AtaFlags |= ATA_FLAGS_DATA_OUT; memcpy(DataPtr, Cmd.buffer(), Cmd.bufferSize()); break; default: break; } PTE.AtaFlags |= Cmd.is48Bit() ? ATA_FLAGS_48BIT_COMMAND : 0; PTE.AtaFlags |= Cmd.isDMA() ? ATA_FLAGS_USE_DMA : 0; PTE.DataTransferLength = Cmd.bufferSize(); PTE.TimeOutValue = Cmd.timeout(); PTE.DataBufferOffset = sizeof(PTE) + FillerSize; PTE.DataTransferLength = Cmd.bufferSize(); PTE.CurrentTaskFile[0] = Cmd.taskFileIn0().Features; PTE.CurrentTaskFile[1] = Cmd.taskFileIn0().Count; PTE.CurrentTaskFile[2] = Cmd.taskFileIn0().LBALow; PTE.CurrentTaskFile[3] = Cmd.taskFileIn0().LBAMid; PTE.CurrentTaskFile[4] = Cmd.taskFileIn0().LBAHigh; PTE.CurrentTaskFile[5] = Cmd.taskFileIn0().Device; PTE.CurrentTaskFile[6] = Cmd.taskFileIn0().Command; PTE.CurrentTaskFile[7] = 0; if (Cmd.is48Bit()) { PTE.PreviousTaskFile[0] = Cmd.taskFileIn1().Features; PTE.PreviousTaskFile[1] = Cmd.taskFileIn1().Count; PTE.PreviousTaskFile[2] = Cmd.taskFileIn1().LBALow; PTE.PreviousTaskFile[3] = Cmd.taskFileIn1().LBAMid; PTE.PreviousTaskFile[4] = Cmd.taskFileIn1().LBAHigh; PTE.PreviousTaskFile[5] = Cmd.taskFileIn1().Device; PTE.PreviousTaskFile[6] = 0; PTE.PreviousTaskFile[7] = 0; } DWORD BR; if (!DeviceIoControl(FHandle, IOCTL_ATA_PASS_THROUGH, &PTE, B.size(), &PTE, B.size(), &BR, 0)) { FLastOSError = GetLastError(); LOG_W << "ioctl ATA_PT failed for " << Cmd << ": " << FLastOSError << " (" << Utils::describeOSError(FLastOSError) << ")"; return Utils::mapOSError(FLastOSError); } Cmd.taskFileOut0().Error = PTE.CurrentTaskFile[0]; Cmd.taskFileOut0().Count = PTE.CurrentTaskFile[1]; Cmd.taskFileOut0().LBALow = PTE.CurrentTaskFile[2]; Cmd.taskFileOut0().LBAMid = PTE.CurrentTaskFile[3]; Cmd.taskFileOut0().LBAHigh = PTE.CurrentTaskFile[4]; Cmd.taskFileOut0().Device = PTE.CurrentTaskFile[5]; Cmd.taskFileOut0().Status = PTE.CurrentTaskFile[6]; Cmd.taskFileOut1().Error = PTE.PreviousTaskFile[0]; Cmd.taskFileOut1().Count = PTE.PreviousTaskFile[1]; Cmd.taskFileOut1().LBALow = PTE.PreviousTaskFile[2]; Cmd.taskFileOut1().LBAMid = PTE.PreviousTaskFile[3]; Cmd.taskFileOut1().LBAHigh = PTE.PreviousTaskFile[4]; Cmd.taskFileOut1().Device = PTE.PreviousTaskFile[5]; Cmd.taskFileOut1().Status = PTE.PreviousTaskFile[6]; if (Cmd.dataDirection() == ddFromDevice) { memcpy(Cmd.buffer(), DataPtr, Cmd.bufferSize()); } return resOK; }

EDITAR: Muestra sin dependencias externas.

IDENTIFY requiere un buffer de 512 bytes para los datos:

unsigned char Buffer[512 + sizeof(ATA_PASS_THROUGH_EX)] = { 0 }; ATA_PASS_THROUGH_EX & PTE = *(ATA_PASS_THROUGH_EX *) Buffer; PTE.Length = sizeof(PTE); PTE.TimeOutValue = 10; PTE.DataTransferLength = 512; PTE.DataBufferOffset = sizeof(ATA_PASS_THROUGH_EX);

Configure los registros IDE como se especifica en la especificación ATA.

IDEREGS * ir = (IDEREGS *) PTE.CurrentTaskFile; ir->bCommandReg = 0xEC; ir->bSectorCountReg = 1;

IDENTIFY no es ni 48 bits ni DMA, se lee desde el dispositivo:

PTE.AtaFlags = ATA_FLAGS_DATA_IN | ATA_FLAGS_DRDY_REQUIRED;

Hacer el ioctl

DeviceIOControl(Handle, IOCTL_ATA_PASS_THROUGH, &PTE, sizeof(Buffer), &PTE, sizeof(Buffer), &BR, 0);

Aquí debe insertar la comprobación de errores, tanto desde DeviceIOControl como mirando IDEREGS para errores informados del dispositivo.

Obtenga los datos de IDENTIFICACIÓN, suponiendo que ha definido una estructura IdentifyData

IdentifyData * IDData = (IdentifyData *) (Buffer + sizeof(ATA_PASS_THROUGH_EX));