os_win32.cpp 142 KB
Newer Older
1
/*
chrfranke's avatar
chrfranke committed
2
 * os_win32.cpp
3
 *
4
 * Home page of code is: https://www.smartmontools.org
5
 *
6
 * Copyright (C) 2004-21 Christian Franke
chrfranke's avatar
chrfranke committed
7
8
9
10
11
12
 *
 * Original AACRaid code:
 *  Copyright (C) 2015    Nidhi Malhotra <nidhi.malhotra@pmcs.com>
 *
 * Original Areca code:
 *  Copyright (C) 2012    Hank Wu <hank@areca.com.tw>
13
 *
14
 * SPDX-License-Identifier: GPL-2.0-or-later
15
16
17
 */

#include "config.h"
18
19
#define WINVER 0x0502
#define _WIN32_WINNT WINVER
20

21
22
#include "atacmds.h"
#include "scsicmds.h"
23
#include "nvmecmds.h"
24
#include "utility.h"
25
26
27

#include "dev_interface.h"
#include "dev_ata_cmd_set.h"
chrfranke's avatar
chrfranke committed
28
#include "dev_areca.h"
29

30
#include "os_win32/wmiquery.h"
31
#include "os_win32/popen.h"
32

33
34
35
// TODO: Move from smartctl.h to other include file
extern unsigned char failuretest_permissive;

36
#include <errno.h>
37

38
#ifdef _DEBUG
39
#include <assert.h>
40
#else
41
#undef assert
42
#define assert(x) /* */
43
#endif
44

45
46
#include <stddef.h> // offsetof()

47
#include <windows.h>
48
#include <ntddscsi.h> // IOCTL_ATA_PASS_THROUGH, IOCTL_SCSI_PASS_THROUGH, ...
49

50
#ifndef _WIN32
chrfranke's avatar
chrfranke committed
51
52
// csmisas.h and aacraid.h require _WIN32 but w32api-headers no longer define it on Cygwin
// (aacraid.h also checks for _WIN64 which is also set on Cygwin x64)
53
54
55
#define _WIN32
#endif

56
57
58
// CSMI support
#include "csmisas.h"

chrfranke's avatar
chrfranke committed
59
// aacraid support
chrfranke's avatar
chrfranke committed
60
61
#include "aacraid.h"

62
63
64
65
66
67
#ifndef _WIN64
#define SELECT_WIN_32_64(x32, x64) (x32)
#else
#define SELECT_WIN_32_64(x32, x64) (x64)
#endif

chrfranke's avatar
chrfranke committed
68
69
70
71
// Cygwin does no longer provide strn?icmp() compatibility macros
// MSVCRT does not provide strn?casecmp()
#if defined(__CYGWIN__) && !defined(stricmp)
#define stricmp strcasecmp
72
73
74
#define strnicmp strncasecmp
#endif

75
76
const char * os_win32_cpp_cvsid = "$Id$";

77
78
79
80
81
82
83
/////////////////////////////////////////////////////////////////////////////
// Windows I/O-controls, some declarations are missing in the include files

