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