Ticket #728: netbsd-dev_interface-and-nvme.patch
File netbsd-dev_interface-and-nvme.patch, 35.5 KB (added by , 8 years ago) |
---|
-
Makefile.am
174 174 dev_legacy.cpp \ 175 175 linux_nvme_ioctl.h \ 176 176 freebsd_nvme_ioctl.h \ 177 netbsd_nvme_ioctl.h \ 177 178 megaraid.h 178 179 179 180 if 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 22 struct nvme_sge { 23 uint8_t id; 24 uint8_t _reserved[15]; 25 } __packed __aligned(8); 26 27 struct 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 51 struct 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 128 struct 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
4 4 * Home page of code is: http://www.smartmontools.org 5 5 * 6 6 * Copyright (C) 2003-8 Sergey Svishchev <smartmontools-support@lists.sourceforge.net> 7 * Copyright (C) 2016 Kimihiro Nonaka 7 8 * 8 9 * This program is free software; you can redistribute it and/or modify 9 10 * it under the terms of the GNU General Public License as published by … … 23 24 #include "utility.h" 24 25 #include "os_netbsd.h" 25 26 27 #include <sys/utsname.h> 26 28 #include <errno.h> 27 29 #include <unistd.h> 28 30 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 29 34 const char * os_netbsd_cpp_cvsid = "$Id$" 30 35 OS_NETBSD_H_CVSID; 31 36 … … 32 37 enum warnings { 33 38 BAD_SMART, MAX_MSG 34 39 }; 35 40 36 41 /* Utility function for printing warnings */ 37 42 void 38 43 printwarning(int msgNo, const char *extra) … … 54 59 return; 55 60 } 56 61 57 static const char *net_dev_prefix = "/dev/r"; 62 #define ARGUSED(x) ((void)(x)) 63 64 ///////////////////////////////////////////////////////////////////////////// 65 66 namespace os_netbsd { // No need to publish anything, name provided for Doxygen 67 68 static const char *net_dev_prefix = "/dev/"; 69 static const char *net_dev_raw_prefix = "/dev/r"; 58 70 static const char *net_dev_ata_disk = "wd"; 59 71 static const char *net_dev_scsi_disk = "sd"; 60 72 static const char *net_dev_scsi_tape = "enrst"; 61 73 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 77 class netbsd_smart_device 78 : virtual public /*implements*/ smart_device 65 79 { 66 int len; 67 int dev_prefix_len = strlen(net_dev_prefix); 80 public: 81 explicit netbsd_smart_device() 82 : smart_device(never_called), 83 m_fd(-1) { } 68 84 69 if (!dev_name || !(len = strlen(dev_name))) 70 return CONTROLLER_UNKNOWN; 85 virtual ~netbsd_smart_device() throw(); 71 86 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; 80 88 81 if (!strncmp(net_dev_scsi_disk, dev_name, strlen(net_dev_scsi_disk))) 82 return CONTROLLER_SCSI; 89 virtual bool open(); 83 90 84 if (!strncmp(net_dev_scsi_tape, dev_name, strlen(net_dev_scsi_tape))) 85 return CONTROLLER_SCSI; 91 virtual bool close(); 86 92 87 return CONTROLLER_UNKNOWN; 93 protected: 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 101 private: 102 int m_fd; ///< filedesc, -1 if not open. 103 }; 104 105 netbsd_smart_device::~netbsd_smart_device() throw() 106 { 107 if (m_fd >= 0) 108 os_netbsd::netbsd_smart_device::close(); 88 109 } 89 110 90 int 91 get_dev_names(char ***names, const char *prefix) 111 bool netbsd_smart_device::is_open() const 92 112 { 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 } 97 115 98 *names = NULL;99 116 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; 117 bool 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; 121 129 } 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; 126 134 } 127 sprintf(mp[n], "%s%s%c", net_dev_prefix, p, 'a' + getrawpartition()); 128 n++; 129 } 135 } else 136 return false; 130 137 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; 141 140 } 142 141 143 int 144 make_device_names(char ***devlist, const char *name) 142 bool netbsd_smart_device::close() 145 143 { 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; 152 153 } 153 154 154 int 155 deviceopen(const char *pathname, char *type) 155 ///////////////////////////////////////////////////////////////////////////// 156 /// Implement standard ATA support 157 158 class netbsd_ata_device 159 : public /*implements*/ ata_device, 160 public /*extends*/ netbsd_smart_device 156 161 { 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; 162 public: 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 166 protected: 167 virtual int do_cmd(struct atareq* request, bool is_48bit_cmd); 168 }; 169 170 netbsd_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 { 166 174 } 167 175 168 int 169 deviceclose(int fd) 176 int netbsd_ata_device::do_cmd( struct atareq* request, bool is_48bit_cmd) 170 177 { 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; 172 183 } 173 184 174 int 175 ata_command_interface(int fd, smart_command_set command, int select, char *data) 185 bool netbsd_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) 176 186 { 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 177 198 struct atareq req; 178 unsigned char inbuf[DEV_BSIZE]; 179 int retval, copydata = 0; 199 memset(&req, 0, sizeof(req)); 180 200 181 memset(&req, 0, sizeof(req));182 201 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)); 183 208 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); 292 225 } 293 226 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); 296 233 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; 298 241 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; 309 249 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)) 313 254 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 } 323 271 } 324 272 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 279 class netbsd_nvme_device 280 : public /*implements*/ nvme_device, 281 public /*extends*/ netbsd_smart_device 282 { 283 public: 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 292 netbsd_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 300 bool 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; 329 307 } 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 332 319 } 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 } 333 350 334 if (command == CHECK_POWER_MODE) 335 data[0] = req.sec_count; 351 bool 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)); 336 356 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); 339 370 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; 341 380 } 342 381 343 int 344 do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) 382 ///////////////////////////////////////////////////////////////////////////// 383 /// Standard SCSI support 384 385 class netbsd_scsi_device 386 : public /*implements*/ scsi_device, 387 public /*extends*/ netbsd_smart_device 345 388 { 389 public: 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 395 netbsd_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 402 bool netbsd_scsi_device::scsi_pass_through(scsi_cmnd_io * iop) 403 { 346 404 struct scsireq sc; 405 int fd = get_fd(); 347 406 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; 350 411 351 const unsigned char *ucp = iop->cmnd;352 const char *np;353 354 412 np = scsi_get_opcode_name(ucp[0]); 355 413 pout(" [%s: ", np ? np : "<unknown opcode>"); 356 414 for (k = 0; k < iop->cmnd_len; ++k) 357 415 pout("%02x ", ucp[k]); 358 if (( report > 1) &&416 if ((scsi_debugmode > 1) && 359 417 (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) { 360 418 int trunc = (iop->dxfer_len > 256) ? 1 : 0; 361 419 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"); 367 426 } 427 368 428 memset(&sc, 0, sizeof(sc)); 369 429 memcpy(sc.cmd, iop->cmnd, iop->cmnd_len); 370 430 sc.cmdlen = iop->cmnd_len; … … 377 437 (iop->dxfer_dir == DXFER_FROM_DEVICE ? SCCMD_READ : SCCMD_WRITE)); 378 438 379 439 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); 382 444 } 383 445 iop->resid = sc.datalen - sc.datalen_used; 384 446 iop->scsi_status = sc.status; … … 386 448 memcpy(iop->sensep, sc.sense, sc.senselen_used); 387 449 iop->resp_sense_len = sc.senselen_used; 388 450 } 389 if ( report > 0) {451 if (scsi_debugmode) { 390 452 int trunc; 391 453 392 454 pout(" status=0\n"); … … 398 460 } 399 461 switch (sc.retsts) { 400 462 case SCCMD_OK: 401 return 0;463 break; 402 464 case SCCMD_TIMEOUT: 403 return -ETIMEDOUT;465 return set_err(ETIMEDOUT); 404 466 case SCCMD_BUSY: 405 return -EBUSY;467 return set_err(EBUSY); 406 468 default: 407 return -EIO;469 return set_err(EIO); 408 470 } 471 472 return true; 409 473 } 410 474 411 /* print examples for smartctl */ 412 void 413 print_smartctl_examples() 475 ///////////////////////////////////////////////////////////////////////////// 476 /// Implement platform interface with old functions. 477 478 class netbsd_smart_interface 479 : public /*implements*/ smart_interface 414 480 { 415 char p; 481 public: 482 virtual std::string get_os_version_str(); 416 483 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 489 protected: 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 503 private: 504 int get_dev_names(char ***, const char *); 505 }; 506 507 508 ////////////////////////////////////////////////////////////////////// 509 510 std::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 517 std::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" 419 525 #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" 429 532 #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" 438 538 #endif 439 return; 539 , p, p, p, p); 540 } 541 return ""; 440 542 } 543 544 ata_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 549 scsi_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 554 nvme_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 560 int 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 612 bool 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 677 smart_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 715 smart_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 722 std::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 732 void smart_interface::init() 733 { 734 static os_netbsd::netbsd_smart_interface the_interface; 735 smart_interface::set(&the_interface); 736 }