extern "C" {

// SMART_* IOCTLs, also known as DFP_* (Disk Fault Protection)

84
85
86
87
88
89
STATIC_ASSERT(SMART_GET_VERSION == 0x074080);
STATIC_ASSERT(SMART_SEND_DRIVE_COMMAND == 0x07c084);
STATIC_ASSERT(SMART_RCV_DRIVE_DATA == 0x07c088);
STATIC_ASSERT(sizeof(GETVERSIONINPARAMS) == 24);
STATIC_ASSERT(sizeof(SENDCMDINPARAMS) == 32+1);
STATIC_ASSERT(sizeof(SENDCMDOUTPARAMS) == 16+1);
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110


// IDE PASS THROUGH (2000, XP, undocumented)

#ifndef IOCTL_IDE_PASS_THROUGH

#define IOCTL_IDE_PASS_THROUGH \
  CTL_CODE(IOCTL_SCSI_BASE, 0x040A, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)

#endif // IOCTL_IDE_PASS_THROUGH

#pragma pack(1)

typedef struct {
  IDEREGS IdeReg;
  ULONG DataBufferSize;
  UCHAR DataBuffer[1];
} ATA_PASS_THROUGH;

#pragma pack()

111
112
STATIC_ASSERT(IOCTL_IDE_PASS_THROUGH == 0x04d028);
STATIC_ASSERT(sizeof(ATA_PASS_THROUGH) == 12+1);
113
114
115
116


// ATA PASS THROUGH (Win2003, XP SP2)

117
118
STATIC_ASSERT(IOCTL_ATA_PASS_THROUGH == 0x04d02c);
STATIC_ASSERT(sizeof(ATA_PASS_THROUGH_EX) == SELECT_WIN_32_64(40, 48));
119
120
121
122


// IOCTL_SCSI_PASS_THROUGH[_DIRECT]

123
124
125
126
STATIC_ASSERT(IOCTL_SCSI_PASS_THROUGH == 0x04d004);
STATIC_ASSERT(IOCTL_SCSI_PASS_THROUGH_DIRECT == 0x04d014);
STATIC_ASSERT(sizeof(SCSI_PASS_THROUGH) == SELECT_WIN_32_64(44, 56));
STATIC_ASSERT(sizeof(SCSI_PASS_THROUGH_DIRECT) == SELECT_WIN_32_64(44, 56));
127
128
129
130
131
132


// SMART IOCTL via SCSI MINIPORT ioctl

#ifndef FILE_DEVICE_SCSI
#define FILE_DEVICE_SCSI 0x001b
133
134
135
#endif

#ifndef IOCTL_SCSI_MINIPORT_SMART_VERSION
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150

#define IOCTL_SCSI_MINIPORT_SMART_VERSION               ((FILE_DEVICE_SCSI << 16) + 0x0500)
#define IOCTL_SCSI_MINIPORT_IDENTIFY                    ((FILE_DEVICE_SCSI << 16) + 0x0501)
#define IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS          ((FILE_DEVICE_SCSI << 16) + 0x0502)
#define IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS       ((FILE_DEVICE_SCSI << 16) + 0x0503)
#define IOCTL_SCSI_MINIPORT_ENABLE_SMART                ((FILE_DEVICE_SCSI << 16) + 0x0504)
#define IOCTL_SCSI_MINIPORT_DISABLE_SMART               ((FILE_DEVICE_SCSI << 16) + 0x0505)
#define IOCTL_SCSI_MINIPORT_RETURN_STATUS               ((FILE_DEVICE_SCSI << 16) + 0x0506)
#define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE     ((FILE_DEVICE_SCSI << 16) + 0x0507)
#define IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES       ((FILE_DEVICE_SCSI << 16) + 0x0508)
#define IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS       ((FILE_DEVICE_SCSI << 16) + 0x0509)
#define IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE ((FILE_DEVICE_SCSI << 16) + 0x050a)
#define IOCTL_SCSI_MINIPORT_READ_SMART_LOG              ((FILE_DEVICE_SCSI << 16) + 0x050b)
#define IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG             ((FILE_DEVICE_SCSI << 16) + 0x050c)

151
#endif // IOCTL_SCSI_MINIPORT_SMART_VERSION
152

153
STATIC_ASSERT(IOCTL_SCSI_MINIPORT == 0x04d008);
154
STATIC_ASSERT(IOCTL_SCSI_MINIPORT_SMART_VERSION == 0x1b0500);
155
STATIC_ASSERT(sizeof(SRB_IO_CONTROL) == 28);
156
157
158
159


// IOCTL_STORAGE_QUERY_PROPERTY

160
161
162
STATIC_ASSERT(IOCTL_STORAGE_QUERY_PROPERTY == 0x002d1400);
STATIC_ASSERT(sizeof(STORAGE_DEVICE_DESCRIPTOR) == 36+1+3);
STATIC_ASSERT(sizeof(STORAGE_PROPERTY_QUERY) == 8+1+3);
163
164


165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
// IOCTL_STORAGE_QUERY_PROPERTY: Windows 10 enhancements

namespace win10 {

  // enum STORAGE_PROPERTY_ID: new values
  const STORAGE_PROPERTY_ID StorageAdapterProtocolSpecificProperty = (STORAGE_PROPERTY_ID)49;
  const STORAGE_PROPERTY_ID StorageDeviceProtocolSpecificProperty = (STORAGE_PROPERTY_ID)50;

  typedef enum _STORAGE_PROTOCOL_TYPE {
    ProtocolTypeUnknown = 0,
    ProtocolTypeScsi,
    ProtocolTypeAta,
    ProtocolTypeNvme,
    ProtocolTypeSd
  } STORAGE_PROTOCOL_TYPE;

  typedef enum _STORAGE_PROTOCOL_NVME_DATA_TYPE {
    NVMeDataTypeUnknown = 0,
    NVMeDataTypeIdentify,
    NVMeDataTypeLogPage,
    NVMeDataTypeFeature
  } STORAGE_PROTOCOL_NVME_DATA_TYPE;

  typedef struct _STORAGE_PROTOCOL_SPECIFIC_DATA {
    STORAGE_PROTOCOL_TYPE ProtocolType;
    ULONG DataType;
    ULONG ProtocolDataRequestValue;
    ULONG ProtocolDataRequestSubValue;
    ULONG ProtocolDataOffset;
    ULONG ProtocolDataLength;
    ULONG FixedProtocolReturnData;
    ULONG Reserved[3];
  } STORAGE_PROTOCOL_SPECIFIC_DATA;

199
  STATIC_ASSERT(sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA) == 40);
200
201
202
203

} // namespace win10


204
205
// IOCTL_STORAGE_PREDICT_FAILURE

206
207
STATIC_ASSERT(IOCTL_STORAGE_PREDICT_FAILURE == 0x002d1100);
STATIC_ASSERT(sizeof(STORAGE_PREDICT_FAILURE) == 4+512);
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239


// 3ware specific versions of SMART ioctl structs

#define SMART_VENDOR_3WARE      0x13C1  // identifies 3ware specific parameters

#pragma pack(1)

typedef struct _GETVERSIONINPARAMS_EX {
  BYTE bVersion;
  BYTE bRevision;
  BYTE bReserved;
  BYTE bIDEDeviceMap;
  DWORD fCapabilities;
  DWORD dwDeviceMapEx;  // 3ware specific: RAID drive bit map
  WORD wIdentifier;     // Vendor specific identifier
  WORD wControllerId;   // 3ware specific: Controller ID (0,1,...)
  ULONG dwReserved[2];
} GETVERSIONINPARAMS_EX;

typedef struct _SENDCMDINPARAMS_EX {
  DWORD cBufferSize;
  IDEREGS irDriveRegs;
  BYTE bDriveNumber;
  BYTE bPortNumber;     // 3ware specific: port number
  WORD wIdentifier;     // Vendor specific identifier
  DWORD dwReserved[4];
  BYTE bBuffer[1];
} SENDCMDINPARAMS_EX;

#pragma pack()

