Ticket #728: netbsd-dev_interface-and-nvme.patch

File netbsd-dev_interface-and-nvme.patch, 35.5 KB (added by Christian Franke, 8 years ago)

Patch from Kimihiro Nonaka merged to SVN HEAD. Not tested yet.

  • Makefile.am

     
    174174        dev_legacy.cpp \
    175175        linux_nvme_ioctl.h \
    176176        freebsd_nvme_ioctl.h \
     177        netbsd_nvme_ioctl.h \
    177178        megaraid.h
    178179
    179180if OS_WIN32_MINGW
  • netbsd_nvme_ioctl.h

     
     1/*      $NetBSD: nvmereg.h,v 1.1 2016/05/01 10:21:02 nonaka Exp $       */
     2/*      $OpenBSD: nvmereg.h,v 1.10 2016/04/14 11:18:32 dlg Exp $ */
     3
     4/*
     5 * Copyright (c) 2014 David Gwynne <dlg@openbsd.org>
     6 *
     7 * Permission to use, copy, modify, and distribute this software for any
     8 * purpose with or without fee is hereby granted, provided that the above
     9 * copyright notice and this permission notice appear in all copies.
     10 *
     11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     18 */
     19
     20#include <sys/param.h>
     21
     22struct nvme_sge {
     23        uint8_t         id;
     24        uint8_t         _reserved[15];
     25} __packed __aligned(8);
     26
     27struct nvme_sqe {
     28        uint8_t         opcode;
     29        uint8_t         flags;
     30        uint16_t        cid;
     31
     32        uint32_t        nsid;
     33
     34        uint8_t         _reserved[8];
     35
     36        uint64_t        mptr;
     37
     38        union {
     39                uint64_t        prp[2];
     40                struct nvme_sge sge;
     41        } __packed      entry;
     42
     43        uint32_t        cdw10;
     44        uint32_t        cdw11;
     45        uint32_t        cdw12;
     46        uint32_t        cdw13;
     47        uint32_t        cdw14;
     48        uint32_t        cdw15;
     49} __packed __aligned(8);
     50
     51struct nvme_cqe {
     52        uint32_t        cdw0;
     53
     54        uint32_t        _reserved;
     55
     56        uint16_t        sqhd; /* SQ Head Pointer */
     57        uint16_t        sqid; /* SQ Identifier */
     58
     59        uint16_t        cid; /* Command Identifier */
     60        uint16_t        flags;
     61#define NVME_CQE_DNR            __BIT(15)
     62#define NVME_CQE_M              __BIT(14)
     63#define NVME_CQE_SCT(_f)        ((_f) & (0x07 << 8))
     64#define  NVME_CQE_SCT_GENERIC           (0x00 << 8)
     65#define  NVME_CQE_SCT_COMMAND           (0x01 << 8)
     66#define  NVME_CQE_SCT_MEDIAERR          (0x02 << 8)
     67#define  NVME_CQE_SCT_VENDOR            (0x07 << 8)
     68#define NVME_CQE_SC(_f)         ((_f) & (0x7f << 1))
     69#define  NVME_CQE_SC_SUCCESS            (0x00 << 1)
     70#define  NVME_CQE_SC_INVALID_OPCODE     (0x01 << 1)
     71#define  NVME_CQE_SC_INVALID_FIELD      (0x02 << 1)
     72#define  NVME_CQE_SC_CID_CONFLICT       (0x03 << 1)
     73#define  NVME_CQE_SC_DATA_XFER_ERR      (0x04 << 1)
     74#define  NVME_CQE_SC_ABRT_BY_NO_PWR     (0x05 << 1)
     75#define  NVME_CQE_SC_INTERNAL_DEV_ERR   (0x06 << 1)
     76#define  NVME_CQE_SC_CMD_ABRT_REQD      (0x07 << 1)
     77#define  NVME_CQE_SC_CMD_ABDR_SQ_DEL    (0x08 << 1)
     78#define  NVME_CQE_SC_CMD_ABDR_FUSE_ERR  (0x09 << 1)
     79#define  NVME_CQE_SC_CMD_ABDR_FUSE_MISS (0x0a << 1)
     80#define  NVME_CQE_SC_INVALID_NS         (0x0b << 1)
     81#define  NVME_CQE_SC_CMD_SEQ_ERR        (0x0c << 1)
     82#define  NVME_CQE_SC_INVALID_LAST_SGL   (0x0d << 1)
     83#define  NVME_CQE_SC_INVALID_NUM_SGL    (0x0e << 1)
     84#define  NVME_CQE_SC_DATA_SGL_LEN       (0x0f << 1)
     85#define  NVME_CQE_SC_MDATA_SGL_LEN      (0x10 << 1)
     86#define  NVME_CQE_SC_SGL_TYPE_INVALID   (0x11 << 1)
     87#define  NVME_CQE_SC_LBA_RANGE          (0x80 << 1)
     88#define  NVME_CQE_SC_CAP_EXCEEDED       (0x81 << 1)
     89#define  NVME_CQE_NS_NOT_RDY            (0x82 << 1)
     90#define  NVME_CQE_RSV_CONFLICT          (0x83 << 1)
     91#define NVME_CQE_PHASE          __BIT(0)
     92} __packed __aligned(8);
     93
     94/*-
     95 * Copyright (C) 2012-2013 Intel Corporation
     96 * All rights reserved.
     97 *
     98 * Redistribution and use in source and binary forms, with or without
     99 * modification, are permitted provided that the following conditions
     100 * are met:
     101 * 1. Redistributions of source code must retain the above copyright
     102 *    notice, this list of conditions and the following disclaimer.
     103 * 2. Redistributions in binary form must reproduce the above copyright
     104 *    notice, this list of conditions and the following disclaimer in the
     105 *    documentation and/or other materials provided with the distribution.
     106 *
     107 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     108 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     109 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     110 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     111 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     112 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     113 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     114 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     115 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     116 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     117 * SUCH DAMAGE.
     118 *
     119 * $FreeBSD: head/sys/dev/nvme/nvme.h 296617 2016-03-10 17:13:10Z mav $
     120 */
     121
     122#define NVME_PASSTHROUGH_CMD            _IOWR('n', 0, struct nvme_pt_command)
     123
     124#define nvme_completion_is_error(cpl)                                   \
     125        ((NVME_CQE_SC((cpl)->flags) != NVME_CQE_SC_SUCCESS)             \
     126            || (NVME_CQE_SCT((cpl)->flags) != NVME_CQE_SCT_GENERIC))
     127
     128struct nvme_pt_command {
     129
     130        /*
     131         * cmd is used to specify a passthrough command to a controller or
     132         *  namespace.
     133         *
     134         * The following fields from cmd may be specified by the caller:
     135         *      * opcode
     136         *      * nsid (namespace id) - for admin commands only
     137         *      * cdw10-cdw15
     138         *
     139         * Remaining fields must be set to 0 by the caller.
     140         */
     141        struct nvme_sqe         cmd;
     142
     143        /*
     144         * cpl returns completion status for the passthrough command
     145         *  specified by cmd.
     146         *
     147         * The following fields will be filled out by the driver, for
     148         *  consumption by the caller:
     149         *      * cdw0
     150         *      * flags (except for phase)
     151         *
     152         * Remaining fields will be set to 0 by the driver.
     153         */
     154        struct nvme_cqe         cpl;
     155
     156        /* buf is the data buffer associated with this passthrough command. */
     157        void                    *buf;
     158
     159        /*
     160         * len is the length of the data buffer associated with this
     161         *  passthrough command.
     162         */
     163        uint32_t                len;
     164
     165        /*
     166         * is_read = 1 if the passthrough command will read data into the
     167         *  supplied buffer from the controller.
     168         *
     169         * is_read = 0 if the passthrough command will write data from the
     170         *  supplied buffer to the controller.
     171         */
     172        uint32_t                is_read;
     173
     174        /*
     175         * timeout (unit: ms)
     176         *
     177         * 0: use default timeout value
     178         */
     179        uint32_t                timeout;
     180};
     181
     182#define NVME_PREFIX             "/dev/nvme"
     183#define NVME_NS_PREFIX          "ns"
  • os_netbsd.cpp

    Property changes on: netbsd_nvme_ioctl.h
    ___________________________________________________________________
    Added: svn:eol-style
    ## -0,0 +1 ##
    +native
    \ No newline at end of property
    Added: svn:keywords
    ## -0,0 +1 ##
    +Id
    \ No newline at end of property
     
    44 * Home page of code is: http://www.smartmontools.org
    55 *
    66 * Copyright (C) 2003-8 Sergey Svishchev <smartmontools-support@lists.sourceforge.net>
     7 * Copyright (C) 2016 Kimihiro Nonaka
    78 *
    89 * This program is free software; you can redistribute it and/or modify
    910 * it under the terms of the GNU General Public License as published by
     
    2324#include "utility.h"
    2425#include "os_netbsd.h"
    2526
     27#include <sys/utsname.h>
    2628#include <errno.h>
    2729#include <unistd.h>
    2830
     31// based on "sys/dev/ic/nvmeio.h" from NetBSD kernel sources
     32#include "netbsd_nvme_ioctl.h" // NVME_PASSTHROUGH_CMD, nvme_completion_is_error
     33
    2934const char * os_netbsd_cpp_cvsid = "$Id$"
    3035  OS_NETBSD_H_CVSID;
    3136
     
    3237enum warnings {
    3338  BAD_SMART, MAX_MSG
    3439};
    35 
     40 
    3641/* Utility function for printing warnings */
    3742void
    3843printwarning(int msgNo, const char *extra)
     
    5459  return;
    5560}
    5661
    57 static const char *net_dev_prefix = "/dev/r";
     62#define ARGUSED(x) ((void)(x))
     63
     64/////////////////////////////////////////////////////////////////////////////
     65
     66namespace os_netbsd { // No need to publish anything, name provided for Doxygen
     67
     68static const char *net_dev_prefix = "/dev/";
     69static const char *net_dev_raw_prefix = "/dev/r";
    5870static const char *net_dev_ata_disk = "wd";
    5971static const char *net_dev_scsi_disk = "sd";
    6072static const char *net_dev_scsi_tape = "enrst";
    6173
    62 /* Guess device type (ATA or SCSI) based on device name */
    63 int
    64 guess_device_type(const char *dev_name)
     74/////////////////////////////////////////////////////////////////////////////
     75/// Implement shared open/close routines with old functions.
     76
     77class netbsd_smart_device
     78: virtual public /*implements*/ smart_device
    6579{
    66   int len;
    67   int dev_prefix_len = strlen(net_dev_prefix);
     80public:
     81  explicit netbsd_smart_device()
     82    : smart_device(never_called),
     83      m_fd(-1) { }
    6884
    69   if (!dev_name || !(len = strlen(dev_name)))
    70     return CONTROLLER_UNKNOWN;
     85  virtual ~netbsd_smart_device() throw();
    7186
    72   if (!strncmp(net_dev_prefix, dev_name, dev_prefix_len)) {
    73     if (len <= dev_prefix_len)
    74       return CONTROLLER_UNKNOWN;
    75     else
    76       dev_name += dev_prefix_len;
    77   }
    78   if (!strncmp(net_dev_ata_disk, dev_name, strlen(net_dev_ata_disk)))
    79     return CONTROLLER_ATA;
     87  virtual bool is_open() const;
    8088
    81   if (!strncmp(net_dev_scsi_disk, dev_name, strlen(net_dev_scsi_disk)))
    82     return CONTROLLER_SCSI;
     89  virtual bool open();
    8390
    84   if (!strncmp(net_dev_scsi_tape, dev_name, strlen(net_dev_scsi_tape)))
    85     return CONTROLLER_SCSI;
     91  virtual bool close();
    8692
    87   return CONTROLLER_UNKNOWN;
     93protected:
     94  /// Return filedesc for derived classes.
     95  int get_fd() const
     96    { return m_fd; }
     97
     98  void set_fd(int fd)
     99    { m_fd = fd; }
     100
     101private:
     102  int m_fd; ///< filedesc, -1 if not open.
     103};
     104
     105netbsd_smart_device::~netbsd_smart_device() throw()
     106{
     107  if (m_fd >= 0)
     108    os_netbsd::netbsd_smart_device::close();
    88109}
    89110
    90 int
    91 get_dev_names(char ***names, const char *prefix)
     111bool netbsd_smart_device::is_open() const
    92112{
    93   char *disknames, *p, **mp;
    94   int n = 0;
    95   int sysctl_mib[2];
    96   size_t sysctl_len;
     113  return (m_fd >= 0);
     114}
    97115
    98   *names = NULL;
    99116
    100   sysctl_mib[0] = CTL_HW;
    101   sysctl_mib[1] = HW_DISKNAMES;
    102   if (-1 == sysctl(sysctl_mib, 2, NULL, &sysctl_len, NULL, 0)) {
    103     pout("Failed to get value of sysctl `hw.disknames'\n");
    104     return -1;
    105   }
    106   if (!(disknames = (char *)malloc(sysctl_len))) {
    107     pout("Out of memory constructing scan device list\n");
    108     return -1;
    109   }
    110   if (-1 == sysctl(sysctl_mib, 2, disknames, &sysctl_len, NULL, 0)) {
    111     pout("Failed to get value of sysctl `hw.disknames'\n");
    112     return -1;
    113   }
    114   if (!(mp = (char **) calloc(strlen(disknames) / 2, sizeof(char *)))) {
    115     pout("Out of memory constructing scan device list\n");
    116     return -1;
    117   }
    118   for (p = strtok(disknames, " "); p; p = strtok(NULL, " ")) {
    119     if (strncmp(p, prefix, strlen(prefix))) {
    120       continue;
     117bool netbsd_smart_device::open()
     118{
     119  const char *dev = get_dev_name();
     120  int fd;
     121
     122  if (is_scsi()) {
     123    fd = ::open(dev,O_RDWR|O_NONBLOCK);
     124    if (fd < 0 && errno == EROFS)
     125      fd = ::open(dev,O_RDONLY|O_NONBLOCK);
     126    if (fd < 0) {
     127      set_err(errno);
     128      return false;
    121129    }
    122     mp[n] = (char *)malloc(strlen(net_dev_prefix) + strlen(p) + 2);
    123     if (!mp[n]) {
    124       pout("Out of memory constructing scan device list\n");
    125       return -1;
     130  } else if (is_ata() || is_nvme()) {
     131    if ((fd = ::open(dev,O_RDWR|O_NONBLOCK))<0) {
     132      set_err(errno);
     133      return false;
    126134    }
    127     sprintf(mp[n], "%s%s%c", net_dev_prefix, p, 'a' + getrawpartition());
    128     n++;
    129   }
     135  } else
     136    return false;
    130137
    131   char ** tmp = (char **)realloc(mp, n * (sizeof(char *)));
    132   if (NULL == tmp) {
    133     pout("Out of memory constructing scan device list\n");
    134     free(mp);
    135     return -1;
    136   }
    137   else
    138     mp = tmp;
    139   *names = mp;
    140   return n;
     138  set_fd(fd);
     139  return true;
    141140}
    142141
    143 int
    144 make_device_names(char ***devlist, const char *name)
     142bool netbsd_smart_device::close()
    145143{
    146   if (!strcmp(name, "SCSI"))
    147     return get_dev_names(devlist, net_dev_scsi_disk);
    148   else if (!strcmp(name, "ATA"))
    149     return get_dev_names(devlist, net_dev_ata_disk);
    150   else
    151     return 0;
     144  int failed = 0;
     145  // close device, if open
     146  if (is_open())
     147    failed=::close(get_fd());
     148
     149  set_fd(-1);
     150
     151  if(failed) return false;
     152    else return true;
    152153}
    153154
    154 int
    155 deviceopen(const char *pathname, char *type)
     155/////////////////////////////////////////////////////////////////////////////
     156/// Implement standard ATA support
     157
     158class netbsd_ata_device
     159: public /*implements*/ ata_device,
     160  public /*extends*/ netbsd_smart_device
    156161{
    157   if (!strcmp(type, "SCSI")) {
    158     int fd = open(pathname, O_RDWR | O_NONBLOCK);
    159     if (fd < 0 && errno == EROFS)
    160       fd = open(pathname, O_RDONLY | O_NONBLOCK);
    161     return fd;
    162   } else if (!strcmp(type, "ATA"))
    163     return open(pathname, O_RDWR | O_NONBLOCK);
    164   else
    165     return -1;
     162public:
     163  netbsd_ata_device(smart_interface * intf, const char * dev_name, const char * req_type);
     164  virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
     165
     166protected:
     167  virtual int do_cmd(struct atareq* request, bool is_48bit_cmd);
     168};
     169
     170netbsd_ata_device::netbsd_ata_device(smart_interface * intf, const char * dev_name, const char * req_type)
     171: smart_device(intf, dev_name, "ata", req_type),
     172  netbsd_smart_device()
     173{
    166174}
    167175
    168 int
    169 deviceclose(int fd)
     176int netbsd_ata_device::do_cmd( struct atareq* request, bool is_48bit_cmd)
    170177{
    171   return close(fd);
     178  int fd = get_fd(), ret;
     179  ARGUSED(is_48bit_cmd); // no support for 48 bit commands in the ATAIOCCOMMAND
     180  ret = ioctl(fd, ATAIOCCOMMAND, request);
     181  if (ret) set_err(errno);
     182  return ret;
    172183}
    173184
    174 int
    175 ata_command_interface(int fd, smart_command_set command, int select, char *data)
     185bool netbsd_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
    176186{
     187  bool ata_48bit = false; // no ata_48bit_support via ATAIOCCOMMAND
     188
     189  if (!ata_cmd_is_ok(in,
     190    true,  // data_out_support
     191    true,  // multi_sector_support
     192    ata_48bit)
     193    ) {
     194      set_err(ENOSYS, "48-bit ATA commands not implemented");
     195      return false;
     196    }
     197
    177198  struct atareq req;
    178   unsigned char inbuf[DEV_BSIZE];
    179   int retval, copydata = 0;
     199  memset(&req, 0, sizeof(req));
    180200
    181   memset(&req, 0, sizeof(req));
    182201  req.timeout = 1000;
     202  req.command = in.in_regs.command;
     203  req.features = in.in_regs.features;
     204  req.sec_count = in.in_regs.sector_count;
     205  req.sec_num = in.in_regs.lba_low;
     206  req.head = in.in_regs.device;
     207  req.cylinder = le16toh(in.in_regs.lba_mid | (in.in_regs.lba_high << 8));
    183208
    184   memset(&inbuf, 0, sizeof(inbuf));
    185 
    186   switch (command) {
    187   case READ_VALUES:
    188     req.flags = ATACMD_READ;
    189     req.features = WDSM_RD_DATA;
    190     req.command = WDCC_SMART;
    191     req.databuf = (char *)inbuf;
    192     req.datalen = sizeof(inbuf);
    193     req.cylinder = WDSMART_CYL;
    194     copydata = 1;
    195     break;
    196   case READ_THRESHOLDS:
    197     req.flags = ATACMD_READ;
    198     req.features = WDSM_RD_THRESHOLDS;
    199     req.command = WDCC_SMART;
    200     req.databuf = (char *)inbuf;
    201     req.datalen = sizeof(inbuf);
    202     req.cylinder = WDSMART_CYL;
    203     copydata = 1;
    204     break;
    205   case READ_LOG:
    206     req.flags = ATACMD_READ;
    207     req.features = ATA_SMART_READ_LOG_SECTOR;   /* XXX missing from wdcreg.h */
    208     req.command = WDCC_SMART;
    209     req.databuf = (char *)inbuf;
    210     req.datalen = sizeof(inbuf);
    211     req.cylinder = WDSMART_CYL;
    212     req.sec_num = select;
    213     req.sec_count = 1;
    214     copydata = 1;
    215     break;
    216   case WRITE_LOG:
    217     memcpy(inbuf, data, 512);
    218     req.flags = ATACMD_WRITE;
    219     req.features = ATA_SMART_WRITE_LOG_SECTOR;  /* XXX missing from wdcreg.h */
    220     req.command = WDCC_SMART;
    221     req.databuf = (char *)inbuf;
    222     req.datalen = sizeof(inbuf);
    223     req.cylinder = WDSMART_CYL;
    224     req.sec_num = select;
    225     req.sec_count = 1;
    226     break;
    227   case IDENTIFY:
    228     req.flags = ATACMD_READ;
    229     req.command = WDCC_IDENTIFY;
    230     req.databuf = (char *)inbuf;
    231     req.datalen = sizeof(inbuf);
    232     copydata = 1;
    233     break;
    234   case PIDENTIFY:
    235     req.flags = ATACMD_READ;
    236     req.command = ATAPI_IDENTIFY_DEVICE;
    237     req.databuf = (char *)inbuf;
    238     req.datalen = sizeof(inbuf);
    239     copydata = 1;
    240     break;
    241   case ENABLE:
    242     req.flags = ATACMD_READREG;
    243     req.features = WDSM_ENABLE_OPS;
    244     req.command = WDCC_SMART;
    245     req.cylinder = WDSMART_CYL;
    246     break;
    247   case DISABLE:
    248     req.flags = ATACMD_READREG;
    249     req.features = WDSM_DISABLE_OPS;
    250     req.command = WDCC_SMART;
    251     req.cylinder = WDSMART_CYL;
    252     break;
    253   case AUTO_OFFLINE:
    254     /* NOTE: According to ATAPI 4 and UP, this command is obsolete */
    255     req.flags = ATACMD_READREG;
    256     req.features = ATA_SMART_AUTO_OFFLINE;      /* XXX missing from wdcreg.h */
    257     req.command = WDCC_SMART;
    258     req.cylinder = WDSMART_CYL;
    259     req.sec_count = select;
    260     break;
    261   case AUTOSAVE:
    262     req.flags = ATACMD_READREG;
    263     req.features = ATA_SMART_AUTOSAVE;  /* XXX missing from wdcreg.h */
    264     req.command = WDCC_SMART;
    265     req.cylinder = WDSMART_CYL;
    266     req.sec_count = select;
    267     break;
    268   case IMMEDIATE_OFFLINE:
    269     /* NOTE: According to ATAPI 4 and UP, this command is obsolete */
    270     req.flags = ATACMD_READREG;
    271     req.features = ATA_SMART_IMMEDIATE_OFFLINE; /* XXX missing from wdcreg.h */
    272     req.command = WDCC_SMART;
    273     req.cylinder = WDSMART_CYL;
    274     req.sec_num = select;
    275     req.sec_count = 1;
    276     break;
    277   case STATUS:          /* should return 0 if SMART is enabled at all */
    278   case STATUS_CHECK:    /* should return 0 if disk's health is ok */
    279     req.flags = ATACMD_READREG;
    280     req.features = WDSM_STATUS;
    281     req.command = WDCC_SMART;
    282     req.cylinder = WDSMART_CYL;
    283     break;
    284   case CHECK_POWER_MODE:
    285     req.flags = ATACMD_READREG;
    286     req.command = WDCC_CHECK_PWR;
    287     break;
    288   default:
    289     pout("Unrecognized command %d in ata_command_interface()\n", command);
    290     errno = ENOSYS;
    291     return -1;
     209  switch (in.direction) {
     210    case ata_cmd_in::no_data:
     211      req.flags = ATACMD_READREG;
     212      break;
     213    case ata_cmd_in::data_in:
     214      req.flags = ATACMD_READ | ATACMD_READREG;
     215      req.databuf = (char *)in.buffer;
     216      req.datalen = in.size;
     217      break;
     218    case ata_cmd_in::data_out:
     219      req.flags = ATACMD_WRITE | ATACMD_READREG;
     220      req.databuf = (char *)in.buffer;
     221      req.datalen = in.size;
     222      break;
     223    default:
     224      return set_err(ENOSYS);
    292225  }
    293226
    294   if (command == STATUS_CHECK || command == AUTOSAVE || command == AUTO_OFFLINE) {
    295     char buf[512];
     227  clear_err();
     228  errno = 0;
     229  if (do_cmd(&req, in.in_regs.is_48bit_cmd()))
     230      return false;
     231  if (req.retsts != ATACMD_OK)
     232      return set_err(EIO, "request failed, error code 0x%02x", req.retsts);
    296233
    297     unsigned const short normal = WDSMART_CYL, failed = 0x2cf4;
     234  out.out_regs.error = req.error;
     235  out.out_regs.sector_count = req.sec_count;
     236  out.out_regs.lba_low = req.sec_num;
     237  out.out_regs.device = req.head;
     238  out.out_regs.lba_mid = le16toh(req.cylinder);
     239  out.out_regs.lba_high = le16toh(req.cylinder) >> 8;
     240  out.out_regs.status = req.command;
    298241
    299     if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) {
    300       perror("Failed command");
    301       return -1;
    302     }
    303     if (req.retsts != ATACMD_OK) {
    304       return -1;
    305     }
    306     /* Cyl low and Cyl high unchanged means "Good SMART status" */
    307     if (req.cylinder == normal)
    308       return 0;
     242  // Command specific processing
     243  if (in.in_regs.command == ATA_SMART_CMD
     244       && in.in_regs.features == ATA_SMART_STATUS
     245       && in.out_needed.lba_high)
     246  {
     247    unsigned const char normal_lo=0x4f, normal_hi=0xc2;
     248    unsigned const char failed_lo=0xf4, failed_hi=0x2c;
    309249
    310     /* These values mean "Bad SMART status" */
    311     if (req.cylinder == failed)
    312       return 1;
     250    // Cyl low and Cyl high unchanged means "Good SMART status"
     251    if (!(out.out_regs.lba_mid==normal_lo && out.out_regs.lba_high==normal_hi)
     252    // These values mean "Bad SMART status"
     253        && !(out.out_regs.lba_mid==failed_lo && out.out_regs.lba_high==failed_hi))
    313254
    314     /* We haven't gotten output that makes sense;
    315      * print out some debugging info */
    316     snprintf(buf, sizeof(buf),
    317       "CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n",
    318       (int) req.command, (int) req.features, (int) req.sec_count, (int) req.sec_num,
    319       (int) (le16toh(req.cylinder) & 0xff), (int) ((le16toh(req.cylinder) >> 8) & 0xff),
    320       (int) req.error);
    321     printwarning(BAD_SMART, buf);
    322     return 0;
     255    {
     256      // We haven't gotten output that makes sense; print out some debugging info
     257      char buf[512];
     258      snprintf(buf, sizeof(buf),
     259        "CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n",
     260        (int)req.command,
     261        (int)req.features,
     262        (int)req.sec_count,
     263        (int)req.sec_num,
     264        (int)(le16toh(req.cylinder) & 0xff),
     265        (int)((le16toh(req.cylinder) >> 8) & 0xff),
     266        (int)req.error);
     267      printwarning(BAD_SMART,buf);
     268      out.out_regs.lba_high = failed_hi;
     269      out.out_regs.lba_mid = failed_lo;
     270    }
    323271  }
    324272
    325   retval = ioctl(fd, ATAIOCCOMMAND, &req);
    326   if (retval < 0) {
    327     perror("Failed command");
    328     return -1;
     273  return true;
     274}
     275
     276/////////////////////////////////////////////////////////////////////////////
     277/// NVMe support
     278
     279class netbsd_nvme_device
     280: public /*implements*/ nvme_device,
     281  public /*extends*/ netbsd_smart_device
     282{
     283public:
     284  netbsd_nvme_device(smart_interface * intf, const char * dev_name,
     285    const char * req_type, unsigned nsid);
     286
     287  virtual bool open();
     288
     289  virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out);
     290};
     291
     292netbsd_nvme_device::netbsd_nvme_device(smart_interface * intf, const char * dev_name,
     293  const char * req_type, unsigned nsid)
     294: smart_device(intf, dev_name, "nvme", req_type),
     295  nvme_device(nsid),
     296  netbsd_smart_device()
     297{
     298}
     299
     300bool netbsd_nvme_device::open()
     301{
     302  const char *dev = get_dev_name();
     303  if (strncmp(dev, NVME_PREFIX, strlen(NVME_PREFIX))) {
     304        set_err(EINVAL, "NVMe controller controller/namespace ids must begin with '%s'",
     305                NVME_PREFIX);
     306        return false;
    329307  }
    330   if (req.retsts != ATACMD_OK) {
    331     return -1;
     308 
     309  int nsid = -1, ctrlid = -1;
     310  char tmp;
     311 
     312  if(sscanf(dev, NVME_PREFIX"%d%c", &ctrlid, &tmp) == 1)
     313  {
     314        if(ctrlid < 0) {
     315                set_err(EINVAL, "Invalid NVMe controller number");
     316                return false;
     317        }
     318        nsid = 0xFFFFFFFF; // broadcast id
    332319  }
     320  else if (sscanf(dev, NVME_PREFIX"%d"NVME_NS_PREFIX"%d%c",
     321        &ctrlid, &nsid, &tmp) == 2)
     322  {
     323        if(ctrlid < 0 || nsid <= 0) {
     324                set_err(EINVAL, "Invalid NVMe controller/namespace number");
     325                return false;
     326        }
     327  }
     328  else {
     329        set_err(EINVAL, "Invalid NVMe controller/namespace syntax");
     330        return false;
     331  }
     332 
     333  // we should always open controller, not namespace device
     334  char  full_path[64];
     335  snprintf(full_path, sizeof(full_path), NVME_PREFIX"%d", ctrlid);
     336 
     337  int fd;
     338  if ((fd = ::open(full_path, O_RDWR))<0) {
     339    set_err(errno);
     340    return false;
     341  }
     342  set_fd(fd);
     343 
     344  if (!get_nsid()) {
     345    set_nsid(nsid);
     346  }
     347 
     348  return true;
     349}
    333350
    334   if (command == CHECK_POWER_MODE)
    335     data[0] = req.sec_count;
     351bool netbsd_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out)
     352{
     353  // nvme_passthru_cmd pt;
     354  struct nvme_pt_command pt;
     355  memset(&pt, 0, sizeof(pt));
    336356
    337   if (copydata)
    338     memcpy(data, inbuf, 512);
     357  pt.cmd.opcode = in.opcode;
     358  pt.cmd.nsid = in.nsid;
     359  pt.buf = in.buffer;
     360  pt.len = in.size;
     361  pt.cmd.cdw10 = in.cdw10;
     362  pt.cmd.cdw11 = in.cdw11;
     363  pt.cmd.cdw12 = in.cdw12;
     364  pt.cmd.cdw13 = in.cdw13;
     365  pt.cmd.cdw14 = in.cdw14;
     366  pt.cmd.cdw15 = in.cdw15;
     367  pt.is_read = 1; // should we use in.direction()?
     368 
     369  int status = ioctl(get_fd(), NVME_PASSTHROUGH_CMD, &pt);
    339370
    340   return 0;
     371  if (status < 0)
     372    return set_err(errno, "NVME_PASSTHROUGH_CMD: %s", strerror(errno));
     373
     374  out.result=pt.cpl.cdw0; // Command specific result (DW0)
     375
     376  if (nvme_completion_is_error(&pt.cpl))
     377    return set_nvme_err(out, nvme_completion_is_error(&pt.cpl));
     378
     379  return true;
    341380}
    342381
    343 int
    344 do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
     382/////////////////////////////////////////////////////////////////////////////
     383/// Standard SCSI support
     384
     385class netbsd_scsi_device
     386: public /*implements*/ scsi_device,
     387  public /*extends*/ netbsd_smart_device
    345388{
     389public:
     390  netbsd_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type);
     391
     392  virtual bool scsi_pass_through(scsi_cmnd_io * iop);
     393};
     394
     395netbsd_scsi_device::netbsd_scsi_device(smart_interface * intf,
     396  const char * dev_name, const char * req_type)
     397: smart_device(intf, dev_name, "scsi", req_type),
     398  netbsd_smart_device()
     399{
     400}
     401
     402bool netbsd_scsi_device::scsi_pass_through(scsi_cmnd_io * iop)
     403{
    346404  struct scsireq sc;
     405  int fd = get_fd();
    347406
    348   if (report > 0) {
    349     size_t k;
     407  if (scsi_debugmode) {
     408    unsigned int k;
     409    const unsigned char * ucp = iop->cmnd;
     410    const char * np;
    350411
    351     const unsigned char *ucp = iop->cmnd;
    352     const char *np;
    353 
    354412    np = scsi_get_opcode_name(ucp[0]);
    355413    pout(" [%s: ", np ? np : "<unknown opcode>");
    356414    for (k = 0; k < iop->cmnd_len; ++k)
    357415      pout("%02x ", ucp[k]);
    358     if ((report > 1) &&
     416    if ((scsi_debugmode > 1) &&
    359417      (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
    360       int trunc = (iop->dxfer_len > 256) ? 1 : 0;
     418    int trunc = (iop->dxfer_len > 256) ? 1 : 0;
    361419
    362       pout("]\n  Outgoing data, len=%d%s:\n", (int) iop->dxfer_len,
    363         (trunc ? " [only first 256 bytes shown]" : ""));
    364       dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1);
    365     } else
    366       pout("]");
     420    pout("]\n  Outgoing data, len=%d%s:\n", (int)iop->dxfer_len,
     421      (trunc ? " [only first 256 bytes shown]" : ""));
     422    dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
     423      }
     424      else
     425        pout("]\n");
    367426  }
     427
    368428  memset(&sc, 0, sizeof(sc));
    369429  memcpy(sc.cmd, iop->cmnd, iop->cmnd_len);
    370430  sc.cmdlen = iop->cmnd_len;
     
    377437    (iop->dxfer_dir == DXFER_FROM_DEVICE ? SCCMD_READ : SCCMD_WRITE));
    378438
    379439  if (ioctl(fd, SCIOCCOMMAND, &sc) < 0) {
    380     warn("error sending SCSI ccb");
    381     return -1;
     440    if (scsi_debugmode) {
     441      pout("  error sending SCSI ccb\n");
     442    }
     443    return set_err(EIO);
    382444  }
    383445  iop->resid = sc.datalen - sc.datalen_used;
    384446  iop->scsi_status = sc.status;
     
    386448    memcpy(iop->sensep, sc.sense, sc.senselen_used);
    387449    iop->resp_sense_len = sc.senselen_used;
    388450  }
    389   if (report > 0) {
     451  if (scsi_debugmode) {
    390452    int trunc;
    391453
    392454    pout("  status=0\n");
     
    398460  }
    399461  switch (sc.retsts) {
    400462    case SCCMD_OK:
    401       return 0;
     463      break;
    402464    case SCCMD_TIMEOUT:
    403       return -ETIMEDOUT;
     465      return set_err(ETIMEDOUT);
    404466    case SCCMD_BUSY:
    405       return -EBUSY;
     467      return set_err(EBUSY);
    406468    default:
    407       return -EIO;
     469      return set_err(EIO);
    408470  }
     471
     472  return true;
    409473}
    410474
    411 /* print examples for smartctl */
    412 void
    413 print_smartctl_examples()
     475/////////////////////////////////////////////////////////////////////////////
     476/// Implement platform interface with old functions.
     477
     478class netbsd_smart_interface
     479: public /*implements*/ smart_interface
    414480{
    415   char p;
     481public:
     482  virtual std::string get_os_version_str();
    416483
    417   p = 'a' + getrawpartition();
    418   printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
     484  virtual std::string get_app_examples(const char * appname);
     485
     486  virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
     487    const char * pattern = 0);
     488
     489protected:
     490  virtual ata_device * get_ata_device(const char * name, const char * type);
     491
     492  virtual scsi_device * get_scsi_device(const char * name, const char * type);
     493 
     494  virtual nvme_device * get_nvme_device(const char * name, const char * type,
     495    unsigned nsid);
     496
     497  virtual smart_device * autodetect_smart_device(const char * name);
     498
     499  virtual smart_device * get_custom_smart_device(const char * name, const char * type);
     500
     501  virtual std::string get_valid_custom_dev_types_str();
     502
     503private:
     504  int get_dev_names(char ***, const char *);
     505};
     506
     507
     508//////////////////////////////////////////////////////////////////////
     509
     510std::string netbsd_smart_interface::get_os_version_str()
     511{
     512  struct utsname osname;
     513  uname(&osname);
     514  return strprintf("%s %s %s", osname.sysname, osname.release, osname.machine);
     515}
     516
     517std::string netbsd_smart_interface::get_app_examples(const char * appname)
     518{
     519  if (!strcmp(appname, "smartctl")) {
     520    char p;
     521
     522    p = 'a' + getrawpartition();
     523    return strprintf(
     524        "=================================================== SMARTCTL EXAMPLES =====\n\n"
    419525#ifdef HAVE_GETOPT_LONG
    420   printf(
    421     "  smartctl -a /dev/wd0%c                      (Prints all SMART information)\n\n"
    422     "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/wd0%c\n"
    423     "                                              (Enables SMART on first disk)\n\n"
    424     "  smartctl -t long /dev/wd0%c             (Executes extended disk self-test)\n\n"
    425     "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/wd0%c\n"
    426     "                                      (Prints Self-Test & Attribute errors)\n",
    427     p, p, p, p
    428     );
     526      "  smartctl -a /dev/wd0%c                      (Prints all SMART information)\n\n"
     527      "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/wd0%c\n"
     528      "                                              (Enables SMART on first disk)\n\n"
     529      "  smartctl -t long /dev/wd0%c             (Executes extended disk self-test)\n\n"
     530      "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/wd0%c\n"
     531      "                                      (Prints Self-Test & Attribute errors)\n"
    429532#else
    430   printf(
    431     "  smartctl -a /dev/wd0%c                     (Prints all SMART information)\n"
    432     "  smartctl -s on -o on -S on /dev/wd0%c        (Enables SMART on first disk)\n"
    433     "  smartctl -t long /dev/wd0%c            (Executes extended disk self-test)\n"
    434     "  smartctl -A -l selftest -q errorsonly /dev/wd0%c"
    435     "                                      (Prints Self-Test & Attribute errors)\n",
    436     p, p, p, p
    437     );
     533      "  smartctl -a /dev/wd0%c                     (Prints all SMART information)\n"
     534      "  smartctl -s on -o on -S on /dev/wd0%c        (Enables SMART on first disk)\n"
     535      "  smartctl -t long /dev/wd0%c            (Executes extended disk self-test)\n"
     536      "  smartctl -A -l selftest -q errorsonly /dev/wd0%c"
     537      "                                      (Prints Self-Test & Attribute errors)\n"
    438538#endif
    439   return;
     539      , p, p, p, p);
     540  }
     541  return "";
    440542}
     543
     544ata_device * netbsd_smart_interface::get_ata_device(const char * name, const char * type)
     545{
     546  return new netbsd_ata_device(this, name, type);
     547}
     548
     549scsi_device * netbsd_smart_interface::get_scsi_device(const char * name, const char * type)
     550{
     551  return new netbsd_scsi_device(this, name, type);
     552}
     553
     554nvme_device * netbsd_smart_interface::get_nvme_device(const char * name, const char * type,
     555  unsigned nsid)
     556{
     557  return new netbsd_nvme_device(this, name, type, nsid);
     558}
     559
     560int netbsd_smart_interface::get_dev_names(char ***names, const char *prefix)
     561{
     562  char *disknames, *p, **mp;
     563  int n = 0;
     564  int sysctl_mib[2];
     565  size_t sysctl_len;
     566
     567  *names = NULL;
     568
     569  sysctl_mib[0] = CTL_HW;
     570  sysctl_mib[1] = HW_DISKNAMES;
     571  if (-1 == sysctl(sysctl_mib, 2, NULL, &sysctl_len, NULL, 0)) {
     572    pout("Failed to get value of sysctl `hw.disknames'\n");
     573    return -1;
     574  }
     575  if (!(disknames = (char *)malloc(sysctl_len))) {
     576    pout("Out of memory constructing scan device list\n");
     577    return -1;
     578  }
     579  if (-1 == sysctl(sysctl_mib, 2, disknames, &sysctl_len, NULL, 0)) {
     580    pout("Failed to get value of sysctl `hw.disknames'\n");
     581    return -1;
     582  }
     583  if (!(mp = (char **) calloc(strlen(disknames) / 2, sizeof(char *)))) {
     584    pout("Out of memory constructing scan device list\n");
     585    return -1;
     586  }
     587  for (p = strtok(disknames, " "); p; p = strtok(NULL, " ")) {
     588    if (strncmp(p, prefix, strlen(prefix))) {
     589      continue;
     590    }
     591    mp[n] = (char *)malloc(strlen(net_dev_raw_prefix) + strlen(p) + 2);
     592    if (!mp[n]) {
     593      pout("Out of memory constructing scan device list\n");
     594      return -1;
     595    }
     596    sprintf(mp[n], "%s%s%c", net_dev_raw_prefix, p, 'a' + getrawpartition());
     597    n++;
     598  }
     599
     600  char ** tmp = (char **)realloc(mp, n * (sizeof(char *)));
     601  if (NULL == tmp) {
     602    pout("Out of memory constructing scan device list\n");
     603    free(mp);
     604    return -1;
     605  }
     606  else
     607    mp = tmp;
     608  *names = mp;
     609  return n;
     610}
     611
     612bool netbsd_smart_interface::scan_smart_devices(smart_device_list & devlist,
     613  const char * type, const char * pattern /*= 0*/)
     614{
     615  if (pattern) {
     616    set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");
     617    return false;
     618  }
     619
     620  if (type == NULL)
     621    type = "";
     622
     623  // Make namelists
     624  char * * atanames = 0; int numata = 0;
     625  if (!strcmp(type, "ata")) {
     626    numata = get_dev_names(&atanames, net_dev_ata_disk);
     627    if (numata < 0) {
     628      set_err(ENOMEM);
     629      return false;
     630    }
     631  }
     632
     633  char * * scsinames = 0; int numscsi = 0;
     634  char * * scsitapenames = 0; int numscsitape = 0;
     635  if (!strcmp(type, "scsi")) {
     636    numscsi = get_dev_names(&scsinames, net_dev_scsi_disk);
     637    if (numscsi < 0) {
     638      set_err(ENOMEM);
     639      return false;
     640    }
     641    numscsitape = get_dev_names(&scsitapenames, net_dev_scsi_tape);
     642    if (numscsitape < 0) {
     643      set_err(ENOMEM);
     644      return false;
     645    }
     646  }
     647
     648  // Add to devlist
     649  int i;
     650  for (i = 0; i < numata; i++) {
     651    ata_device * atadev = get_ata_device(atanames[i], type);
     652    if (atadev)
     653      devlist.push_back(atadev);
     654    free(atanames[i]);
     655  }
     656  if(numata) free(atanames);
     657
     658  for (i = 0; i < numscsi; i++) {
     659    scsi_device * scsidev = get_scsi_device(scsinames[i], type);
     660    if (scsidev)
     661      devlist.push_back(scsidev);
     662    free(scsinames[i]);
     663  }
     664  if(numscsi) free(scsinames);
     665
     666  for (i = 0; i < numscsitape; i++) {
     667    scsi_device * scsidev = get_scsi_device(scsitapenames[i], type);
     668    if (scsidev)
     669      devlist.push_back(scsidev);
     670    free(scsitapenames[i]);
     671  }
     672  if(numscsitape) free(scsitapenames);
     673
     674  return true;
     675}
     676
     677smart_device * netbsd_smart_interface::autodetect_smart_device(const char * name)
     678{
     679  const char * test_name = name;
     680
     681  // if dev_name null, or string length zero
     682  if (!name || !*name)
     683    return 0;
     684
     685  // Dereference symlinks
     686  struct stat st;
     687  std::string pathbuf;
     688  if (!lstat(name, &st) && S_ISLNK(st.st_mode)) {
     689    char * p = realpath(name, (char *)0);
     690    if (p) {
     691      pathbuf = p;
     692      free(p);
     693      test_name = pathbuf.c_str();
     694    }
     695  }
     696
     697  if (str_starts_with(test_name, net_dev_raw_prefix)) {
     698    test_name += strlen(net_dev_raw_prefix);
     699    if (!strncmp(net_dev_ata_disk, test_name, strlen(net_dev_ata_disk)))
     700      return new netbsd_ata_device(this, test_name, "ata");
     701    if (!strncmp(net_dev_scsi_disk, test_name, strlen(net_dev_scsi_disk)))
     702      return new netbsd_scsi_device(this, test_name, "scsi");
     703    if (!strncmp(net_dev_scsi_tape, test_name, strlen(net_dev_scsi_tape)))
     704      return new netbsd_scsi_device(this, test_name, "scsi");
     705  } else if (str_starts_with(test_name, net_dev_prefix)) {
     706    if(!strncmp(NVME_PREFIX, test_name, strlen(NVME_PREFIX)))
     707      return new netbsd_nvme_device(this, test_name, "nvme",
     708        0 /* use default nsid */);
     709  }
     710
     711  // device type unknown
     712  return 0;
     713}
     714
     715smart_device * netbsd_smart_interface::get_custom_smart_device(const char * name, const char * type)
     716{
     717  ARGUSED(name);
     718  ARGUSED(type);
     719  return 0;
     720}
     721
     722std::string netbsd_smart_interface::get_valid_custom_dev_types_str()
     723{
     724  return "";
     725}
     726
     727} // namespace
     728
     729/////////////////////////////////////////////////////////////////////////////
     730/// Initialize platform interface and register with smi()
     731
     732void smart_interface::init()
     733{
     734  static os_netbsd::netbsd_smart_interface the_interface;
     735  smart_interface::set(&the_interface);
     736}