4 * Home page of code is: http://www.smartmontools.org
6 * Copyright (C) 2004-8 Geoffrey Keating <geoffk@geoffk.org>
7 * Copyright (C) 2014 Alex Samorukov <samm@os2.kiev.ua>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2, or (at your option)
14 * You should have received a copy of the GNU General Public License
15 * along with smartmontools. If not, see <http://www.gnu.org/licenses/>.
22 #include <mach/mach.h>
23 #include <mach/mach_error.h>
24 #include <mach/mach_init.h>
25 #include <sys/utsname.h>
26 #include <IOKit/IOCFPlugIn.h>
27 #include <IOKit/IOKitLib.h>
28 #include <IOKit/IOReturn.h>
29 #include <IOKit/IOBSD.h>
30 #include <IOKit/storage/IOBlockStorageDevice.h>
31 #include <IOKit/storage/IOStorageDeviceCharacteristics.h>
32 #include <IOKit/storage/IOMedia.h>
33 #include <IOKit/storage/ata/IOATAStorageDefines.h>
34 #include <IOKit/storage/ata/ATASMARTLib.h>
35 #include <CoreFoundation/CoreFoundation.h>
37 // No, I don't know why there isn't a header for this.
38 #define kIOATABlockStorageDeviceClass "IOATABlockStorageDevice"
45 #include "os_darwin.h"
46 #include "dev_interface.h"
48 // Needed by '-V' option (CVS versioning) of smartd/smartctl
49 const char *os_darwin_cpp_cvsid
="$Id: os_darwin.cpp 4214 2016-01-24 22:53:37Z samm2 $" \
50 ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_DARWIN_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID
;
52 // examples for smartctl
53 static const char smartctl_examples
[] =
54 "=================================================== SMARTCTL EXAMPLES =====\n\n"
55 " smartctl -a disk0 (Prints all SMART information)\n\n"
56 " smartctl -t long /dev/disk0 (Executes extended disk self-test)\n\n"
57 #ifdef HAVE_GETOPT_LONG
58 " smartctl --smart=on --saveauto=on /dev/rdisk0 (Enables SMART on first disk)\n\n"
59 " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/disk0\n"
60 " (Prints Self-Test & Attribute errors)\n\n"
62 " smartctl -s on -S on /dev/rdisk0 (Enables SMART on first disk)\n\n"
63 " smartctl -A -l selftest -q errorsonly /dev/disk0\n"
64 " (Prints Self-Test & Attribute errors)\n\n"
66 " smartctl -a IOService:/MacRISC2PE/pci@f4000000/AppleMacRiscPCI/ata-6@D/AppleKauaiATA/ATADeviceNub@0/IOATABlockStorageDriver/IOATABlockStorageDevice\n"
67 " (You can use IOService: ...)\n\n"
68 " smartctl -c IODeviceTree:/pci@f4000000/ata-6@D/@0:0\n"
69 " (... Or IODeviceTree:)\n"
73 // Information that we keep about each device.
77 IOCFPlugInInterface
**plugin
;
78 IOATASMARTInterface
**smartIf
;
81 const char * dev_darwin_cpp_cvsid
= "$Id: os_darwin.cpp 4214 2016-01-24 22:53:37Z samm2 $"
82 DEV_INTERFACE_H_CVSID
;
84 /////////////////////////////////////////////////////////////////////////////
86 namespace os
{ // No need to publish anything, name provided for Doxygen
88 /////////////////////////////////////////////////////////////////////////////
89 /// Implement shared open/close routines with old functions.
91 class darwin_smart_device
92 : virtual public /*implements*/ smart_device
95 explicit darwin_smart_device(const char * mode
)
96 : smart_device(never_called
),
97 m_fd(-1), m_mode(mode
) { }
99 virtual ~darwin_smart_device() throw();
101 virtual bool is_open() const;
105 virtual bool close();
108 /// Return filedesc for derived classes.
114 int m_fd
; ///< filedesc, -1 if not open.
115 const char * m_mode
; ///< Mode string for deviceopen().
119 darwin_smart_device::~darwin_smart_device() throw()
122 darwin_smart_device::close();
125 bool darwin_smart_device::is_open() const
130 // Determine whether 'dev' is a SMART-capable device.
131 static bool is_smart_capable (io_object_t dev
) {
132 CFTypeRef smartCapableKey
;
133 CFDictionaryRef diskChars
;
135 // If the device has kIOPropertySMARTCapableKey, then it's capable,
136 // no matter what it looks like.
137 smartCapableKey
= IORegistryEntryCreateCFProperty
138 (dev
, CFSTR (kIOPropertySMARTCapableKey
),
139 kCFAllocatorDefault
, 0);
142 CFRelease (smartCapableKey
);
146 // If it's an kIOATABlockStorageDeviceClass then we're successful
147 // only if its ATA features indicate it supports SMART.
148 if (IOObjectConformsTo (dev
, kIOATABlockStorageDeviceClass
)
149 && (diskChars
= (CFDictionaryRef
)IORegistryEntryCreateCFProperty
150 (dev
, CFSTR (kIOPropertyDeviceCharacteristicsKey
),
151 kCFAllocatorDefault
, kNilOptions
)) != NULL
)
153 CFNumberRef diskFeatures
= NULL
;
154 UInt32 ataFeatures
= 0;
156 if (CFDictionaryGetValueIfPresent (diskChars
, CFSTR ("ATA Features"),
157 (const void **)&diskFeatures
))
158 CFNumberGetValue (diskFeatures
, kCFNumberLongType
,
160 CFRelease (diskChars
);
162 CFRelease (diskFeatures
);
164 return (ataFeatures
& kIOATAFeatureSMART
) != 0;
169 bool darwin_smart_device::open()
171 // Acceptable device names are:
180 const char *pathname
= get_dev_name();
181 char *type
= const_cast<char*>(m_mode
);
183 if (strcmp (type
, "ATA") != 0)
189 // Find a free device number.
190 for (devnum
= 0; devnum
< sizeof (devices
) / sizeof (devices
[0]); devnum
++)
191 if (! devices
[devnum
].ioob
)
193 if (devnum
== sizeof (devices
) / sizeof (devices
[0]))
200 if (strncmp (pathname
, "/dev/rdisk", 10) == 0)
201 devname
= pathname
+ 6;
202 else if (strncmp (pathname
, "/dev/disk", 9) == 0)
203 devname
= pathname
+ 5;
204 else if (strncmp (pathname
, "disk", 4) == 0)
205 // allow user to just say 'disk0'
211 CFMutableDictionaryRef matcher
;
212 matcher
= IOBSDNameMatching (kIOMasterPortDefault
, 0, devname
);
213 disk
= IOServiceGetMatchingService (kIOMasterPortDefault
, matcher
);
217 disk
= IORegistryEntryFromPath (kIOMasterPortDefault
, pathname
);
226 // Find a SMART-capable driver which is a parent of this device.
227 while (! is_smart_capable (disk
))
230 io_object_t prevdisk
= disk
;
232 // Find this device's parent and try again.
233 err
= IORegistryEntryGetParentEntry (disk
, kIOServicePlane
, &disk
);
234 if (err
!= kIOReturnSuccess
|| ! disk
)
237 IOObjectRelease (prevdisk
);
242 devices
[devnum
].ioob
= disk
;
247 devices
[devnum
].plugin
= NULL
;
248 devices
[devnum
].smartIf
= NULL
;
250 // Create an interface to the ATA SMART library.
251 if (IOCreatePlugInInterfaceForService (disk
,
252 kIOATASMARTUserClientTypeID
,
253 kIOCFPlugInInterfaceID
,
254 &devices
[devnum
].plugin
,
255 &dummy
) == kIOReturnSuccess
)
256 (*devices
[devnum
].plugin
)->QueryInterface
257 (devices
[devnum
].plugin
,
258 CFUUIDGetUUIDBytes ( kIOATASMARTInterfaceID
),
259 (void **)&devices
[devnum
].smartIf
);
265 set_err((errno
==ENOENT
|| errno
==ENOTDIR
) ? ENODEV
: errno
);
271 bool darwin_smart_device::close()
273 int fd
= m_fd
; m_fd
= -1;
274 if (devices
[fd
].smartIf
)
275 (*devices
[fd
].smartIf
)->Release (devices
[fd
].smartIf
);
276 if (devices
[fd
].plugin
)
277 IODestroyPlugInInterface (devices
[fd
].plugin
);
278 IOObjectRelease (devices
[fd
].ioob
);
279 devices
[fd
].ioob
= MACH_PORT_NULL
;
283 // makes a list of ATA or SCSI devices for the DEVICESCAN directive of
284 // smartd. Returns number N of devices, or -1 if out of
285 // memory. Allocates N+1 arrays: one of N pointers (devlist); the
286 // other N arrays each contain null-terminated character strings. In
287 // the case N==0, no arrays are allocated because the array of 0
288 // pointers has zero length, equivalent to calling malloc(0).
289 static int make_device_names (char*** devlist
, const char* name
) {
292 io_object_t device
= MACH_PORT_NULL
;
296 // We treat all devices as ATA so long as they support SMARTLib.
297 if (strcmp (name
, "ATA") != 0)
300 err
= IOServiceGetMatchingServices
301 (kIOMasterPortDefault
, IOServiceMatching (kIOBlockStorageDeviceClass
), &i
);
302 if (err
!= kIOReturnSuccess
)
305 // Count the devices.
307 while ((device
= IOIteratorNext (i
)) != MACH_PORT_NULL
) {
308 if (is_smart_capable (device
))
310 IOObjectRelease (device
);
313 // Create an array of service names.
317 *devlist
= (char**)calloc (result
, sizeof (char *));
319 while ((device
= IOIteratorNext (i
)) != MACH_PORT_NULL
) {
320 if (is_smart_capable (device
))
323 IORegistryEntryGetPath(device
, kIOServicePlane
, devName
);
324 (*devlist
)[index
] = strdup (devName
);
325 if (! (*devlist
)[index
])
329 IOObjectRelease (device
);
336 if (device
!= MACH_PORT_NULL
)
337 IOObjectRelease (device
);
341 for (index
= 0; index
< result
; index
++)
342 if ((*devlist
)[index
])
343 free ((*devlist
)[index
]);
346 if(!result
) // no devs found
352 /////////////////////////////////////////////////////////////////////////////
353 /// Implement standard ATA support
355 class darwin_ata_device
356 : public /*implements*/ ata_device
,
357 public /*extends*/ darwin_smart_device
360 darwin_ata_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
361 virtual bool ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
);
364 // virtual int ata_command_interface(smart_command_set command, int select, char * data);
367 darwin_ata_device::darwin_ata_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
)
368 : smart_device(intf
, dev_name
, "ata", req_type
),
369 darwin_smart_device("ATA")
373 bool darwin_ata_device::ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
)
375 if (!ata_cmd_is_ok(in
,
376 true, // data_out_support
377 true, // multi_sector_support
378 false) // not supported by API
383 char * data
= (char *)in
.buffer
;
385 IOATASMARTInterface
**ifp
= devices
[fd
].smartIf
;
386 IOATASMARTInterface
*smartIf
;
388 int timeoutCount
= 5;
394 clear_err(); errno
= 0;
396 switch (in
.in_regs
.command
) {
397 case ATA_IDENTIFY_DEVICE
:
400 err
= smartIf
->GetATAIdentifyData (ifp
, data
, 512, &dummy
);
401 if (err
!= kIOReturnSuccess
&& err
!= kIOReturnTimeout
402 && err
!= kIOReturnNotResponding
)
403 printf ("identify failed: %#x\n", (unsigned) rc
);
404 if (err
== kIOReturnSuccess
&& isbigendian())
407 /* The system has already byte-swapped, undo it. */
408 for (i
= 0; i
< 256; i
+=2)
413 case ATA_IDENTIFY_PACKET_DEVICE
:
414 case ATA_CHECK_POWER_MODE
:
419 switch (in
.in_regs
.features
) {
420 case ATA_SMART_READ_VALUES
:
421 err
= smartIf
->SMARTReadData (ifp
, (ATASMARTData
*)data
);
423 case ATA_SMART_READ_THRESHOLDS
:
424 err
= smartIf
->SMARTReadDataThresholds (ifp
,
425 (ATASMARTDataThresholds
*)data
);
427 case ATA_SMART_READ_LOG_SECTOR
:
428 err
= smartIf
->SMARTReadLogAtAddress (ifp
, in
.in_regs
.lba_low
, data
, 512 * in
.in_regs
.sector_count
);
430 case ATA_SMART_WRITE_LOG_SECTOR
:
431 err
= smartIf
->SMARTWriteLogAtAddress (ifp
, in
.in_regs
.lba_low
, data
, 512 * in
.in_regs
.sector_count
);
433 case ATA_SMART_ENABLE
:
434 case ATA_SMART_DISABLE
:
435 err
= smartIf
->SMARTEnableDisableOperations (ifp
, in
.in_regs
.features
== ATA_SMART_ENABLE
);
437 case ATA_SMART_STATUS
:
438 if (in
.out_needed
.lba_high
) // statuscheck
441 err
= smartIf
->SMARTReturnStatus (ifp
, &is_failing
);
442 if (err
== kIOReturnSuccess
&& is_failing
) {
443 err
= -1; // thresholds exceeded condition
444 out
.out_regs
.lba_high
= 0x2c; out
.out_regs
.lba_mid
= 0xf4;
447 out
.out_regs
.lba_high
= 0xc2; out
.out_regs
.lba_mid
= 0x4f;
452 case ATA_SMART_AUTOSAVE
:
453 err
= smartIf
->SMARTEnableDisableAutosave (ifp
,
454 (in
.in_regs
.sector_count
== 241 ? true : false));
456 case ATA_SMART_IMMEDIATE_OFFLINE
:
457 select
= in
.in_regs
.lba_low
;
458 if (select
!= SHORT_SELF_TEST
&& select
!= EXTEND_SELF_TEST
)
461 return set_err(ENOSYS
, "Unsupported SMART self-test mode");
463 err
= smartIf
->SMARTExecuteOffLineImmediate (ifp
,
464 select
== EXTEND_SELF_TEST
);
466 case ATA_SMART_AUTO_OFFLINE
:
467 return set_err(ENOSYS
, "SMART command not supported");
469 return set_err(ENOSYS
, "Unknown SMART command");
473 return set_err(ENOSYS
, "Non-SMART commands not implemented");
475 } while ((err
== kIOReturnTimeout
|| err
== kIOReturnNotResponding
)
476 && timeoutCount
-- > 0);
477 if (err
== kIOReturnExclusiveAccess
)
479 rc
= err
== kIOReturnSuccess
? 0 : -1;
488 /////////////////////////////////////////////////////////////////////////////
489 /// Implement platform interface
491 class darwin_smart_interface
492 : public /*implements*/ smart_interface
495 virtual std::string
get_os_version_str();
497 virtual std::string
get_app_examples(const char * appname
);
499 virtual bool scan_smart_devices(smart_device_list
& devlist
, const char * type
,
500 const char * pattern
= 0);
503 virtual ata_device
* get_ata_device(const char * name
, const char * type
);
505 virtual scsi_device
* get_scsi_device(const char * name
, const char * type
);
507 virtual smart_device
* autodetect_smart_device(const char * name
);
512 //////////////////////////////////////////////////////////////////////
514 std::string
darwin_smart_interface::get_os_version_str()
516 // now we are just getting darwin runtime version, to get OSX version more things needs to be done, see
517 // http://stackoverflow.com/questions/11072804/how-do-i-determine-the-os-version-at-runtime-in-os-x-or-ios-without-using-gesta
518 struct utsname osname
;
520 return strprintf("%s %s %s", osname
.sysname
, osname
.release
, osname
.machine
);
523 std::string
darwin_smart_interface::get_app_examples(const char * appname
)
525 if (!strcmp(appname
, "smartctl"))
526 return smartctl_examples
;
527 return ""; // ... so don't print again.
530 ata_device
* darwin_smart_interface::get_ata_device(const char * name
, const char * type
)
532 return new darwin_ata_device(this, name
, type
);
535 scsi_device
* darwin_smart_interface::get_scsi_device(const char *, const char *)
537 return 0; // scsi devices are not supported [yet]
541 smart_device
* darwin_smart_interface::autodetect_smart_device(const char * name
)
543 return new darwin_ata_device(this, name
, "");
546 static void free_devnames(char * * devnames
, int numdevs
)
548 for (int i
= 0; i
< numdevs
; i
++)
553 bool darwin_smart_interface::scan_smart_devices(smart_device_list
& devlist
,
554 const char * type
, const char * pattern
/*= 0*/)
557 set_err(EINVAL
, "DEVICESCAN with pattern not implemented yet");
562 char * * atanames
= 0; int numata
= 0;
563 if (!type
|| !strcmp(type
, "ata")) {
564 numata
= make_device_names(&atanames
, "ATA");
575 for (i
= 0; i
< numata
; i
++) {
576 ata_device
* atadev
= get_ata_device(atanames
[i
], type
);
578 devlist
.push_back(atadev
);
580 free_devnames(atanames
, numata
);
587 /////////////////////////////////////////////////////////////////////////////
588 /// Initialize platform interface and register with smi()
590 void smart_interface::init()
592 static os::darwin_smart_interface the_interface
;
593 smart_interface::set(&the_interface
);