240
241
STATIC_ASSERT(sizeof(GETVERSIONINPARAMS_EX) == sizeof(GETVERSIONINPARAMS));
STATIC_ASSERT(sizeof(SENDCMDINPARAMS_EX) == sizeof(SENDCMDINPARAMS));
242

243

244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
// NVME_PASS_THROUGH

#ifndef NVME_PASS_THROUGH_SRB_IO_CODE

#define NVME_SIG_STR "NvmeMini"
#define NVME_STORPORT_DRIVER 0xe000

#define NVME_PASS_THROUGH_SRB_IO_CODE \
  CTL_CODE(NVME_STORPORT_DRIVER, 0x0800, METHOD_BUFFERED, FILE_ANY_ACCESS)

#pragma pack(1)
typedef struct _NVME_PASS_THROUGH_IOCTL
{
  SRB_IO_CONTROL SrbIoCtrl;
  ULONG VendorSpecific[6];
  ULONG NVMeCmd[16]; // Command DW[0...15]
  ULONG CplEntry[4]; // Completion DW[0...3]
  ULONG Direction; // 0=No, 1=Out, 2=In, 3=I/O
  ULONG QueueId; // 0=AdminQ
  ULONG DataBufferLen; // sizeof(DataBuffer) if Data In
  ULONG MetaDataLen;
  ULONG ReturnBufferLen; // offsetof(DataBuffer), plus sizeof(DataBuffer) if Data Out
  UCHAR DataBuffer[1];
} NVME_PASS_THROUGH_IOCTL;
#pragma pack()

#endif // NVME_PASS_THROUGH_SRB_IO_CODE

272
273
274
STATIC_ASSERT(NVME_PASS_THROUGH_SRB_IO_CODE == (int)0xe0002000);
STATIC_ASSERT(sizeof(NVME_PASS_THROUGH_IOCTL) == 152+1);
STATIC_ASSERT(sizeof(NVME_PASS_THROUGH_IOCTL) == offsetof(NVME_PASS_THROUGH_IOCTL, DataBuffer)+1);
275
276


277
278
// CSMI structs

279
280
281
282
STATIC_ASSERT(sizeof(IOCTL_HEADER) == sizeof(SRB_IO_CONTROL));
STATIC_ASSERT(sizeof(CSMI_SAS_DRIVER_INFO_BUFFER) == 204);
STATIC_ASSERT(sizeof(CSMI_SAS_PHY_INFO_BUFFER) == 2080);
STATIC_ASSERT(sizeof(CSMI_SAS_STP_PASSTHRU_BUFFER) == 168);
283

chrfranke's avatar
chrfranke committed
284
285
// aacraid struct

286
STATIC_ASSERT(sizeof(SCSI_REQUEST_BLOCK) == SELECT_WIN_32_64(64, 88));
chrfranke's avatar
chrfranke committed
287

288
289
} // extern "C"

290
291
292
293
294
295
296
297
/////////////////////////////////////////////////////////////////////////////

namespace os_win32 { // no need to publish anything, name provided for Doxygen

#ifdef _MSC_VER
#pragma warning(disable:4250)
#endif

298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
static int is_permissive()
{
  if (!failuretest_permissive) {
    pout("To continue, add one or more '-T permissive' options.\n");
    return 0;
  }
  failuretest_permissive--;
  return 1;
}

// return number for drive letter, -1 on error
// "[A-Za-z]:([/\\][.]?)?" => 0-25
// Accepts trailing '"' to fix broken "X:\" parameter passing from .bat files
static int drive_letter(const char * s)
{
  return (   (('A' <= s[0] && s[0] <= 'Z') || ('a' <= s[0] && s[0] <= 'z'))
          && s[1] == ':'
          && (!s[2] || (   strchr("/\\\"", s[2])
                        && (!s[3] || (s[3] == '.' && !s[4])))              ) ?
          (s[0] & 0x1f) - 1 : -1);
}

// Skip trailing "/dev/", do not allow "/dev/X:"
static const char * skipdev(const char * s)
{
  return (!strncmp(s, "/dev/", 5) && drive_letter(s+5) < 0 ? s+5 : s);
}

// "sd[a-z]" -> 0-25, "sd[a-z][a-z]" -> 26-701
static int sdxy_to_phydrive(const char (& xy)[2+1])
{
  int phydrive = xy[0] - 'a';
  if (xy[1])
    phydrive = (phydrive + 1) * ('z' - 'a' + 1) + (xy[1] - 'a');
  return phydrive;
}

static void copy_swapped(unsigned char * dest, const char * src, int destsize)
{
  int srclen = strcspn(src, "\r\n");
  int i;
  for (i = 0; i < destsize-1 && i < srclen-1; i+=2) {
    dest[i] = src[i+1]; dest[i+1] = src[i];
  }
  if (i < destsize-1 && i < srclen)
    dest[i+1] = src[i];
}


/////////////////////////////////////////////////////////////////////////////
// win_smart_device

350
351
352
353
354
355
356
357
358
class win_smart_device
: virtual public /*implements*/ smart_device
{
public:
  win_smart_device()
    : smart_device(never_called),
      m_fh(INVALID_HANDLE_VALUE)
    { }

359
  virtual ~win_smart_device();
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378

  virtual bool is_open() const;

  virtual bool close();

protected:
  /// Set handle for open() in derived classes.
  void set_fh(HANDLE fh)
    { m_fh = fh; }

  /// Return handle for derived classes.
  HANDLE get_fh() const
    { return m_fh; }

private:
  HANDLE m_fh; ///< File handle
};


379
// Common routines for devices with HANDLEs
380

381
win_smart_device::~win_smart_device()
382
{
383
384
385
  if (m_fh != INVALID_HANDLE_VALUE)
    ::CloseHandle(m_fh);
}
386

387
388
389
390
bool win_smart_device::is_open() const
{
  return (m_fh != INVALID_HANDLE_VALUE);
}
391

392
393
394
395
396
397
398
399
bool win_smart_device::close()
{
  if (m_fh == INVALID_HANDLE_VALUE)
    return true;
  BOOL rc = ::CloseHandle(m_fh);
  m_fh = INVALID_HANDLE_VALUE;
  return !!rc;
}
400
401
402
403


/////////////////////////////////////////////////////////////////////////////

404
405
#define SMART_CYL_LOW  0x4F
#define SMART_CYL_HI   0xC2
406

407
408
409
410
411
412
static void print_ide_regs(const IDEREGS * r, int out)
{
  pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, SN=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n",
    (out?"STS":"CMD"), r->bCommandReg, (out?"ERR":" FR"), r->bFeaturesReg,
    r->bSectorCountReg, r->bSectorNumberReg, r->bCylLowReg, r->bCylHighReg, r->bDriveHeadReg);
}
413

