]> git.proxmox.com Git - mirror_smartmontools-debian.git/blame - os_darwin.cpp
import smartmontools 7.0
[mirror_smartmontools-debian.git] / os_darwin.cpp
CommitLineData
832b75ed 1/*
d2e702cf 2 * os_darwin.cpp
832b75ed 3 *
a86ec89e 4 * Home page of code is: http://www.smartmontools.org
832b75ed 5 *
34ad0c5f 6 * Copyright (C) 2004-8 Geoffrey Keating <geoffk@geoffk.org>
d2e702cf 7 * Copyright (C) 2014 Alex Samorukov <samm@os2.kiev.ua>
832b75ed 8 *
ff28b140 9 * SPDX-License-Identifier: GPL-2.0-or-later
832b75ed
GG
10 */
11
12#include <stdbool.h>
13#include <errno.h>
a37e7145 14#include <unistd.h>
832b75ed
GG
15#include <mach/mach.h>
16#include <mach/mach_error.h>
17#include <mach/mach_init.h>
a86ec89e 18#include <sys/utsname.h>
832b75ed
GG
19#include <IOKit/IOCFPlugIn.h>
20#include <IOKit/IOKitLib.h>
21#include <IOKit/IOReturn.h>
22#include <IOKit/IOBSD.h>
4d59bff9 23#include <IOKit/storage/IOBlockStorageDevice.h>
832b75ed
GG
24#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
25#include <IOKit/storage/IOMedia.h>
4d59bff9
GG
26#include <IOKit/storage/ata/IOATAStorageDefines.h>
27#include <IOKit/storage/ata/ATASMARTLib.h>
832b75ed
GG
28#include <CoreFoundation/CoreFoundation.h>
29
832b75ed 30#include "config.h"
ff28b140 31
832b75ed
GG
32#include "atacmds.h"
33#include "scsicmds.h"
f9e10201 34#include "nvmecmds.h"
832b75ed 35#include "utility.h"
832b75ed 36#include "os_darwin.h"
d2e702cf 37#include "dev_interface.h"
832b75ed 38
f9e10201 39#define ARGUSED(x) ((void)(x))
832b75ed 40// Needed by '-V' option (CVS versioning) of smartd/smartctl
ff28b140
TL
41const char *os_darwin_cpp_cvsid="$Id: os_darwin.cpp 4831 2018-11-09 07:18:23Z samm2 $" \
42ATACMDS_H_CVSID CONFIG_H_CVSID OS_DARWIN_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
832b75ed 43
d2e702cf
GI
44// examples for smartctl
45static const char smartctl_examples[] =
46 "=================================================== SMARTCTL EXAMPLES =====\n\n"
832b75ed
GG
47 " smartctl -a disk0 (Prints all SMART information)\n\n"
48 " smartctl -t long /dev/disk0 (Executes extended disk self-test)\n\n"
832b75ed
GG
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"
832b75ed
GG
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"
d2e702cf
GI
56 ;
57
58
59// Information that we keep about each device.
60
61static struct {
62 io_object_t ioob;
63 IOCFPlugInInterface **plugin;
f9e10201
JD
64 IOATASMARTInterface **smartIf; // ATA devices
65 IONVMeSMARTInterface **smartIfNVMe;
d2e702cf
GI
66} devices[20];
67
ff28b140 68const char * dev_darwin_cpp_cvsid = "$Id: os_darwin.cpp 4831 2018-11-09 07:18:23Z samm2 $"
d2e702cf
GI
69 DEV_INTERFACE_H_CVSID;
70
71/////////////////////////////////////////////////////////////////////////////
72
73namespace os { // No need to publish anything, name provided for Doxygen
74
75/////////////////////////////////////////////////////////////////////////////
76/// Implement shared open/close routines with old functions.
77
78class darwin_smart_device
79: virtual public /*implements*/ smart_device
80{
81public:
82 explicit darwin_smart_device(const char * mode)
83 : smart_device(never_called),
84 m_fd(-1), m_mode(mode) { }
85
86 virtual ~darwin_smart_device() throw();
87
88 virtual bool is_open() const;
89
90 virtual bool open();
91
92 virtual bool close();
93
94protected:
95 /// Return filedesc for derived classes.
96 int get_fd() const
97 { return m_fd; }
98
d2e702cf
GI
99private:
100 int m_fd; ///< filedesc, -1 if not open.
101 const char * m_mode; ///< Mode string for deviceopen().
102};
103
104
105darwin_smart_device::~darwin_smart_device() throw()
106{
107 if (m_fd >= 0)
108 darwin_smart_device::close();
832b75ed
GG
109}
110
d2e702cf
GI
111bool darwin_smart_device::is_open() const
112{
113 return (m_fd >= 0);
832b75ed
GG
114}
115
4d59bff9 116// Determine whether 'dev' is a SMART-capable device.
f9e10201
JD
117static bool is_smart_capable (io_object_t dev, const char * type) {
118 CFTypeRef smartCapableKey = NULL;
4d59bff9
GG
119 CFDictionaryRef diskChars;
120
121 // If the device has kIOPropertySMARTCapableKey, then it's capable,
122 // no matter what it looks like.
f9e10201 123 if (!strcmp("ATA", type)) {
4d59bff9
GG
124 smartCapableKey = IORegistryEntryCreateCFProperty
125 (dev, CFSTR (kIOPropertySMARTCapableKey),
126 kCFAllocatorDefault, 0);
f9e10201
JD
127 }
128
129 else if (!strcmp("NVME", type)) {
130 smartCapableKey = IORegistryEntryCreateCFProperty
131 (dev, CFSTR (kIOPropertyNVMeSMARTCapableKey),
132 kCFAllocatorDefault, 0);
133 }
134
4d59bff9
GG
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.
f9e10201 143 // This will be broken for NVMe, however it is not needed
4d59bff9 144 if (IOObjectConformsTo (dev, kIOATABlockStorageDeviceClass)
d2e702cf
GI
145 && (diskChars = (CFDictionaryRef)IORegistryEntryCreateCFProperty
146 (dev, CFSTR (kIOPropertyDeviceCharacteristicsKey),
147 kCFAllocatorDefault, kNilOptions)) != NULL)
4d59bff9
GG
148 {
149 CFNumberRef diskFeatures = NULL;
150 UInt32 ataFeatures = 0;
151
152 if (CFDictionaryGetValueIfPresent (diskChars, CFSTR ("ATA Features"),
d2e702cf
GI
153 (const void **)&diskFeatures))
154 CFNumberGetValue (diskFeatures, kCFNumberLongType,
155 &ataFeatures);
4d59bff9
GG
156 CFRelease (diskChars);
157 if (diskFeatures)
d2e702cf 158 CFRelease (diskFeatures);
4d59bff9
GG
159
160 return (ataFeatures & kIOATAFeatureSMART) != 0;
161 }
162 return false;
163}
164
d2e702cf
GI
165bool darwin_smart_device::open()
166{
167 // Acceptable device names are:
168 // /dev/disk*
169 // /dev/rdisk*
170 // disk*
171 // IOService:*
172 // IODeviceTree:*
832b75ed
GG
173 size_t devnum;
174 const char *devname;
175 io_object_t disk;
d2e702cf
GI
176 const char *pathname = get_dev_name();
177 char *type = const_cast<char*>(m_mode);
832b75ed 178
f9e10201 179 if (!(strcmp("ATA", type) || strcmp("NVME", type)))
832b75ed 180 {
a86ec89e
GI
181 set_err (EINVAL);
182 return false;
832b75ed
GG
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 {
a86ec89e
GI
191 set_err (EMFILE);
192 return false;
832b75ed
GG
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
f9e10201 204 // Find the device. This part should be the same for the NVMe and ATA
832b75ed
GG
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 }
832b75ed
GG
215 if (! disk)
216 {
a86ec89e
GI
217 set_err(ENOENT);
218 return false;
832b75ed 219 }
4d59bff9 220 // Find a SMART-capable driver which is a parent of this device.
f9e10201 221 while (! is_smart_capable (disk, type))
832b75ed
GG
222 {
223 IOReturn err;
4d59bff9 224 io_object_t prevdisk = disk;
832b75ed 225
4d59bff9
GG
226 // Find this device's parent and try again.
227 err = IORegistryEntryGetParentEntry (disk, kIOServicePlane, &disk);
832b75ed 228 if (err != kIOReturnSuccess || ! disk)
d2e702cf 229 {
a86ec89e 230 set_err(ENODEV);
d2e702cf 231 IOObjectRelease (prevdisk);
a86ec89e 232 return false;
d2e702cf 233 }
832b75ed 234 }
832b75ed 235
4d59bff9
GG
236 devices[devnum].ioob = disk;
237
832b75ed
GG
238 {
239 SInt32 dummy;
240
241 devices[devnum].plugin = NULL;
242 devices[devnum].smartIf = NULL;
f9e10201
JD
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 }
832b75ed
GG
259
260 // Create an interface to the ATA SMART library.
4d59bff9 261 if (IOCreatePlugInInterfaceForService (disk,
f9e10201 262 pluginType,
d2e702cf
GI
263 kIOCFPlugInInterfaceID,
264 &devices[devnum].plugin,
265 &dummy) == kIOReturnSuccess)
266 (*devices[devnum].plugin)->QueryInterface
267 (devices[devnum].plugin,
f9e10201
JD
268 CFUUIDGetUUIDBytes ( smartInterfaceId),
269 SMARTptr);
270 else
271 return set_err(ENOSYS, "IOCreatePlugInInterfaceForService failed");
832b75ed 272 }
a86ec89e
GI
273
274
d2e702cf
GI
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;
832b75ed
GG
281}
282
d2e702cf
GI
283bool darwin_smart_device::close()
284{
285 int fd = m_fd; m_fd = -1;
832b75ed
GG
286 if (devices[fd].smartIf)
287 (*devices[fd].smartIf)->Release (devices[fd].smartIf);
f9e10201
JD
288 if (devices[fd].smartIfNVMe)
289 (*devices[fd].smartIfNVMe)->Release (devices[fd].smartIfNVMe);
832b75ed
GG
290 if (devices[fd].plugin)
291 IODestroyPlugInInterface (devices[fd].plugin);
292 IOObjectRelease (devices[fd].ioob);
293 devices[fd].ioob = MACH_PORT_NULL;
d2e702cf
GI
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).
303static 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
f9e10201 310 if (!(strcmp("ATA", name) || strcmp("NVME", name))) {
d2e702cf 311 return 0;
f9e10201 312 }
d2e702cf
GI
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) {
f9e10201 322 if (is_smart_capable (device, name))
d2e702cf
GI
323 result++;
324 IOObjectRelease (device);
325 }
326
327 // Create an array of service names.
328 IOIteratorReset (i);
a86ec89e 329 if (! result)
d2e702cf 330 goto error;
a86ec89e 331 *devlist = (char**)calloc (result, sizeof (char *));
d2e702cf
GI
332 index = 0;
333 while ((device = IOIteratorNext (i)) != MACH_PORT_NULL) {
f9e10201 334 if (is_smart_capable (device, name))
d2e702cf
GI
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])
ff28b140
TL
357 free ((*devlist)[index]);
358 free (*devlist);
359 }
a86ec89e
GI
360 if(!result) // no devs found
361 return 0;
362
d2e702cf
GI
363 return -1;
364}
365
366/////////////////////////////////////////////////////////////////////////////
367/// Implement standard ATA support
368
369class darwin_ata_device
370: public /*implements*/ ata_device,
371 public /*extends*/ darwin_smart_device
372{
373public:
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);
376
377protected:
378 // virtual int ata_command_interface(smart_command_set command, int select, char * data);
379};
380
381darwin_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{
832b75ed
GG
385}
386
d2e702cf 387bool darwin_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
832b75ed 388{
d2e702cf
GI
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();
832b75ed
GG
399 IOATASMARTInterface **ifp = devices[fd].smartIf;
400 IOATASMARTInterface *smartIf;
401 IOReturn err;
a37e7145 402 int timeoutCount = 5;
d2e702cf 403 int rc = 0;
832b75ed
GG
404
405 if (! ifp)
ff28b140 406 return false;
832b75ed 407 smartIf = *ifp;
d2e702cf 408 clear_err(); errno = 0;
4d59bff9 409 do {
d2e702cf
GI
410 switch (in.in_regs.command) {
411 case ATA_IDENTIFY_DEVICE:
832b75ed 412 {
d2e702cf
GI
413 UInt32 dummy;
414 err = smartIf->GetATAIdentifyData (ifp, data, 512, &dummy);
415 if (err != kIOReturnSuccess && err != kIOReturnTimeout
416 && err != kIOReturnNotResponding)
417 printf ("identify failed: %#x\n", (unsigned) rc);
418 if (err == kIOReturnSuccess && isbigendian())
419 {
420 int i;
421 /* The system has already byte-swapped, undo it. */
422 for (i = 0; i < 256; i+=2)
423 swap2 (data + i);
424 }
425 }
426 break;
427 case ATA_IDENTIFY_PACKET_DEVICE:
428 case ATA_CHECK_POWER_MODE:
429 errno = ENOTSUP;
430 err = -1;
431 break;
432 case ATA_SMART_CMD:
433 switch (in.in_regs.features) {
434 case ATA_SMART_READ_VALUES:
435 err = smartIf->SMARTReadData (ifp, (ATASMARTData *)data);
436 break;
437 case ATA_SMART_READ_THRESHOLDS:
438 err = smartIf->SMARTReadDataThresholds (ifp,
439 (ATASMARTDataThresholds *)data);
440 break;
441 case ATA_SMART_READ_LOG_SECTOR:
442 err = smartIf->SMARTReadLogAtAddress (ifp, in.in_regs.lba_low, data, 512 * in.in_regs.sector_count);
443 break;
444 case ATA_SMART_WRITE_LOG_SECTOR:
445 err = smartIf->SMARTWriteLogAtAddress (ifp, in.in_regs.lba_low, data, 512 * in.in_regs.sector_count);
446 break;
447 case ATA_SMART_ENABLE:
448 case ATA_SMART_DISABLE:
449 err = smartIf->SMARTEnableDisableOperations (ifp, in.in_regs.features == ATA_SMART_ENABLE);
450 break;
451 case ATA_SMART_STATUS:
452 if (in.out_needed.lba_high) // statuscheck
453 {
454 Boolean is_failing;
455 err = smartIf->SMARTReturnStatus (ifp, &is_failing);
456 if (err == kIOReturnSuccess && is_failing) {
457 err = -1; // thresholds exceeded condition
458 out.out_regs.lba_high = 0x2c; out.out_regs.lba_mid = 0xf4;
459 }
ff28b140 460 else {
d2e702cf 461 out.out_regs.lba_high = 0xc2; out.out_regs.lba_mid = 0x4f;
ff28b140 462 }
d2e702cf
GI
463 break;
464 }
465 else err = 0;
466 break;
467 case ATA_SMART_AUTOSAVE:
468 err = smartIf->SMARTEnableDisableAutosave (ifp,
469 (in.in_regs.sector_count == 241 ? true : false));
470 break;
471 case ATA_SMART_IMMEDIATE_OFFLINE:
472 select = in.in_regs.lba_low;
473 if (select != SHORT_SELF_TEST && select != EXTEND_SELF_TEST)
474 {
475 errno = EINVAL;
a86ec89e 476 return set_err(ENOSYS, "Unsupported SMART self-test mode");
d2e702cf
GI
477 }
478 err = smartIf->SMARTExecuteOffLineImmediate (ifp,
479 select == EXTEND_SELF_TEST);
480 break;
481 case ATA_SMART_AUTO_OFFLINE:
482 return set_err(ENOSYS, "SMART command not supported");
4d59bff9 483 default:
d2e702cf 484 return set_err(ENOSYS, "Unknown SMART command");
832b75ed 485 }
d2e702cf
GI
486 break;
487 default:
488 return set_err(ENOSYS, "Non-SMART commands not implemented");
489 }
a37e7145 490 } while ((err == kIOReturnTimeout || err == kIOReturnNotResponding)
d2e702cf 491 && timeoutCount-- > 0);
832b75ed
GG
492 if (err == kIOReturnExclusiveAccess)
493 errno = EBUSY;
d2e702cf
GI
494 rc = err == kIOReturnSuccess ? 0 : -1;
495 if (rc < 0) {
496 if (!get_errno())
497 set_err(errno);
498 return false;
499 }
500 return true;
501}
502
503/////////////////////////////////////////////////////////////////////////////
504/// Implement platform interface
505
506class darwin_smart_interface
507: public /*implements*/ smart_interface
508{
509public:
a86ec89e
GI
510 virtual std::string get_os_version_str();
511
d2e702cf
GI
512 virtual std::string get_app_examples(const char * appname);
513
514 virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
515 const char * pattern = 0);
516
517protected:
518 virtual ata_device * get_ata_device(const char * name, const char * type);
519
520 virtual scsi_device * get_scsi_device(const char * name, const char * type);
521
f9e10201
JD
522 virtual nvme_device * get_nvme_device(const char * name, const char * type,
523 unsigned nsid);
524
d2e702cf
GI
525 virtual smart_device * autodetect_smart_device(const char * name);
526
527};
528
f9e10201
JD
529/////////////////////////////////////////////////////////////////////////////
530/// NVMe support
531
532class darwin_nvme_device
533: public /*implements*/ nvme_device,
534 public /*extends*/ darwin_smart_device
535{
536public:
537 darwin_nvme_device(smart_interface * intf, const char * dev_name,
538 const char * req_type, unsigned nsid);
d2e702cf 539
f9e10201
JD
540 virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out);
541};
542
543darwin_nvme_device::darwin_nvme_device(smart_interface * intf, const char * dev_name,
544 const char * req_type, unsigned nsid)
545: smart_device(intf, dev_name, "nvme", req_type),
546 nvme_device(nsid),
547 darwin_smart_device("NVME")
548{
549}
550
551bool darwin_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out)
552{
553 ARGUSED(out);
554 int fd = get_fd();
555 IONVMeSMARTInterface **ifp = devices[fd].smartIfNVMe;
556 IONVMeSMARTInterface *smartIfNVMe ;
557 IOReturn err = 0;
558 unsigned int page = in.cdw10 & 0xff;
559
560 if (! ifp)
ff28b140 561 return false;
f9e10201
JD
562 smartIfNVMe = *ifp;
563 // currently only GetIdentifyData and SMARTReadData are supported
564 switch (in.opcode) {
565 case smartmontools::nvme_admin_identify:
566 err = smartIfNVMe->GetIdentifyData(ifp, (struct nvme_id_ctrl *) in.buffer, in.nsid); // FIXME
567 break;
568 case smartmontools::nvme_admin_get_log_page:
569 if(page == 0x02)
570 err = smartIfNVMe->SMARTReadData(ifp, (struct nvme_smart_log *) in.buffer);
571 else /* GetLogPage() is not working yet */
572 return set_err(ENOSYS, "NVMe admin command:0x%02x/page:0x%02x is not supported",
573 in.opcode, page);
574 break;
575 default:
576 return set_err(ENOSYS, "NVMe admin command 0x%02x is not supported", in.opcode);
577 }
578 return true;
579}
d2e702cf
GI
580//////////////////////////////////////////////////////////////////////
581
a86ec89e
GI
582std::string darwin_smart_interface::get_os_version_str()
583{
584 // now we are just getting darwin runtime version, to get OSX version more things needs to be done, see
585 // http://stackoverflow.com/questions/11072804/how-do-i-determine-the-os-version-at-runtime-in-os-x-or-ios-without-using-gesta
586 struct utsname osname;
587 uname(&osname);
588 return strprintf("%s %s %s", osname.sysname, osname.release, osname.machine);
589}
590
d2e702cf
GI
591std::string darwin_smart_interface::get_app_examples(const char * appname)
592{
593 if (!strcmp(appname, "smartctl"))
594 return smartctl_examples;
595 return ""; // ... so don't print again.
832b75ed
GG
596}
597
d2e702cf
GI
598ata_device * darwin_smart_interface::get_ata_device(const char * name, const char * type)
599{
600 return new darwin_ata_device(this, name, type);
601}
602
603scsi_device * darwin_smart_interface::get_scsi_device(const char *, const char *)
604{
605 return 0; // scsi devices are not supported [yet]
606}
607
f9e10201
JD
608nvme_device * darwin_smart_interface::get_nvme_device(const char * name, const char * type,
609 unsigned nsid)
610{
611 return new darwin_nvme_device(this, name, type, nsid);
612}
d2e702cf
GI
613
614smart_device * darwin_smart_interface::autodetect_smart_device(const char * name)
f9e10201
JD
615{ // TODO - refactor as a function
616 // Acceptable device names are:
617 // /dev/disk*
618 // /dev/rdisk*
619 // disk*
620 // IOService:*
621 // IODeviceTree:*
622 const char *devname = NULL;
623 io_object_t disk;
624
625 if (strncmp (name, "/dev/rdisk", 10) == 0)
626 devname = name + 6;
627 else if (strncmp (name, "/dev/disk", 9) == 0)
628 devname = name + 5;
629 else if (strncmp (name, "disk", 4) == 0)
630 // allow user to just say 'disk0'
631 devname = name;
632 // Find the device. This part should be the same for the NVMe and ATA
633 if (devname) {
634 CFMutableDictionaryRef matcher;
635 matcher = IOBSDNameMatching (kIOMasterPortDefault, 0, devname);
636 disk = IOServiceGetMatchingService (kIOMasterPortDefault, matcher);
637 }
638 else {
639 disk = IORegistryEntryFromPath (kIOMasterPortDefault, name);
640 }
641 if (! disk) {
642 return 0;
643 }
644 io_registry_entry_t tmpdisk=disk;
645
646
647 while (! is_smart_capable (tmpdisk, "ATA"))
648 {
649 IOReturn err;
650 io_object_t prevdisk = tmpdisk;
651
652 // Find this device's parent and try again.
653 err = IORegistryEntryGetParentEntry (tmpdisk, kIOServicePlane, &tmpdisk);
654 if (err != kIOReturnSuccess || ! tmpdisk)
655 {
656 IOObjectRelease (prevdisk);
657 break;
658 }
659 }
660 if (tmpdisk)
661 return new darwin_ata_device(this, name, "");
662 tmpdisk=disk;
663 while (! is_smart_capable (tmpdisk, "NVME"))
664 {
665 IOReturn err;
666 io_object_t prevdisk = tmpdisk;
667
668 // Find this device's parent and try again.
669 err = IORegistryEntryGetParentEntry (tmpdisk, kIOServicePlane, &tmpdisk);
670 if (err != kIOReturnSuccess || ! tmpdisk)
671 {
672 IOObjectRelease (prevdisk);
673 break;
674 }
675 }
676 if (tmpdisk)
677 return new darwin_nvme_device(this, name, "", 0);
678
679 // try ATA as a last option, for compatibility
d2e702cf
GI
680 return new darwin_ata_device(this, name, "");
681}
682
683static void free_devnames(char * * devnames, int numdevs)
684{
685 for (int i = 0; i < numdevs; i++)
686 free(devnames[i]);
687 free(devnames);
688}
689
690bool darwin_smart_interface::scan_smart_devices(smart_device_list & devlist,
691 const char * type, const char * pattern /*= 0*/)
692{
693 if (pattern) {
694 set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");
695 return false;
696 }
697
698 // Make namelists
699 char * * atanames = 0; int numata = 0;
700 if (!type || !strcmp(type, "ata")) {
701 numata = make_device_names(&atanames, "ATA");
702 if (numata < 0) {
703 set_err(ENOMEM);
704 return false;
705 }
706 }
f9e10201
JD
707 char * * nvmenames = 0; int numnvme = 0;
708 if (
709#ifdef WITH_NVME_DEVICESCAN // TODO: Remove when NVMe support is no longer EXPERIMENTAL
710 !type ||
711#else
712 type &&
713#endif
714 !strcmp(type, "nvme")) {
715 numnvme = make_device_names(&nvmenames, "NVME");
716 if (numnvme < 0) {
717 set_err(ENOMEM);
718 return false;
719 }
720 }
d2e702cf
GI
721
722 // Add to devlist
723 int i;
724 if (!type)
725 type="";
726 for (i = 0; i < numata; i++) {
727 ata_device * atadev = get_ata_device(atanames[i], type);
728 if (atadev)
729 devlist.push_back(atadev);
730 }
731 free_devnames(atanames, numata);
f9e10201
JD
732
733 for (i = 0; i < numnvme; i++) {
734 nvme_device * nvmedev = get_nvme_device(nvmenames[i], type, 0); // default nsid
735 if (nvmedev)
736 devlist.push_back(nvmedev);
737 }
738 free_devnames(nvmenames, numnvme);
739
d2e702cf
GI
740 return true;
741}
742
743} // namespace
744
745
746/////////////////////////////////////////////////////////////////////////////
747/// Initialize platform interface and register with smi()
748
749void smart_interface::init()
750{
751 static os::darwin_smart_interface the_interface;
752 smart_interface::set(&the_interface);
832b75ed 753}