4 * Home page of code is: http://www.smartmontools.org
6 * Copyright (C) 2003-8 Sergey Svishchev
7 * Copyright (C) 2016 Kimihiro Nonaka
9 * SPDX-License-Identifier: GPL-2.0-or-later
17 #include "os_netbsd.h"
19 #include <sys/drvctlio.h>
20 #include <sys/utsname.h>
25 // based on "sys/dev/ic/nvmeio.h" from NetBSD kernel sources
26 #include "netbsd_nvme_ioctl.h" // NVME_PASSTHROUGH_CMD, nvme_completion_is_error
28 const char * os_netbsd_cpp_cvsid
= "$Id: os_netbsd.cpp 4780 2018-09-16 15:03:22Z chrfranke $"
31 #define ARGUSED(x) ((void)(x))
33 /////////////////////////////////////////////////////////////////////////////
35 namespace os_netbsd
{ // No need to publish anything, name provided for Doxygen
37 static const char *net_dev_prefix
= "/dev/";
38 static const char *net_dev_raw_prefix
= "/dev/r";
39 static const char *net_dev_ata_disk
= "wd";
40 static const char *net_dev_scsi_disk
= "sd";
41 static const char *net_dev_scsi_tape
= "enrst";
42 static const char *net_dev_nvme_ctrl
= "nvme";
44 /////////////////////////////////////////////////////////////////////////////
45 /// Implement shared open/close routines with old functions.
47 class netbsd_smart_device
48 : virtual public /*implements*/ smart_device
51 explicit netbsd_smart_device()
52 : smart_device(never_called
),
55 virtual ~netbsd_smart_device() throw();
57 virtual bool is_open() const;
64 /// Return filedesc for derived classes.
72 int m_fd
; ///< filedesc, -1 if not open.
75 netbsd_smart_device::~netbsd_smart_device() throw()
78 os_netbsd::netbsd_smart_device::close();
81 bool netbsd_smart_device::is_open() const
87 bool netbsd_smart_device::open()
89 const char *dev
= get_dev_name();
93 fd
= ::open(dev
,O_RDWR
|O_NONBLOCK
);
94 if (fd
< 0 && errno
== EROFS
)
95 fd
= ::open(dev
,O_RDONLY
|O_NONBLOCK
);
100 } else if (is_ata() || is_nvme()) {
101 if ((fd
= ::open(dev
,O_RDWR
|O_NONBLOCK
))<0) {
112 bool netbsd_smart_device::close()
115 // close device, if open
117 failed
=::close(get_fd());
121 if(failed
) return false;
125 /////////////////////////////////////////////////////////////////////////////
126 /// Implement standard ATA support
128 class netbsd_ata_device
129 : public /*implements*/ ata_device
,
130 public /*extends*/ netbsd_smart_device
133 netbsd_ata_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
134 virtual bool ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
);
137 virtual int do_cmd(struct atareq
* request
, bool is_48bit_cmd
);
140 netbsd_ata_device::netbsd_ata_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
)
141 : smart_device(intf
, dev_name
, "ata", req_type
),
142 netbsd_smart_device()
146 int netbsd_ata_device::do_cmd( struct atareq
* request
, bool is_48bit_cmd
)
148 int fd
= get_fd(), ret
;
149 ARGUSED(is_48bit_cmd
); // no support for 48 bit commands in the ATAIOCCOMMAND
150 ret
= ioctl(fd
, ATAIOCCOMMAND
, request
);
151 if (ret
) set_err(errno
);
155 bool netbsd_ata_device::ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
)
157 bool ata_48bit
= false; // no ata_48bit_support via ATAIOCCOMMAND
159 if (!ata_cmd_is_ok(in
,
160 true, // data_out_support
161 true, // multi_sector_support
164 set_err(ENOSYS
, "48-bit ATA commands not implemented");
169 memset(&req
, 0, sizeof(req
));
172 req
.command
= in
.in_regs
.command
;
173 req
.features
= in
.in_regs
.features
;
174 req
.sec_count
= in
.in_regs
.sector_count
;
175 req
.sec_num
= in
.in_regs
.lba_low
;
176 req
.head
= in
.in_regs
.device
;
177 req
.cylinder
= in
.in_regs
.lba_mid
| (in
.in_regs
.lba_high
<< 8);
179 switch (in
.direction
) {
180 case ata_cmd_in::no_data
:
181 req
.flags
= ATACMD_READREG
;
183 case ata_cmd_in::data_in
:
184 req
.flags
= ATACMD_READ
| ATACMD_READREG
;
185 req
.databuf
= (char *)in
.buffer
;
186 req
.datalen
= in
.size
;
188 case ata_cmd_in::data_out
:
189 req
.flags
= ATACMD_WRITE
| ATACMD_READREG
;
190 req
.databuf
= (char *)in
.buffer
;
191 req
.datalen
= in
.size
;
194 return set_err(ENOSYS
);
199 if (do_cmd(&req
, in
.in_regs
.is_48bit_cmd()))
201 if (req
.retsts
!= ATACMD_OK
)
202 return set_err(EIO
, "request failed, error code 0x%02x", req
.retsts
);
204 out
.out_regs
.error
= req
.error
;
205 out
.out_regs
.sector_count
= req
.sec_count
;
206 out
.out_regs
.lba_low
= req
.sec_num
;
207 out
.out_regs
.device
= req
.head
;
208 out
.out_regs
.lba_mid
= req
.cylinder
;
209 out
.out_regs
.lba_high
= req
.cylinder
>> 8;
210 out
.out_regs
.status
= req
.command
;
211 /* Undo byte-swapping for IDENTIFY */
212 if (in
.in_regs
.command
== ATA_IDENTIFY_DEVICE
&& isbigendian()) {
213 for (int i
= 0; i
< 256; i
+=2)
214 swap2 ((char *)req
.databuf
+ i
);
219 /////////////////////////////////////////////////////////////////////////////
222 class netbsd_nvme_device
223 : public /*implements*/ nvme_device
,
224 public /*extends*/ netbsd_smart_device
227 netbsd_nvme_device(smart_interface
* intf
, const char * dev_name
,
228 const char * req_type
, unsigned nsid
);
232 virtual bool nvme_pass_through(const nvme_cmd_in
& in
, nvme_cmd_out
& out
);
235 netbsd_nvme_device::netbsd_nvme_device(smart_interface
* intf
, const char * dev_name
,
236 const char * req_type
, unsigned nsid
)
237 : smart_device(intf
, dev_name
, "nvme", req_type
),
239 netbsd_smart_device()
243 bool netbsd_nvme_device::open()
245 const char *dev
= get_dev_name();
246 if (strncmp(dev
, NVME_PREFIX
, strlen(NVME_PREFIX
))) {
247 set_err(EINVAL
, "NVMe controller controller/namespace ids must begin with '%s'",
252 int nsid
= -1, ctrlid
= -1;
255 if(sscanf(dev
, NVME_PREFIX
"%d%c", &ctrlid
, &tmp
) == 1)
258 set_err(EINVAL
, "Invalid NVMe controller number");
261 nsid
= 0xFFFFFFFF; // broadcast id
263 else if (sscanf(dev
, NVME_PREFIX
"%d" NVME_NS_PREFIX
"%d%c",
264 &ctrlid
, &nsid
, &tmp
) == 2)
266 if(ctrlid
< 0 || nsid
<= 0) {
267 set_err(EINVAL
, "Invalid NVMe controller/namespace number");
272 set_err(EINVAL
, "Invalid NVMe controller/namespace syntax");
276 // we should always open controller, not namespace device
278 snprintf(full_path
, sizeof(full_path
), NVME_PREFIX
"%d", ctrlid
);
281 if ((fd
= ::open(full_path
, O_RDWR
))<0) {
294 bool netbsd_nvme_device::nvme_pass_through(const nvme_cmd_in
& in
, nvme_cmd_out
& out
)
296 struct nvme_pt_command pt
;
297 memset(&pt
, 0, sizeof(pt
));
299 pt
.cmd
.opcode
= in
.opcode
;
300 pt
.cmd
.nsid
= in
.nsid
;
303 pt
.cmd
.cdw10
= in
.cdw10
;
304 pt
.cmd
.cdw11
= in
.cdw11
;
305 pt
.cmd
.cdw12
= in
.cdw12
;
306 pt
.cmd
.cdw13
= in
.cdw13
;
307 pt
.cmd
.cdw14
= in
.cdw14
;
308 pt
.cmd
.cdw15
= in
.cdw15
;
309 pt
.is_read
= 1; // should we use in.direction()?
311 int status
= ioctl(get_fd(), NVME_PASSTHROUGH_CMD
, &pt
);
314 return set_err(errno
, "NVME_PASSTHROUGH_CMD: %s", strerror(errno
));
316 out
.result
=pt
.cpl
.cdw0
; // Command specific result (DW0)
318 if (nvme_completion_is_error(&pt
.cpl
))
319 return set_nvme_err(out
, nvme_completion_is_error(&pt
.cpl
));
324 /////////////////////////////////////////////////////////////////////////////
325 /// Standard SCSI support
327 class netbsd_scsi_device
328 : public /*implements*/ scsi_device
,
329 public /*extends*/ netbsd_smart_device
332 netbsd_scsi_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
, bool scanning
= false);
334 virtual smart_device
* autodetect_open();
336 virtual bool scsi_pass_through(scsi_cmnd_io
* iop
);
339 bool m_scanning
; ///< true if created within scan_smart_devices
342 netbsd_scsi_device::netbsd_scsi_device(smart_interface
* intf
,
343 const char * dev_name
, const char * req_type
, bool scanning
/* = false */)
344 : smart_device(intf
, dev_name
, "scsi", req_type
),
345 netbsd_smart_device(),
350 bool netbsd_scsi_device::scsi_pass_through(scsi_cmnd_io
* iop
)
355 if (scsi_debugmode
) {
357 const unsigned char * ucp
= iop
->cmnd
;
360 np
= scsi_get_opcode_name(ucp
[0]);
361 pout(" [%s: ", np
? np
: "<unknown opcode>");
362 for (k
= 0; k
< iop
->cmnd_len
; ++k
)
363 pout("%02x ", ucp
[k
]);
364 if ((scsi_debugmode
> 1) &&
365 (DXFER_TO_DEVICE
== iop
->dxfer_dir
) && (iop
->dxferp
)) {
366 int trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
368 pout("]\n Outgoing data, len=%d%s:\n", (int)iop
->dxfer_len
,
369 (trunc
? " [only first 256 bytes shown]" : ""));
370 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
) , 1);
376 memset(&sc
, 0, sizeof(sc
));
377 memcpy(sc
.cmd
, iop
->cmnd
, iop
->cmnd_len
);
378 sc
.cmdlen
= iop
->cmnd_len
;
379 sc
.databuf
= (char *)iop
->dxferp
;
380 sc
.datalen
= iop
->dxfer_len
;
381 sc
.senselen
= iop
->max_sense_len
;
382 sc
.timeout
= iop
->timeout
== 0 ? 60000 : (1000 * iop
->timeout
);
384 (iop
->dxfer_dir
== DXFER_NONE
? SCCMD_READ
:
385 (iop
->dxfer_dir
== DXFER_FROM_DEVICE
? SCCMD_READ
: SCCMD_WRITE
));
387 if (ioctl(fd
, SCIOCCOMMAND
, &sc
) < 0) {
388 if (scsi_debugmode
) {
389 pout(" error sending SCSI ccb\n");
393 iop
->resid
= sc
.datalen
- sc
.datalen_used
;
394 iop
->scsi_status
= sc
.status
;
396 memcpy(iop
->sensep
, sc
.sense
, sc
.senselen_used
);
397 iop
->resp_sense_len
= sc
.senselen_used
;
399 if (scsi_debugmode
) {
403 trunc
= (iop
->dxfer_len
> 256) ? 1 : 0;
405 pout(" Incoming data, len=%d%s:\n", (int) iop
->dxfer_len
,
406 (trunc
? " [only first 256 bytes shown]" : ""));
407 dStrHex(iop
->dxferp
, (trunc
? 256 : iop
->dxfer_len
), 1);
413 return set_err(ETIMEDOUT
);
415 return set_err(EBUSY
);
423 /////////////////////////////////////////////////////////////////////////////
424 ///// SCSI open with autodetection support
426 smart_device
* netbsd_scsi_device::autodetect_open()
432 // No Autodetection if device type was specified by user
433 bool sat_only
= false;
434 if (*get_req_type()) {
435 // Detect SAT if device object was created by scan_smart_devices().
436 if (!(m_scanning
&& !strcmp(get_req_type(), "sat")))
441 // The code below is based on smartd.cpp:SCSIFilterKnown()
444 unsigned char req_buff
[64] = {0, };
446 if (scsiStdInquiry(this, req_buff
, req_len
)) {
447 // Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices
448 // watch this spot ... other devices could lock up here
450 if (scsiStdInquiry(this, req_buff
, req_len
)) {
451 // device doesn't like INQUIRY commands
453 set_err(EIO
, "INQUIRY failed");
458 int avail_len
= req_buff
[4] + 5;
459 int len
= (avail_len
< req_len
? avail_len
: req_len
);
463 set_err(EIO
, "INQUIRY too short for SAT");
468 // Use INQUIRY to detect type
470 // SAT or USB, skip MFI controllers because of bugs
472 smart_device
* newdev
= smi()->autodetect_sat_device(this, req_buff
, len
);
474 // NOTE: 'this' is now owned by '*newdev'
479 // Nothing special found
483 set_err(EIO
, "Not a SAT device");
488 /////////////////////////////////////////////////////////////////////////////
489 /// Implement platform interface with old functions.
491 class netbsd_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 nvme_device
* get_nvme_device(const char * name
, const char * type
,
510 virtual smart_device
* autodetect_smart_device(const char * name
);
512 virtual smart_device
* get_custom_smart_device(const char * name
, const char * type
);
514 virtual std::string
get_valid_custom_dev_types_str();
517 int get_dev_names(char ***, const char *);
519 bool get_nvme_devlist(smart_device_list
& devlist
, const char * type
);
523 //////////////////////////////////////////////////////////////////////
525 std::string
netbsd_smart_interface::get_os_version_str()
527 struct utsname osname
;
529 return strprintf("%s %s %s", osname
.sysname
, osname
.release
, osname
.machine
);
532 std::string
netbsd_smart_interface::get_app_examples(const char * appname
)
534 if (!strcmp(appname
, "smartctl")) {
537 p
= 'a' + getrawpartition();
539 "=================================================== SMARTCTL EXAMPLES =====\n\n"
540 " smartctl -a /dev/wd0%c (Prints all SMART information)\n\n"
541 " smartctl --smart=on --offlineauto=on --saveauto=on /dev/wd0%c\n"
542 " (Enables SMART on first disk)\n\n"
543 " smartctl -t long /dev/wd0%c (Executes extended disk self-test)\n\n"
544 " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/wd0%c\n"
545 " (Prints Self-Test & Attribute errors)\n"
551 ata_device
* netbsd_smart_interface::get_ata_device(const char * name
, const char * type
)
553 return new netbsd_ata_device(this, name
, type
);
556 scsi_device
* netbsd_smart_interface::get_scsi_device(const char * name
, const char * type
)
558 return new netbsd_scsi_device(this, name
, type
);
561 nvme_device
* netbsd_smart_interface::get_nvme_device(const char * name
, const char * type
, unsigned nsid
)
563 return new netbsd_nvme_device(this, name
, type
, nsid
);
566 int netbsd_smart_interface::get_dev_names(char ***names
, const char *prefix
)
568 char *disknames
, *p
, **mp
;
575 sysctl_mib
[0] = CTL_HW
;
576 sysctl_mib
[1] = HW_DISKNAMES
;
577 if (-1 == sysctl(sysctl_mib
, 2, NULL
, &sysctl_len
, NULL
, 0)) {
578 pout("Failed to get value of sysctl `hw.disknames'\n");
581 if (!(disknames
= (char *)malloc(sysctl_len
))) {
582 pout("Out of memory constructing scan device list\n");
585 if (-1 == sysctl(sysctl_mib
, 2, disknames
, &sysctl_len
, NULL
, 0)) {
586 pout("Failed to get value of sysctl `hw.disknames'\n");
589 if (!(mp
= (char **) calloc(strlen(disknames
) / 2, sizeof(char *)))) {
590 pout("Out of memory constructing scan device list\n");
593 for (p
= strtok(disknames
, " "); p
; p
= strtok(NULL
, " ")) {
594 if (strncmp(p
, prefix
, strlen(prefix
))) {
597 mp
[n
] = (char *)malloc(strlen(net_dev_raw_prefix
) + strlen(p
) + 2);
599 pout("Out of memory constructing scan device list\n");
602 sprintf(mp
[n
], "%s%s%c", net_dev_raw_prefix
, p
, 'a' + getrawpartition());
606 char ** tmp
= (char **)realloc(mp
, n
* (sizeof(char *)));
608 pout("Out of memory constructing scan device list\n");
618 bool netbsd_smart_interface::get_nvme_devlist(smart_device_list
& devlist
,
621 char ctrlpath
[64], nspath
[64];
623 struct devlistargs laa
;
624 nvme_device
* nvmedev
;
626 int drvfd
= ::open(DRVCTLDEV
, O_RDONLY
, 0);
632 for (int ctrl
= 0;; ctrl
++) {
633 snprintf(ctrlpath
, sizeof(ctrlpath
), NVME_PREFIX
"%d", ctrl
);
634 if (stat(ctrlpath
, &sb
) == -1 || !S_ISCHR(sb
.st_mode
))
637 snprintf(laa
.l_devname
, sizeof(laa
.l_devname
), "%s%d", net_dev_nvme_ctrl
,
639 laa
.l_childname
= NULL
;
641 if (ioctl(drvfd
, DRVLISTDEV
, &laa
) == -1) {
647 nvmedev
= get_nvme_device(ctrlpath
, type
, 0);
649 devlist
.push_back(nvmedev
);
652 for (int nsid
= 1; n
< laa
.l_children
; nsid
++) {
653 snprintf(nspath
, sizeof(nspath
), NVME_PREFIX
"%d" NVME_NS_PREFIX
"%d",
655 if (stat(nspath
, &sb
) == -1 || !S_ISCHR(sb
.st_mode
))
657 int nsfd
= ::open(nspath
, O_RDONLY
, 0);
663 nvmedev
= get_nvme_device(nspath
, type
, nsid
);
665 devlist
.push_back(nvmedev
);
673 bool netbsd_smart_interface::scan_smart_devices(smart_device_list
& devlist
,
674 const char * type
, const char * pattern
/*= 0*/)
677 set_err(EINVAL
, "DEVICESCAN with pattern not implemented yet");
684 bool scan_ata
= !*type
|| !strcmp(type
, "ata");
685 bool scan_scsi
= !*type
|| !strcmp(type
, "scsi") || !strcmp(type
, "sat");
687 #ifdef WITH_NVME_DEVICESCAN // TODO: Remove when NVMe support is no longer EXPERIMENTAL
688 bool scan_nvme
= !*type
|| !strcmp(type
, "nvme");
690 bool scan_nvme
= !strcmp(type
, "nvme");
694 char * * atanames
= 0; int numata
= 0;
696 numata
= get_dev_names(&atanames
, net_dev_ata_disk
);
703 char * * scsinames
= 0; int numscsi
= 0;
704 char * * scsitapenames
= 0; int numscsitape
= 0;
706 numscsi
= get_dev_names(&scsinames
, net_dev_scsi_disk
);
711 numscsitape
= get_dev_names(&scsitapenames
, net_dev_scsi_tape
);
712 if (numscsitape
< 0) {
720 for (i
= 0; i
< numata
; i
++) {
721 ata_device
* atadev
= get_ata_device(atanames
[i
], type
);
723 devlist
.push_back(atadev
);
726 if(numata
) free(atanames
);
728 for (i
= 0; i
< numscsi
; i
++) {
729 scsi_device
* scsidev
= new netbsd_scsi_device(this, scsinames
[i
], type
, true /*scanning*/);
731 devlist
.push_back(scsidev
);
734 if(numscsi
) free(scsinames
);
736 for (i
= 0; i
< numscsitape
; i
++) {
737 scsi_device
* scsidev
= get_scsi_device(scsitapenames
[i
], type
);
739 devlist
.push_back(scsidev
);
740 free(scsitapenames
[i
]);
742 if(numscsitape
) free(scsitapenames
);
745 get_nvme_devlist(devlist
, type
);
750 smart_device
* netbsd_smart_interface::autodetect_smart_device(const char * name
)
752 const char * test_name
= name
;
754 // if dev_name null, or string length zero
758 // Dereference symlinks
761 if (!lstat(name
, &st
) && S_ISLNK(st
.st_mode
)) {
762 char * p
= realpath(name
, (char *)0);
766 test_name
= pathbuf
.c_str();
770 if (str_starts_with(test_name
, net_dev_raw_prefix
))
771 test_name
+= strlen(net_dev_raw_prefix
);
772 else if (str_starts_with(test_name
, net_dev_prefix
))
773 test_name
+= strlen(net_dev_prefix
);
775 return 0; // device is not starting with /dev/ or /dev/r*
777 if (!strncmp(net_dev_ata_disk
, test_name
, strlen(net_dev_ata_disk
)))
778 return get_ata_device(name
, "ata");
780 if (!strncmp(net_dev_scsi_disk
, test_name
, strlen(net_dev_scsi_disk
))) {
781 // XXX Try to detect possible USB->(S)ATA bridge
782 // XXX get USB vendor ID, product ID and version from sd(4)/umass(4).
783 // XXX check sat device via get_usb_dev_type_by_id().
784 // No USB bridge found, assume regular SCSI device
785 return get_scsi_device(name
, "scsi");
788 if (!strncmp(net_dev_scsi_tape
, test_name
, strlen(net_dev_scsi_tape
)))
789 return get_scsi_device(name
, "scsi");
791 if (!strncmp(net_dev_nvme_ctrl
, test_name
, strlen(net_dev_nvme_ctrl
)))
792 return get_nvme_device(name
, "nvme", 0 /* use default nsid */);
794 // device type unknown
798 smart_device
* netbsd_smart_interface::get_custom_smart_device(const char * name
, const char * type
)
805 std::string
netbsd_smart_interface::get_valid_custom_dev_types_str()
812 /////////////////////////////////////////////////////////////////////////////
813 /// Initialize platform interface and register with smi()
815 void smart_interface::init()
817 static os_netbsd::netbsd_smart_interface the_interface
;
818 smart_interface::set(&the_interface
);
821 /* vim: set ts=2 sw=2 et ff=unix : */