414
415
416
417
418
419
420
static void print_ide_regs_io(const IDEREGS * ri, const IDEREGS * ro)
{
  pout("    Input : "); print_ide_regs(ri, 0);
  if (ro) {
    pout("    Output: "); print_ide_regs(ro, 1);
  }
}
421
422
423

/////////////////////////////////////////////////////////////////////////////

424
// call SMART_GET_VERSION, return device map or -1 on error
425

426
427
428
429
430
static int smart_get_version(HANDLE hdevice, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
{
  GETVERSIONINPARAMS vers; memset(&vers, 0, sizeof(vers));
  const GETVERSIONINPARAMS_EX & vers_ex = (const GETVERSIONINPARAMS_EX &)vers;
  DWORD num_out;
431

432
433
434
435
436
437
438
439
  if (!DeviceIoControl(hdevice, SMART_GET_VERSION,
    NULL, 0, &vers, sizeof(vers), &num_out, NULL)) {
    if (ata_debugmode)
      pout("  SMART_GET_VERSION failed, Error=%u\n", (unsigned)GetLastError());
    errno = ENOSYS;
    return -1;
  }
  assert(num_out == sizeof(GETVERSIONINPARAMS));
440

441
  if (ata_debugmode > 1) {
chrfranke's avatar
chrfranke committed
442
    pout("  SMART_GET_VERSION succeeded, bytes returned: %u\n"
443
444
445
446
447
448
449
         "    Vers = %d.%d, Caps = 0x%x, DeviceMap = 0x%02x\n",
      (unsigned)num_out, vers.bVersion, vers.bRevision,
      (unsigned)vers.fCapabilities, vers.bIDEDeviceMap);
    if (vers_ex.wIdentifier == SMART_VENDOR_3WARE)
      pout("    Identifier = %04x(3WARE), ControllerId=%u, DeviceMapEx = 0x%08x\n",
      vers_ex.wIdentifier, vers_ex.wControllerId, (unsigned)vers_ex.dwDeviceMapEx);
  }
450

451
452
  if (ata_version_ex)
    *ata_version_ex = vers_ex;
453

454
455
456
  // TODO: Check vers.fCapabilities here?
  return vers.bIDEDeviceMap;
}
457
458


459
// call SMART_* ioctl
460

461
static int smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize, int port)
462
{
463
464
  SENDCMDINPARAMS inpar;
  SENDCMDINPARAMS_EX & inpar_ex = (SENDCMDINPARAMS_EX &)inpar;
465

466
467
468
469
470
  unsigned char outbuf[sizeof(SENDCMDOUTPARAMS)-1 + 512];
  const SENDCMDOUTPARAMS * outpar;
  DWORD code, num_out;
  unsigned int size_out;
  const char * name;
471

472
473
  memset(&inpar, 0, sizeof(inpar));
  inpar.irDriveRegs = *regs;
474

475
476
477
  // Older drivers may require bits 5 and 7 set
  // ATA-3: bits shall be set, ATA-4 and later: bits are obsolete
  inpar.irDriveRegs.bDriveHeadReg |= 0xa0;
478

479
480
481
  // Drive number 0-3 was required on Win9x/ME only
  //inpar.irDriveRegs.bDriveHeadReg |= (drive & 1) << 4;
  //inpar.bDriveNumber = drive;
482

483
484
485
486
487
  if (port >= 0) {
    // Set RAID port
    inpar_ex.wIdentifier = SMART_VENDOR_3WARE;
    inpar_ex.bPortNumber = port;
  }
488

489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
  if (datasize == 512) {
    code = SMART_RCV_DRIVE_DATA; name = "SMART_RCV_DRIVE_DATA";
    inpar.cBufferSize = size_out = 512;
  }
  else if (datasize == 0) {
    code = SMART_SEND_DRIVE_COMMAND; name = "SMART_SEND_DRIVE_COMMAND";
    if (regs->bFeaturesReg == ATA_SMART_STATUS)
      size_out = sizeof(IDEREGS); // ioctl returns new IDEREGS as data
      // Note: cBufferSize must be 0 on Win9x
    else
      size_out = 0;
  }
  else {
    errno = EINVAL;
    return -1;
  }
505

506
  memset(&outbuf, 0, sizeof(outbuf));
507

508
509
510
511
512
513
514
515
516
517
518
519
520
521
  if (!DeviceIoControl(hdevice, code, &inpar, sizeof(SENDCMDINPARAMS)-1,
    outbuf, sizeof(SENDCMDOUTPARAMS)-1 + size_out, &num_out, NULL)) {
    // CAUTION: DO NOT change "regs" Parameter in this case, see win_ata_device::ata_pass_through()
    long err = GetLastError();
    if (ata_debugmode && (err != ERROR_INVALID_PARAMETER || ata_debugmode > 1)) {
      pout("  %s failed, Error=%ld\n", name, err);
      print_ide_regs_io(regs, NULL);
    }
    errno = (   err == ERROR_INVALID_FUNCTION/*9x*/
             || err == ERROR_INVALID_PARAMETER/*NT/2K/XP*/
             || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
    return -1;
  }
  // NOTE: On Win9x, inpar.irDriveRegs now contains the returned regs
522

523
  outpar = (const SENDCMDOUTPARAMS *)outbuf;
524

525
526
527
528
529
530
531
532
533
  if (outpar->DriverStatus.bDriverError) {
    if (ata_debugmode) {
      pout("  %s failed, DriverError=0x%02x, IDEError=0x%02x\n", name,
        outpar->DriverStatus.bDriverError, outpar->DriverStatus.bIDEError);
      print_ide_regs_io(regs, NULL);
    }
    errno = (!outpar->DriverStatus.bIDEError ? ENOSYS : EIO);
    return -1;
  }
534

535
  if (ata_debugmode > 1) {
chrfranke's avatar
chrfranke committed
536
    pout("  %s succeeded, bytes returned: %u (buffer %u)\n", name,
537
538
539
540
      (unsigned)num_out, (unsigned)outpar->cBufferSize);
    print_ide_regs_io(regs, (regs->bFeaturesReg == ATA_SMART_STATUS ?
      (const IDEREGS *)(outpar->bBuffer) : NULL));
  }
541

542
543
544
545
546
547
548
549
550
551
552
  if (datasize)
    memcpy(data, outpar->bBuffer, 512);
  else if (regs->bFeaturesReg == ATA_SMART_STATUS) {
    if (nonempty(outpar->bBuffer, sizeof(IDEREGS)))
      memcpy(regs, outpar->bBuffer, sizeof(IDEREGS));
    else {  // Workaround for driver not returning regs
      if (ata_debugmode)
        pout("  WARNING: driver does not return ATA registers in output buffer!\n");
      *regs = inpar.irDriveRegs;
    }
  }
553

554
555
  return 0;
}
556
557


chrfranke's avatar
chrfranke committed
558
/////////////////////////////////////////////////////////////////////////////
559
560
561
562
// IDE PASS THROUGH (2000, XP, undocumented)
//
// Based on WinATA.cpp, 2002 c't/Matthias Withopf
// ftp://ftp.heise.de/pub/ct/listings/0207-218.zip
chrfranke's avatar
chrfranke committed
563

564
static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize)
chrfranke's avatar
chrfranke committed
565
{
566
567
568
569
570
571
572
573
  if (datasize > 512) {
    errno = EINVAL;
    return -1;
  }
  unsigned int size = sizeof(ATA_PASS_THROUGH)-1 + datasize;
  ATA_PASS_THROUGH * buf = (ATA_PASS_THROUGH *)VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
  DWORD num_out;
  const unsigned char magic = 0xcf;
chrfranke's avatar
chrfranke committed
574

575
576
577
578
  if (!buf) {
    errno = ENOMEM;
    return -1;
  }
chrfranke's avatar
chrfranke committed
579

580
581
582
583
  buf->IdeReg = *regs;
  buf->DataBufferSize = datasize;
  if (datasize)
    buf->DataBuffer[0] = magic;
chrfranke's avatar
chrfranke committed
584

585
586
587
588
589
590
591
592
593
594
595
  if (!DeviceIoControl(hdevice, IOCTL_IDE_PASS_THROUGH,
    buf, size, buf, size, &num_out, NULL)) {
    long err = GetLastError();
    if (ata_debugmode) {
      pout("  IOCTL_IDE_PASS_THROUGH failed, Error=%ld\n", err);
      print_ide_regs_io(regs, NULL);
    }
    VirtualFree(buf, 0, MEM_RELEASE);
    errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
    return -1;
  }
chrfranke's avatar
chrfranke committed
596

597
598
599
600
601
602
603
604
605
606
  // Check ATA status
  if (buf->IdeReg.bCommandReg/*Status*/ & 0x01) {
    if (ata_debugmode) {
      pout("  IOCTL_IDE_PASS_THROUGH command failed:\n");
      print_ide_regs_io(regs, &buf->IdeReg);
    }
    VirtualFree(buf, 0, MEM_RELEASE);
    errno = EIO;
    return -1;
  }
chrfranke's avatar
chrfranke committed
607

608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
  // Check and copy data
  if (datasize) {
    if (   num_out != size
        || (buf->DataBuffer[0] == magic && !nonempty(buf->DataBuffer+1, datasize-1))) {
      if (ata_debugmode) {
        pout("  IOCTL_IDE_PASS_THROUGH output data missing (%u, %u)\n",
          (unsigned)num_out, (unsigned)buf->DataBufferSize);
        print_ide_regs_io(regs, &buf->IdeReg);
      }
      VirtualFree(buf, 0, MEM_RELEASE);
      errno = EIO;
      return -1;
    }
    memcpy(data, buf->DataBuffer, datasize);
  }
chrfranke's avatar
chrfranke committed
623

624
  if (ata_debugmode > 1) {
chrfranke's avatar
chrfranke committed
625
    pout("  IOCTL_IDE_PASS_THROUGH succeeded, bytes returned: %u (buffer %u)\n",
626
627
628
629
      (unsigned)num_out, (unsigned)buf->DataBufferSize);
    print_ide_regs_io(regs, &buf->IdeReg);
  }
  *regs = buf->IdeReg;
chrfranke's avatar
chrfranke committed
630

631
632
633
634
  // Caution: VirtualFree() fails if parameter "dwSize" is nonzero
  VirtualFree(buf, 0, MEM_RELEASE);
  return 0;
}
chrfranke's avatar
chrfranke committed
635

