smartmontools  SVN Rev 5304
Utility to control and monitor storage systems with "S.M.A.R.T."
os_darwin.cpp
Go to the documentation of this file.
1 /*
2  * os_darwin.cpp
3  *
4  * Home page of code is: https://www.smartmontools.org
5  *
6  * Copyright (C) 2004-8 Geoffrey Keating <geoffk@geoffk.org>
7  * Copyright (C) 2014 Alex Samorukov <samm@os2.kiev.ua>
8  *
9  * SPDX-License-Identifier: GPL-2.0-or-later
10  */
11 
12 #include <stdbool.h>
13 #include <errno.h>
14 #include <unistd.h>
15 #include <mach/mach.h>
16 #include <mach/mach_error.h>
17 #include <mach/mach_init.h>
18 #include <sys/utsname.h>
19 #include <IOKit/IOCFPlugIn.h>
20 #include <IOKit/IOKitLib.h>
21 #include <IOKit/IOReturn.h>
22 #include <IOKit/IOBSD.h>
23 #include <IOKit/storage/IOBlockStorageDevice.h>
24 #include <IOKit/storage/IOStorageDeviceCharacteristics.h>
25 #include <IOKit/storage/IOMedia.h>
26 #include <IOKit/storage/ata/IOATAStorageDefines.h>
27 #include <IOKit/storage/ata/ATASMARTLib.h>
28 #include <CoreFoundation/CoreFoundation.h>
29 
30 #include "config.h"
31 
32 #include "atacmds.h"
33 #include "scsicmds.h"
34 #include "nvmecmds.h"
35 #include "utility.h"
36 #include "os_darwin.h"
37 #include "dev_interface.h"
38 
39 #define ARGUSED(x) ((void)(x))
40 // Needed by '-V' option (CVS versioning) of smartd/smartctl
41 const char *os_darwin_cpp_cvsid="$Id: os_darwin.cpp 5209 2021-02-14 18:02:51Z samm2 $" \
42 ATACMDS_H_CVSID CONFIG_H_CVSID OS_DARWIN_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
43 
44 // examples for smartctl
45 static const char smartctl_examples[] =
46  "=================================================== SMARTCTL EXAMPLES =====\n\n"
47  " smartctl -a disk0 (Prints all SMART information)\n\n"
48  " smartctl -t long /dev/disk0 (Executes extended disk self-test)\n\n"
49  " smartctl --smart=on --saveauto=on /dev/rdisk0 (Enables SMART on first disk)\n\n"
50  " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/disk0\n"
51  " (Prints Self-Test & Attribute errors)\n\n"
52  " smartctl -a IOService:/MacRISC2PE/pci@f4000000/AppleMacRiscPCI/ata-6@D/AppleKauaiATA/ATADeviceNub@0/IOATABlockStorageDriver/IOATABlockStorageDevice\n"
53  " (You can use IOService: ...)\n\n"
54  " smartctl -c IODeviceTree:/pci@f4000000/ata-6@D/@0:0\n"
55  " (... Or IODeviceTree:)\n"
56  ;
57 
58 
59 // Information that we keep about each device.
60 
61 static struct {
62  io_object_t ioob;
63  IOCFPlugInInterface **plugin;
64  IOATASMARTInterface **smartIf; // ATA devices
66 } devices[20];
67 
68 const char * dev_darwin_cpp_cvsid = "$Id: os_darwin.cpp 5209 2021-02-14 18:02:51Z samm2 $"
70 
71 /////////////////////////////////////////////////////////////////////////////
72 
73 namespace os { // No need to publish anything, name provided for Doxygen
74 
75 /////////////////////////////////////////////////////////////////////////////
76 /// Implement shared open/close routines with old functions.
77 
79 : virtual public /*implements*/ smart_device
80 {
81 public:
82  explicit darwin_smart_device(const char * mode)
84  m_fd(-1), m_mode(mode) { }
85 
86  virtual ~darwin_smart_device();
87 
88  virtual bool is_open() const override;
89 
90  virtual bool open() override;
91 
92  virtual bool close() override;
93 
94 protected:
95  /// Return filedesc for derived classes.
96  int get_fd() const
97  { return m_fd; }
98 
99 private:
100  int m_fd; ///< filedesc, -1 if not open.
101  const char * m_mode; ///< Mode string for deviceopen().
102 };
103 
104 
106 {
107  if (m_fd >= 0)
109 }
110 
112 {
113  return (m_fd >= 0);
114 }
115 
116 // Determine whether 'dev' is a SMART-capable device.
117 static bool is_smart_capable (io_object_t dev, const char * type) {
118  CFTypeRef smartCapableKey = NULL;
119  CFDictionaryRef diskChars;
120 
121  // If the device has kIOPropertySMARTCapableKey, then it's capable,
122  // no matter what it looks like.
123  if (!strcmp("ATA", type)) {
124  smartCapableKey = IORegistryEntryCreateCFProperty
125  (dev, CFSTR (kIOPropertySMARTCapableKey),
126  kCFAllocatorDefault, 0);
127  }
128 
129  else if (!strcmp("NVME", type)) {
130  smartCapableKey = IORegistryEntryCreateCFProperty
131  (dev, CFSTR (kIOPropertyNVMeSMARTCapableKey),
132  kCFAllocatorDefault, 0);
133  }
134 
135  if (smartCapableKey)
136  {
137  CFRelease (smartCapableKey);
138  return true;
139  }
140 
141  // If it's an kIOATABlockStorageDeviceClass then we're successful
142  // only if its ATA features indicate it supports SMART.
143  // This will be broken for NVMe, however it is not needed
144  if (IOObjectConformsTo (dev, kIOATABlockStorageDeviceClass)
145  && (diskChars = (CFDictionaryRef)IORegistryEntryCreateCFProperty
146  (dev, CFSTR (kIOPropertyDeviceCharacteristicsKey),
147  kCFAllocatorDefault, kNilOptions)) != NULL)
148  {
149  CFNumberRef diskFeatures = NULL;
150  UInt32 ataFeatures = 0;
151 
152  if (CFDictionaryGetValueIfPresent (diskChars, CFSTR ("ATA Features"),
153  (const void **)&diskFeatures))
154  CFNumberGetValue (diskFeatures, kCFNumberLongType,
155  &ataFeatures);
156  CFRelease (diskChars);
157  if (diskFeatures)
158  CFRelease (diskFeatures);
159 
160  return (ataFeatures & kIOATAFeatureSMART) != 0;
161  }
162  return false;
163 }
164 
166 {
167  // Acceptable device names are:
168  // /dev/disk*
169  // /dev/rdisk*
170  // disk*
171  // IOService:*
172  // IODeviceTree:*
173  size_t devnum;
174  const char *devname;
175  io_object_t disk;
176  const char *pathname = get_dev_name();
177  char *type = const_cast<char*>(m_mode);
178 
179  if (!(strcmp("ATA", type) || strcmp("NVME", type)))
180  {
181  set_err (EINVAL);
182  return false;
183  }
184 
185  // Find a free device number.
186  for (devnum = 0; devnum < sizeof (devices) / sizeof (devices[0]); devnum++)
187  if (! devices[devnum].ioob)
188  break;
189  if (devnum == sizeof (devices) / sizeof (devices[0]))
190  {
191  set_err (EMFILE);
192  return false;
193  }
194 
195  devname = NULL;
196  if (strncmp (pathname, "/dev/rdisk", 10) == 0)
197  devname = pathname + 6;
198  else if (strncmp (pathname, "/dev/disk", 9) == 0)
199  devname = pathname + 5;
200  else if (strncmp (pathname, "disk", 4) == 0)
201  // allow user to just say 'disk0'
202  devname = pathname;
203 
204  // Find the device. This part should be the same for the NVMe and ATA
205  if (devname)
206  {
207  CFMutableDictionaryRef matcher;
208  matcher = IOBSDNameMatching (kIOMasterPortDefault, 0, devname);
209  disk = IOServiceGetMatchingService (kIOMasterPortDefault, matcher);
210  }
211  else
212  {
213  disk = IORegistryEntryFromPath (kIOMasterPortDefault, pathname);
214  }
215  if (! disk)
216  {
217  set_err(ENOENT);
218  return false;
219  }
220  // Find a SMART-capable driver which is a parent of this device.
221  while (! is_smart_capable (disk, type))
222  {
223  IOReturn err;
224  io_object_t prevdisk = disk;
225 
226  // Find this device's parent and try again.
227  err = IORegistryEntryGetParentEntry (disk, kIOServicePlane, &disk);
228  if (err != kIOReturnSuccess || ! disk)
229  {
230  set_err(ENODEV);
231  IOObjectRelease (prevdisk);
232  return false;
233  }
234  }
235 
236  devices[devnum].ioob = disk;
237 
238  {
239  SInt32 dummy;
240 
241  devices[devnum].plugin = NULL;
242  devices[devnum].smartIf = NULL;
243  devices[devnum].smartIfNVMe = NULL;
244 
245  CFUUIDRef pluginType = NULL;
246  CFUUIDRef smartInterfaceId = NULL;
247  void ** SMARTptr = NULL;
248 
249  if (!strcmp("ATA", type)) {
250  pluginType = kIOATASMARTUserClientTypeID;
251  smartInterfaceId = kIOATASMARTInterfaceID;
252  SMARTptr = (void **)&devices[devnum].smartIf;
253  }
254  else if (!strcmp("NVME", type)) {
255  pluginType = kIONVMeSMARTUserClientTypeID;
256  smartInterfaceId = kIONVMeSMARTInterfaceID;
257  SMARTptr = (void **)&devices[devnum].smartIfNVMe;
258  }
259 
260  // Create an interface to the ATA SMART library.
261  if (IOCreatePlugInInterfaceForService (disk,
262  pluginType,
263  kIOCFPlugInInterfaceID,
264  &devices[devnum].plugin,
265  &dummy) == kIOReturnSuccess)
266  (*devices[devnum].plugin)->QueryInterface
267  (devices[devnum].plugin,
268  CFUUIDGetUUIDBytes ( smartInterfaceId),
269  SMARTptr);
270  else
271  return set_err(ENOSYS, "IOCreatePlugInInterfaceForService failed");
272  }
273 
274 
275  m_fd = devnum;
276  if (m_fd < 0) {
277  set_err((errno==ENOENT || errno==ENOTDIR) ? ENODEV : errno);
278  return false;
279  }
280  return true;
281 }
282 
284 {
285  int fd = m_fd; m_fd = -1;
286  if (devices[fd].smartIf)
287  (*devices[fd].smartIf)->Release (devices[fd].smartIf);
288  if (devices[fd].smartIfNVMe)
289  (*devices[fd].smartIfNVMe)->Release (devices[fd].smartIfNVMe);
290  if (devices[fd].plugin)
291  IODestroyPlugInInterface (devices[fd].plugin);
292  IOObjectRelease (devices[fd].ioob);
293  devices[fd].ioob = MACH_PORT_NULL;
294  return true;
295 }
296 
297 // makes a list of ATA or SCSI devices for the DEVICESCAN directive of
298 // smartd. Returns number N of devices, or -1 if out of
299 // memory. Allocates N+1 arrays: one of N pointers (devlist); the
300 // other N arrays each contain null-terminated character strings. In
301 // the case N==0, no arrays are allocated because the array of 0
302 // pointers has zero length, equivalent to calling malloc(0).
303 static int make_device_names (char*** devlist, const char* name) {
304  IOReturn err;
305  io_iterator_t i;
306  io_object_t device = MACH_PORT_NULL;
307  int result;
308  int index;
309 
310  if (!(strcmp("ATA", name) || strcmp("NVME", name))) {
311  return 0;
312  }
313 
314  err = IOServiceGetMatchingServices
315  (kIOMasterPortDefault, IOServiceMatching (kIOBlockStorageDeviceClass), &i);
316  if (err != kIOReturnSuccess)
317  return -1;
318 
319  // Count the devices.
320  result = 0;
321  while ((device = IOIteratorNext (i)) != MACH_PORT_NULL) {
322  if (is_smart_capable (device, name))
323  result++;
324  IOObjectRelease (device);
325  }
326 
327  // Create an array of service names.
328  IOIteratorReset (i);
329  if (! result)
330  goto error;
331  *devlist = (char**)calloc (result, sizeof (char *));
332  index = 0;
333  while ((device = IOIteratorNext (i)) != MACH_PORT_NULL) {
334  if (is_smart_capable (device, name))
335  {
336  io_string_t devName;
337  IORegistryEntryGetPath(device, kIOServicePlane, devName);
338  (*devlist)[index] = strdup (devName);
339  if (! (*devlist)[index])
340  goto error;
341  index++;
342  }
343  IOObjectRelease (device);
344  }
345 
346  IOObjectRelease (i);
347  return result;
348 
349  error:
350  if (device != MACH_PORT_NULL)
351  IOObjectRelease (device);
352  IOObjectRelease (i);
353  if (*devlist)
354  {
355  for (index = 0; index < result; index++)
356  if ((*devlist)[index])
357  free ((*devlist)[index]);
358  free (*devlist);
359  }
360  if(!result) // no devs found
361  return 0;
362 
363  return -1;
364 }
365 
366 /////////////////////////////////////////////////////////////////////////////
367 /// Implement standard ATA support
368 
370 : public /*implements*/ ata_device,
371  public /*extends*/ darwin_smart_device
372 {
373 public:
374  darwin_ata_device(smart_interface * intf, const char * dev_name, const char * req_type);
375  virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) override;
376 
377 protected:
378  // virtual int ata_command_interface(smart_command_set command, int select, char * data);
379 };
380 
381 darwin_ata_device::darwin_ata_device(smart_interface * intf, const char * dev_name, const char * req_type)
382 : smart_device(intf, dev_name, "ata", req_type),
383  darwin_smart_device("ATA")
384 {
385 }
386 
388 {
389  if (!ata_cmd_is_ok(in,
390  true, // data_out_support
391  true, // multi_sector_support
392  false) // not supported by API
393  )
394  return false;
395 
396  int select = 0;
397  char * data = (char *)in.buffer;
398  int fd = get_fd();
399  IOATASMARTInterface **ifp = devices[fd].smartIf;
400  IOATASMARTInterface *smartIf;
401  io_object_t disk = devices[fd].ioob;
402  IOReturn err;
403  int timeoutCount = 5;
404  int rc = 0;
405 
406  if (! ifp)
407  return false;
408  smartIf = *ifp;
409  clear_err(); errno = 0;
410  do {
411  switch (in.in_regs.command) {
412  case ATA_IDENTIFY_DEVICE:
413  {
414  UInt32 dummy;
415  err = smartIf->GetATAIdentifyData (ifp, data, 512, &dummy);
416  if (err != kIOReturnSuccess && err != kIOReturnTimeout
417  && err != kIOReturnNotResponding)
418  printf ("identify failed: %#x\n", (unsigned) rc);
419  if (err == kIOReturnSuccess && isbigendian())
420  {
421  int i;
422  /* The system has already byte-swapped, undo it. */
423  for (i = 0; i < 256; i+=2)
424  swap2 (data + i);
425  }
426  }
427  break;
430  errno = ENOTSUP;
431  err = -1;
432  break;
433  case ATA_SET_FEATURES:
434  switch(in.in_regs.features) {
435  case ATA_ENABLE_APM:
436  if (in.in_regs.sector_count) {
437  int l = (int) in.in_regs.sector_count;
438  CFNumberRef cfLevel = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &l);
439  kern_return_t r = IORegistryEntrySetCFProperty(disk, CFSTR("APM Level"), cfLevel);
440  CFRelease(cfLevel);
441  if (r) {
442  switch(r) {
443  case kIOReturnNotPrivileged:
444  return set_err(ENOSYS, "Use superuser to manage APM");
445  break;
446  case kIOReturnUnsupported:
447  return set_err(ENOSYS, "APM not supported");
448  break;
449  default:
450  return set_err(ENOSYS, "APM error: %u", r);
451  }
452  }
453  break;
454  }
455  default:
456  return set_err(ENOSYS, "Unsupported ATA feature");
457  }
458  break;
459  case ATA_SMART_CMD:
460  switch (in.in_regs.features) {
462  err = smartIf->SMARTReadData (ifp, (ATASMARTData *)data);
463  break;
465  err = smartIf->SMARTReadDataThresholds (ifp,
466  (ATASMARTDataThresholds *)data);
467  break;
469  err = smartIf->SMARTReadLogAtAddress (ifp, in.in_regs.lba_low, data, 512 * in.in_regs.sector_count);
470  break;
472  err = smartIf->SMARTWriteLogAtAddress (ifp, in.in_regs.lba_low, data, 512 * in.in_regs.sector_count);
473  break;
474  case ATA_SMART_ENABLE:
475  case ATA_SMART_DISABLE:
476  err = smartIf->SMARTEnableDisableOperations (ifp, in.in_regs.features == ATA_SMART_ENABLE);
477  break;
478  case ATA_SMART_STATUS:
479  if (in.out_needed.lba_high) // statuscheck
480  {
481  Boolean is_failing;
482  err = smartIf->SMARTReturnStatus (ifp, &is_failing);
483  if (err == kIOReturnSuccess && is_failing) {
484  err = -1; // thresholds exceeded condition
485  out.out_regs.lba_high = 0x2c; out.out_regs.lba_mid = 0xf4;
486  }
487  else {
488  out.out_regs.lba_high = 0xc2; out.out_regs.lba_mid = 0x4f;
489  }
490  break;
491  }
492  else err = 0;
493  break;
494  case ATA_SMART_AUTOSAVE:
495  err = smartIf->SMARTEnableDisableAutosave (ifp,
496  (in.in_regs.sector_count == 241 ? true : false));
497  break;
499  select = in.in_regs.lba_low;
500  if (select != SHORT_SELF_TEST && select != EXTEND_SELF_TEST)
501  {
502  errno = EINVAL;
503  return set_err(ENOSYS, "Unsupported SMART self-test mode");
504  }
505  err = smartIf->SMARTExecuteOffLineImmediate (ifp,
506  select == EXTEND_SELF_TEST);
507  break;
509  return set_err(ENOSYS, "SMART command not supported");
510  default:
511  return set_err(ENOSYS, "Unknown SMART command");
512  }
513  break;
514  default:
515  return set_err(ENOSYS, "Non-SMART commands not implemented");
516  }
517  } while ((err == kIOReturnTimeout || err == kIOReturnNotResponding)
518  && timeoutCount-- > 0);
519  if (err == kIOReturnExclusiveAccess)
520  errno = EBUSY;
521  rc = err == kIOReturnSuccess ? 0 : -1;
522  if (rc < 0) {
523  if (!get_errno())
524  set_err(errno);
525  return false;
526  }
527  return true;
528 }
529 
530 /////////////////////////////////////////////////////////////////////////////
531 /// Implement platform interface
532 
534 : public /*implements*/ smart_interface
535 {
536 public:
537  virtual std::string get_os_version_str() override;
538 
539  virtual std::string get_app_examples(const char * appname) override;
540 
541  virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
542  const char * pattern = 0) override;
543 
544 protected:
545  virtual ata_device * get_ata_device(const char * name, const char * type) override;
546 
547  virtual scsi_device * get_scsi_device(const char * name, const char * type) override;
548 
549  virtual nvme_device * get_nvme_device(const char * name, const char * type,
550  unsigned nsid) override;
551 
552  virtual smart_device * autodetect_smart_device(const char * name) override;
553 
554 };
555 
556 /////////////////////////////////////////////////////////////////////////////
557 /// NVMe support
558 
560 : public /*implements*/ nvme_device,
561  public /*extends*/ darwin_smart_device
562 {
563 public:
564  darwin_nvme_device(smart_interface * intf, const char * dev_name,
565  const char * req_type, unsigned nsid);
566 
567  virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out) override;
568 };
569 
571  const char * req_type, unsigned nsid)
572 : smart_device(intf, dev_name, "nvme", req_type),
573  nvme_device(nsid),
574  darwin_smart_device("NVME")
575 {
576 }
577 
579 {
580  ARGUSED(out);
581  int fd = get_fd();
582  IONVMeSMARTInterface **ifp = devices[fd].smartIfNVMe;
584  IOReturn err = 0;
585  unsigned int page = in.cdw10 & 0xff;
586 
587  if (! ifp)
588  return false;
589  smartIfNVMe = *ifp;
590  // currently only GetIdentifyData and GetLogPage are supported
591  switch (in.opcode) {
593  err = smartIfNVMe->GetIdentifyData(ifp, (struct nvme_id_ctrl *) in.buffer, in.nsid);
594  if (err)
595  return set_err(ENOSYS, "GetIdentifyData failed: system=0x%x, sub=0x%x, code=%d",
596  err_get_system(err), err_get_sub(err), err_get_code(err));
597  break;
599  err = smartIfNVMe->GetLogPage(ifp, in.buffer, page, in.size / 4 - 1);
600  if (err)
601  return set_err(ENOSYS, "GetLogPage failed: system=0x%x, sub=0x%x, code=%d",
602  err_get_system(err), err_get_sub(err), err_get_code(err));
603  break;
604  default:
605  return set_err(ENOSYS, "NVMe admin command 0x%02x is not supported", in.opcode);
606  }
607  return true;
608 }
609 //////////////////////////////////////////////////////////////////////
610 
612 {
613  // now we are just getting darwin runtime version, to get OSX version more things needs to be done, see
614  // http://stackoverflow.com/questions/11072804/how-do-i-determine-the-os-version-at-runtime-in-os-x-or-ios-without-using-gesta
615  struct utsname osname;
616  uname(&osname);
617  return strprintf("%s %s %s", osname.sysname, osname.release, osname.machine);
618 }
619 
620 std::string darwin_smart_interface::get_app_examples(const char * appname)
621 {
622  if (!strcmp(appname, "smartctl"))
623  return smartctl_examples;
624  return ""; // ... so don't print again.
625 }
626 
627 ata_device * darwin_smart_interface::get_ata_device(const char * name, const char * type)
628 {
629  return new darwin_ata_device(this, name, type);
630 }
631 
633 {
634  return 0; // scsi devices are not supported [yet]
635 }
636 
637 nvme_device * darwin_smart_interface::get_nvme_device(const char * name, const char * type,
638  unsigned nsid)
639 {
640  return new darwin_nvme_device(this, name, type, nsid);
641 }
642 
644 { // TODO - refactor as a function
645  // Acceptable device names are:
646  // /dev/disk*
647  // /dev/rdisk*
648  // disk*
649  // IOService:*
650  // IODeviceTree:*
651  const char *devname = NULL;
652  io_object_t disk;
653 
654  if (strncmp (name, "/dev/rdisk", 10) == 0)
655  devname = name + 6;
656  else if (strncmp (name, "/dev/disk", 9) == 0)
657  devname = name + 5;
658  else if (strncmp (name, "disk", 4) == 0)
659  // allow user to just say 'disk0'
660  devname = name;
661  // Find the device. This part should be the same for the NVMe and ATA
662  if (devname) {
663  CFMutableDictionaryRef matcher;
664  matcher = IOBSDNameMatching (kIOMasterPortDefault, 0, devname);
665  disk = IOServiceGetMatchingService (kIOMasterPortDefault, matcher);
666  }
667  else {
668  disk = IORegistryEntryFromPath (kIOMasterPortDefault, name);
669  }
670  if (! disk) {
671  return 0;
672  }
673  io_registry_entry_t tmpdisk=disk;
674 
675 
676  while (! is_smart_capable (tmpdisk, "ATA"))
677  {
678  IOReturn err;
679  io_object_t prevdisk = tmpdisk;
680 
681  // Find this device's parent and try again.
682  err = IORegistryEntryGetParentEntry (tmpdisk, kIOServicePlane, &tmpdisk);
683  if (err != kIOReturnSuccess || ! tmpdisk)
684  {
685  IOObjectRelease (prevdisk);
686  break;
687  }
688  }
689  if (tmpdisk)
690  return new darwin_ata_device(this, name, "");
691  tmpdisk=disk;
692  while (! is_smart_capable (tmpdisk, "NVME"))
693  {
694  IOReturn err;
695  io_object_t prevdisk = tmpdisk;
696 
697  // Find this device's parent and try again.
698  err = IORegistryEntryGetParentEntry (tmpdisk, kIOServicePlane, &tmpdisk);
699  if (err != kIOReturnSuccess || ! tmpdisk)
700  {
701  IOObjectRelease (prevdisk);
702  break;
703  }
704  }
705  if (tmpdisk)
706  return new darwin_nvme_device(this, name, "", 0);
707 
708  // try ATA as a last option, for compatibility
709  return new darwin_ata_device(this, name, "");
710 }
711 
712 static void free_devnames(char * * devnames, int numdevs)
713 {
714  for (int i = 0; i < numdevs; i++)
715  free(devnames[i]);
716  free(devnames);
717 }
718 
720  const char * type, const char * pattern /*= 0*/)
721 {
722  if (pattern) {
723  set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");
724  return false;
725  }
726 
727  // Make namelists
728  char * * atanames = 0; int numata = 0;
729  if (!type || !strcmp(type, "ata")) {
730  numata = make_device_names(&atanames, "ATA");
731  if (numata < 0) {
732  set_err(ENOMEM);
733  return false;
734  }
735  }
736  char * * nvmenames = 0; int numnvme = 0;
737  if (
738 #ifdef WITH_NVME_DEVICESCAN // TODO: Remove when NVMe support is no longer EXPERIMENTAL
739  !type ||
740 #else
741  type &&
742 #endif
743  !strcmp(type, "nvme")) {
744  numnvme = make_device_names(&nvmenames, "NVME");
745  if (numnvme < 0) {
746  set_err(ENOMEM);
747  return false;
748  }
749  }
750 
751  // Add to devlist
752  int i;
753  if (!type)
754  type="";
755  for (i = 0; i < numata; i++) {
756  ata_device * atadev = get_ata_device(atanames[i], type);
757  if (atadev)
758  devlist.push_back(atadev);
759  }
760  free_devnames(atanames, numata);
761 
762  for (i = 0; i < numnvme; i++) {
763  nvme_device * nvmedev = get_nvme_device(nvmenames[i], type, 0); // default nsid
764  if (nvmedev)
765  devlist.push_back(nvmedev);
766  }
767  free_devnames(nvmenames, numnvme);
768 
769  return true;
770 }
771 
772 } // namespace
773 
774 
775 /////////////////////////////////////////////////////////////////////////////
776 /// Initialize platform interface and register with smi()
777 
779 {
780  static os::darwin_smart_interface the_interface;
781  smart_interface::set(&the_interface);
782 }
void swap2(char *location)
Definition: atacmds.cpp:304
#define ATA_SMART_AUTO_OFFLINE
Definition: atacmds.h:94
#define ATA_IDENTIFY_DEVICE
Definition: atacmds.h:53
#define ATA_SMART_WRITE_LOG_SECTOR
Definition: atacmds.h:87
#define ATA_IDENTIFY_PACKET_DEVICE
Definition: atacmds.h:54
#define ATA_SMART_STATUS
Definition: atacmds.h:91
#define ATA_ENABLE_APM
Definition: atacmds.h:70
#define SHORT_SELF_TEST
Definition: atacmds.h:98
#define ATA_SET_FEATURES
Definition: atacmds.h:59
#define ATA_SMART_READ_VALUES
Definition: atacmds.h:81
#define ATA_SMART_READ_THRESHOLDS
Definition: atacmds.h:82
#define EXTEND_SELF_TEST
Definition: atacmds.h:99
#define ATA_SMART_READ_LOG_SECTOR
Definition: atacmds.h:86
#define ATA_SMART_CMD
Definition: atacmds.h:56
#define ATA_SMART_IMMEDIATE_OFFLINE
Definition: atacmds.h:85
#define ATA_SMART_ENABLE
Definition: atacmds.h:89
#define ATA_SMART_AUTOSAVE
Definition: atacmds.h:83
#define ATA_SMART_DISABLE
Definition: atacmds.h:90
#define ATA_CHECK_POWER_MODE
Definition: atacmds.h:52
ATA device access.
bool ata_cmd_is_ok(const ata_cmd_in &in, bool data_out_support=false, bool multi_sector_support=false, bool ata_48bit_support=false)
Check command input parameters (old version).
NVMe device access.
Implement standard ATA support.
Definition: os_darwin.cpp:372
virtual bool ata_pass_through(const ata_cmd_in &in, ata_cmd_out &out) override
ATA pass through.
Definition: os_darwin.cpp:387
darwin_ata_device(smart_interface *intf, const char *dev_name, const char *req_type)
Definition: os_darwin.cpp:381
virtual bool nvme_pass_through(const nvme_cmd_in &in, nvme_cmd_out &out) override
NVMe pass through.
Definition: os_darwin.cpp:578
darwin_nvme_device(smart_interface *intf, const char *dev_name, const char *req_type, unsigned nsid)
Definition: os_darwin.cpp:570
Implement shared open/close routines with old functions.
Definition: os_darwin.cpp:80
virtual ~darwin_smart_device()
Definition: os_darwin.cpp:105
darwin_smart_device(const char *mode)
Definition: os_darwin.cpp:82
virtual bool close() override
Close device, return false on error.
Definition: os_darwin.cpp:283
virtual bool is_open() const override
Return true if device is open.
Definition: os_darwin.cpp:111
virtual bool open() override
Open device, return false on error.
Definition: os_darwin.cpp:165
int m_fd
filedesc, -1 if not open.
Definition: os_darwin.cpp:100
const char * m_mode
Mode string for deviceopen().
Definition: os_darwin.cpp:101
int get_fd() const
Return filedesc for derived classes.
Definition: os_darwin.cpp:96
Implement platform interface.
Definition: os_darwin.cpp:535
virtual std::string get_app_examples(const char *appname) override
Return example string for program 'appname'.
Definition: os_darwin.cpp:620
virtual scsi_device * get_scsi_device(const char *name, const char *type) override
Return standard SCSI device.
Definition: os_darwin.cpp:632
virtual std::string get_os_version_str() override
Return info string about build host and/or OS version.
Definition: os_darwin.cpp:611
virtual ata_device * get_ata_device(const char *name, const char *type) override
Return standard ATA device.
Definition: os_darwin.cpp:627
virtual bool scan_smart_devices(smart_device_list &devlist, const char *type, const char *pattern=0) override
Fill 'devlist' with devices of some 'type' with device names specified by some optional 'pattern'.
Definition: os_darwin.cpp:719
virtual smart_device * autodetect_smart_device(const char *name) override
Autodetect device if no device type specified.
Definition: os_darwin.cpp:643
virtual nvme_device * get_nvme_device(const char *name, const char *type, unsigned nsid) override
Return standard NVMe device.
Definition: os_darwin.cpp:637
SCSI device access.
List of devices for DEVICESCAN.
void push_back(smart_device *dev)
Base class for all devices.
Definition: dev_interface.h:33
int get_errno() const
Get last error number.
bool set_err(int no, const char *msg,...) __attribute_format_printf(3
Set last error number and message.
const char * get_dev_name() const
Get device (path)name.
void clear_err()
Clear last error info.
The platform interface abstraction.
static void set(smart_interface *intf)
Set interface to use, must be called from init().
static void init()
Initialize platform interface and register with smi().
Definition: dev_legacy.cpp:334
bool set_err(int no, const char *msg,...) __attribute_format_printf(3
Set last error number and message.
#define DEV_INTERFACE_H_CVSID
Definition: dev_interface.h:14
ptr_t data
Definition: megaraid.h:15
static int make_device_names(char ***devlist, const char *name)
Definition: os_darwin.cpp:303
static void free_devnames(char **devnames, int numdevs)
Definition: dev_legacy.cpp:269
static bool is_smart_capable(io_object_t dev, const char *type)
Definition: os_darwin.cpp:117
@ nvme_admin_identify
Definition: nvmecmds.h:208
@ nvme_admin_get_log_page
Definition: nvmecmds.h:205
uint32_t nsid
IONVMeSMARTInterface ** smartIfNVMe
Definition: os_darwin.cpp:65
static const char smartctl_examples[]
Definition: os_darwin.cpp:45
#define ARGUSED(x)
Definition: os_darwin.cpp:39
const char * dev_darwin_cpp_cvsid
Definition: os_darwin.cpp:68
const char * os_darwin_cpp_cvsid
Definition: os_darwin.cpp:41
static struct @44 devices[20]
io_object_t ioob
Definition: os_darwin.cpp:62
IOCFPlugInInterface ** plugin
Definition: os_darwin.cpp:63
IOATASMARTInterface ** smartIf
Definition: os_darwin.cpp:64
#define kIOATABlockStorageDeviceClass
Definition: os_darwin.h:16
#define kIONVMeSMARTUserClientTypeID
Definition: os_darwin.h:28
#define OS_DARWIN_H_CVSID
Definition: os_darwin.h:14
#define kIONVMeSMARTInterfaceID
Definition: os_darwin.h:33
#define kIOPropertyNVMeSMARTCapableKey
Definition: os_darwin.h:25
#define kIOPropertySMARTCapableKey
Definition: os_darwin.h:21
#define ENOTSUP
Definition: os_linux.cpp:87
#define SCSICMDS_H_CVSID
Definition: scsicmds.h:22
IOReturn(* GetIdentifyData)(void *interface, struct nvme_id_ctrl *NVMeIdentifyControllerStruct, unsigned int ns)
Definition: os_darwin.h:49
IOReturn(* GetLogPage)(void *interface, void *data, unsigned int logPageId, unsigned int numDWords)
Definition: os_darwin.h:56
ATA pass through input parameters.
void * buffer
Pointer to data buffer.
ata_in_regs_48bit in_regs
Input registers.
ata_out_regs_flags out_needed
True if output register value needed.
ATA pass through output parameters.
ata_out_regs_48bit out_regs
Output registers.
ata_register sector_count
ata_register lba_low
ata_register features
ata_register command
ata_register lba_mid
ata_register lba_high
NVMe pass through input parameters.
unsigned cdw10
unsigned char opcode
Opcode (CDW0 07:00)
unsigned size
Size of buffer.
unsigned nsid
Namespace ID.
void * buffer
Pointer to data buffer.
NVMe pass through output parameters.
std::string strprintf(const char *fmt,...)
Definition: utility.cpp:772
#define UTILITY_H_CVSID
Definition: utility.h:16
bool isbigendian()
Definition: utility.h:81