636

637
/////////////////////////////////////////////////////////////////////////////
638
// ATA PASS THROUGH (Win2003, XP SP2)
639

640
641
642
643
644
645
646
647
// Warning:
// IOCTL_ATA_PASS_THROUGH[_DIRECT] can only handle one interrupt/DRQ data
// transfer per command. Therefore, multi-sector transfers are only supported
// for the READ/WRITE MULTIPLE [EXT] commands. Other commands like READ/WRITE SECTORS
// or READ/WRITE LOG EXT work only with single sector transfers.
// The latter are supported on Vista (only) through new ATA_FLAGS_NO_MULTIPLE.
// See:
// http://social.msdn.microsoft.com/Forums/en-US/storageplatformata/thread/eb408507-f221-455b-9bbb-d1069b29c4da
648

649
static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev_regs, char * data, int datasize)
650
{
651
  const int max_sectors = 32; // TODO: Allocate dynamic buffer
652

653
654
655
656
657
  typedef struct {
    ATA_PASS_THROUGH_EX apt;
    ULONG Filler;
    UCHAR ucDataBuf[max_sectors * 512];
  } ATA_PASS_THROUGH_EX_WITH_BUFFERS;
658

659
  const unsigned char magic = 0xcf;
660

661
662
663
664
665
  ATA_PASS_THROUGH_EX_WITH_BUFFERS ab; memset(&ab, 0, sizeof(ab));
  ab.apt.Length = sizeof(ATA_PASS_THROUGH_EX);
  //ab.apt.PathId = 0;
  //ab.apt.TargetId = 0;
  //ab.apt.Lun = 0;
666
  ab.apt.TimeOutValue = 60; // seconds
667
668
  unsigned size = offsetof(ATA_PASS_THROUGH_EX_WITH_BUFFERS, ucDataBuf);
  ab.apt.DataBufferOffset = size;
669

670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
  if (datasize > 0) {
    if (datasize > (int)sizeof(ab.ucDataBuf)) {
      errno = EINVAL;
      return -1;
    }
    ab.apt.AtaFlags = ATA_FLAGS_DATA_IN;
    ab.apt.DataTransferLength = datasize;
    size += datasize;
    ab.ucDataBuf[0] = magic;
  }
  else if (datasize < 0) {
    if (-datasize > (int)sizeof(ab.ucDataBuf)) {
      errno = EINVAL;
      return -1;
    }
    ab.apt.AtaFlags = ATA_FLAGS_DATA_OUT;
    ab.apt.DataTransferLength = -datasize;
    size += -datasize;
    memcpy(ab.ucDataBuf, data, -datasize);
  }
  else {
    assert(ab.apt.AtaFlags == 0);
    assert(ab.apt.DataTransferLength == 0);
  }
694

695
696
697
698
  assert(sizeof(ab.apt.CurrentTaskFile) == sizeof(IDEREGS));
  IDEREGS * ctfregs = (IDEREGS *)ab.apt.CurrentTaskFile;
  IDEREGS * ptfregs = (IDEREGS *)ab.apt.PreviousTaskFile;
  *ctfregs = *regs;
699

700
701
702
703
  if (prev_regs) {
    *ptfregs = *prev_regs;
    ab.apt.AtaFlags |= ATA_FLAGS_48BIT_COMMAND;
  }
704

705
706
707
708
709
710
711
712
713
714
715
  DWORD num_out;
  if (!DeviceIoControl(hdevice, IOCTL_ATA_PASS_THROUGH,
    &ab, size, &ab, size, &num_out, NULL)) {
    long err = GetLastError();
    if (ata_debugmode) {
      pout("  IOCTL_ATA_PASS_THROUGH failed, Error=%ld\n", err);
      print_ide_regs_io(regs, NULL);
    }
    errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
    return -1;
  }
716

717
718
719
720
721
722
723
724
725
  // Check ATA status
  if (ctfregs->bCommandReg/*Status*/ & (0x01/*Err*/|0x08/*DRQ*/)) {
    if (ata_debugmode) {
      pout("  IOCTL_ATA_PASS_THROUGH command failed:\n");
      print_ide_regs_io(regs, ctfregs);
    }
    errno = EIO;
    return -1;
  }
726

727
728
729
730
731
732
733
734
735
736
737
738
739
  // Check and copy data
  if (datasize > 0) {
    if (   num_out != size
        || (ab.ucDataBuf[0] == magic && !nonempty(ab.ucDataBuf+1, datasize-1))) {
      if (ata_debugmode) {
        pout("  IOCTL_ATA_PASS_THROUGH output data missing (%u)\n", (unsigned)num_out);
        print_ide_regs_io(regs, ctfregs);
      }
      errno = EIO;
      return -1;
    }
    memcpy(data, ab.ucDataBuf, datasize);
  }
740

741
  if (ata_debugmode > 1) {
chrfranke's avatar
chrfranke committed
742
    pout("  IOCTL_ATA_PASS_THROUGH succeeded, bytes returned: %u\n", (unsigned)num_out);
743
744
745
746
747
    print_ide_regs_io(regs, ctfregs);
  }
  *regs = *ctfregs;
  if (prev_regs)
    *prev_regs = *ptfregs;
748

749
750
  return 0;
}
751
752


753
754
/////////////////////////////////////////////////////////////////////////////
// SMART IOCTL via SCSI MINIPORT ioctl
755

756
757
758
759
// This function is handled by ATAPI port driver (atapi.sys) or by SCSI
// miniport driver (via SCSI port driver scsiport.sys).
// It can be used to skip the missing or broken handling of some SMART
// command codes (e.g. READ_LOG) in the disk class driver (disk.sys)
760

761
static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize)
762
{
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
  // Select code
  DWORD code = 0; const char * name = 0;
  if (regs->bCommandReg == ATA_IDENTIFY_DEVICE) {
    code = IOCTL_SCSI_MINIPORT_IDENTIFY; name = "IDENTIFY";
  }
  else if (regs->bCommandReg == ATA_SMART_CMD) switch (regs->bFeaturesReg) {
    case ATA_SMART_READ_VALUES:
      code = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS; name = "READ_SMART_ATTRIBS"; break;
    case ATA_SMART_READ_THRESHOLDS:
      code = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS; name = "READ_SMART_THRESHOLDS"; break;
    case ATA_SMART_ENABLE:
      code = IOCTL_SCSI_MINIPORT_ENABLE_SMART; name = "ENABLE_SMART"; break;
    case ATA_SMART_DISABLE:
      code = IOCTL_SCSI_MINIPORT_DISABLE_SMART; name = "DISABLE_SMART"; break;
    case ATA_SMART_STATUS:
      code = IOCTL_SCSI_MINIPORT_RETURN_STATUS; name = "RETURN_STATUS"; break;
    case ATA_SMART_AUTOSAVE:
      code = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE; name = "ENABLE_DISABLE_AUTOSAVE"; break;
  //case ATA_SMART_SAVE: // obsolete since ATA-6, not used by smartmontools
  //  code = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES; name = "SAVE_ATTRIBUTE_VALUES"; break;
    case ATA_SMART_IMMEDIATE_OFFLINE:
      code = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS; name = "EXECUTE_OFFLINE_DIAGS"; break;
    case ATA_SMART_AUTO_OFFLINE:
      code = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE; name = "ENABLE_DISABLE_AUTO_OFFLINE"; break;
    case ATA_SMART_READ_LOG_SECTOR:
      code = IOCTL_SCSI_MINIPORT_READ_SMART_LOG; name = "READ_SMART_LOG"; break;
    case ATA_SMART_WRITE_LOG_SECTOR:
      code = IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG; name = "WRITE_SMART_LOG"; break;
  }
  if (!code) {
    errno = ENOSYS;
    return -1;
  }
796

797
798
799
800
801
802
803
804
805
  // Set SRB
  struct {
    SRB_IO_CONTROL srbc;
    union {
      SENDCMDINPARAMS in;
      SENDCMDOUTPARAMS out;
    } params;
    char space[512-1];
  } sb;
806
  STATIC_ASSERT(sizeof(sb) == sizeof(SRB_IO_CONTROL)+sizeof(SENDCMDINPARAMS)-1+512);
807
  memset(&sb, 0, sizeof(sb));
808

809
810
811
812
813
814
815
  unsigned size;
  if (datasize > 0) {
    if (datasize > (int)sizeof(sb.space)+1) {
      errno = EINVAL;
      return -1;
    }
    size = datasize;
816
  }
817
818
819
820
  else if (datasize < 0) {
    if (-datasize > (int)sizeof(sb.space)+1) {
      errno = EINVAL;
      return -1;
821
    }
822
823
    size = -datasize;
    memcpy(sb.params.in.bBuffer, data, size);
824
  }
825
826
  else if (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS)
    size = sizeof(IDEREGS);
827
  else
828
829
830
831
832
833
834
835
836
    size = 0;
  sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
  memcpy(sb.srbc.Signature, "SCSIDISK", 8); // atapi.sys
  sb.srbc.Timeout = 60; // seconds
  sb.srbc.ControlCode = code;
  //sb.srbc.ReturnCode = 0;
  sb.srbc.Length = sizeof(SENDCMDINPARAMS)-1 + size;
  sb.params.in.irDriveRegs = *regs;
  sb.params.in.cBufferSize = size;
837

838
839
840
841
842
843
844
845
846
847
848
  // Call miniport ioctl
  size += sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)-1;
  DWORD num_out;
  if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
    &sb, size, &sb, size, &num_out, NULL)) {
    long err = GetLastError();
    if (ata_debugmode) {
      pout("  IOCTL_SCSI_MINIPORT_%s failed, Error=%ld\n", name, err);
      print_ide_regs_io(regs, NULL);
    }
    errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
849
    return -1;
850
851
852
853
854
855
856
857
858
  }

  // Check result
  if (sb.srbc.ReturnCode) {
    if (ata_debugmode) {
      pout("  IOCTL_SCSI_MINIPORT_%s failed, ReturnCode=0x%08x\n", name, (unsigned)sb.srbc.ReturnCode);
      print_ide_regs_io(regs, NULL);
    }
    errno = EIO;
859
    return -1;
860
  }
861

862
863
864
865
866
867
868
869
870
  if (sb.params.out.DriverStatus.bDriverError) {
    if (ata_debugmode) {
      pout("  IOCTL_SCSI_MINIPORT_%s failed, DriverError=0x%02x, IDEError=0x%02x\n", name,
        sb.params.out.DriverStatus.bDriverError, sb.params.out.DriverStatus.bIDEError);
      print_ide_regs_io(regs, NULL);
    }
    errno = (!sb.params.out.DriverStatus.bIDEError ? ENOSYS : EIO);
    return -1;
  }
871

872
  if (ata_debugmode > 1) {
chrfranke's avatar
chrfranke committed
873
    pout("  IOCTL_SCSI_MINIPORT_%s succeeded, bytes returned: %u (buffer %u)\n", name,
874
875
876
877
      (unsigned)num_out, (unsigned)sb.params.out.cBufferSize);
    print_ide_regs_io(regs, (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS ?
                             (const IDEREGS *)(sb.params.out.bBuffer) : 0));
  }
878

879
880
881
882
  if (datasize > 0)
    memcpy(data, sb.params.out.bBuffer, datasize);
  else if (datasize == 0 && code == IOCTL_SCSI_MINIPORT_RETURN_STATUS)
    memcpy(regs, sb.params.out.bBuffer, sizeof(IDEREGS));
883

884
885
  return 0;
}
886

887

888
889
/////////////////////////////////////////////////////////////////////////////
// ATA PASS THROUGH via 3ware specific SCSI MINIPORT ioctl
890

891
static int ata_via_3ware_miniport_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize, int port)
892
{
893
894
895
896
897
  struct {
    SRB_IO_CONTROL srbc;
    IDEREGS regs;
    UCHAR buffer[512];
  } sb;
898
  STATIC_ASSERT(sizeof(sb) == sizeof(SRB_IO_CONTROL)+sizeof(IDEREGS)+512);
899

900
901
902
903
904
905
906
907
908
909
910
911
912
  if (!(0 <= datasize && datasize <= (int)sizeof(sb.buffer) && port >= 0)) {
    errno = EINVAL;
    return -1;
  }
  memset(&sb, 0, sizeof(sb));
  strncpy((char *)sb.srbc.Signature, "<3ware>", sizeof(sb.srbc.Signature));
  sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
  sb.srbc.Timeout = 60; // seconds
  sb.srbc.ControlCode = 0xA0000000;
  sb.srbc.ReturnCode = 0;
  sb.srbc.Length = sizeof(IDEREGS) + (datasize > 0 ? datasize : 1);
  sb.regs = *regs;
  sb.regs.bReserved = port;
913

914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
  DWORD num_out;
  if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
    &sb, sizeof(sb), &sb, sizeof(sb), &num_out, NULL)) {
    long err = GetLastError();
    if (ata_debugmode) {
      pout("  ATA via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err);
      print_ide_regs_io(regs, NULL);
    }
    errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
    return -1;
  }

  if (sb.srbc.ReturnCode) {
    if (ata_debugmode) {
      pout("  ATA via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08x\n", (unsigned)sb.srbc.ReturnCode);
      print_ide_regs_io(regs, NULL);
    }
    errno = EIO;
    return -1;
  }

  // Copy data
  if (datasize > 0)
    memcpy(data, sb.buffer, datasize);

  if (ata_debugmode > 1) {
chrfranke's avatar
chrfranke committed
940
    pout("  ATA via IOCTL_SCSI_MINIPORT succeeded, bytes returned: %u\n", (unsigned)num_out);
941
942
943
944
945
    print_ide_regs_io(regs, &sb.regs);
  }
  *regs = sb.regs;

  return 0;
946
947
}

948
949
950
951
952
953
954

/////////////////////////////////////////////////////////////////////////////

// 3ware specific call to update the devicemap returned by SMART_GET_VERSION.
// 3DM/CLI "Rescan Controller" function does not to always update it.

static int update_3ware_devicemap_ioctl(HANDLE hdevice)
955
{
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
  SRB_IO_CONTROL srbc;
  memset(&srbc, 0, sizeof(srbc));
  strncpy((char *)srbc.Signature, "<3ware>", sizeof(srbc.Signature));
  srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
  srbc.Timeout = 60; // seconds
  srbc.ControlCode = 0xCC010014;
  srbc.ReturnCode = 0;
  srbc.Length = 0;

  DWORD num_out;
  if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
    &srbc, sizeof(srbc), &srbc, sizeof(srbc), &num_out, NULL)) {
    long err = GetLastError();
    if (ata_debugmode)
      pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err);
    errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
    return -1;
  }
  if (srbc.ReturnCode) {
    if (ata_debugmode)
      pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08x\n", (unsigned)srbc.ReturnCode);
    errno = EIO;
    return -1;
  }
  if (ata_debugmode > 1)
chrfranke's avatar
chrfranke committed
981
    pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT succeeded\n");
982
  return 0;
983
984
}

985
986
987
988
989
990
991
992
993
994
995
996
997

/////////////////////////////////////////////////////////////////////////////
// IOCTL_STORAGE_QUERY_PROPERTY

union STORAGE_DEVICE_DESCRIPTOR_DATA {
  STORAGE_DEVICE_DESCRIPTOR desc;
  char raw[256];
};

// Get STORAGE_DEVICE_DESCRIPTOR_DATA for device.
// (This works without admin rights)

static int storage_query_property_ioctl(HANDLE hdevice, STORAGE_DEVICE_DESCRIPTOR_DATA * data)
998
{
999
1000
  STORAGE_PROPERTY_QUERY query = {StorageDeviceProperty, PropertyStandardQuery, {0} };
  memset(data, 0, sizeof(*data));
For faster browsing, not all history is shown. View entire blame