xref: /illumos-gate/usr/src/cmd/nvmeadm/nvmeadm_dev.c (revision baf9a850)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2022 Tintri by DDN, Inc. All rights reserved.
14  */
15 
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <fcntl.h>
19 #include <unistd.h>
20 #include <stropts.h>
21 #include <err.h>
22 #include <libdevinfo.h>
23 #include <sys/nvme.h>
24 #include <assert.h>
25 
26 #include "nvmeadm.h"
27 
28 
29 static boolean_t
nvme_ioctl(int fd,int ioc,size_t * bufsize,void ** buf,uint64_t arg,uint64_t * res)30 nvme_ioctl(int fd, int ioc, size_t *bufsize, void **buf, uint64_t arg,
31     uint64_t *res)
32 {
33 	nvme_ioctl_t nioc = { 0 };
34 	void *ptr = NULL;
35 	int ret;
36 
37 	if (bufsize != NULL && *bufsize != 0) {
38 		assert(buf != NULL);
39 
40 		if (*buf != NULL) {
41 			nioc.n_buf = (uintptr_t)*buf;
42 		} else {
43 			ptr = calloc(1, *bufsize);
44 			if (ptr == NULL)
45 				err(-1, "nvme_ioctl()");
46 
47 			nioc.n_buf = (uintptr_t)ptr;
48 		}
49 
50 		nioc.n_len = *bufsize;
51 	}
52 
53 	nioc.n_arg = arg;
54 
55 	ret = ioctl(fd, ioc, &nioc);
56 
57 	if (res != NULL)
58 		*res = nioc.n_arg;
59 
60 	if (ret != 0) {
61 		/*
62 		 * We're not clearing *res here as there may be cases where
63 		 * we get an error _and_ we have interesting information in
64 		 * returned in *res that callers of this functions might be
65 		 * interested in.
66 		 */
67 
68 		if (debug)
69 			warn("nvme_ioctl()");
70 		if (ptr != NULL)
71 			free(ptr);
72 
73 		return (B_FALSE);
74 	}
75 
76 	if (bufsize != NULL)
77 		*bufsize = nioc.n_len;
78 
79 	if (buf != NULL)
80 		*buf = (void *)nioc.n_buf;
81 
82 	return (B_TRUE);
83 }
84 
85 nvme_capabilities_t *
nvme_capabilities(int fd)86 nvme_capabilities(int fd)
87 {
88 	void *cap = NULL;
89 	size_t bufsize = sizeof (nvme_capabilities_t);
90 
91 	(void) nvme_ioctl(fd, NVME_IOC_CAPABILITIES, &bufsize, &cap, 0, NULL);
92 
93 	return (cap);
94 }
95 
96 nvme_version_t *
nvme_version(int fd)97 nvme_version(int fd)
98 {
99 	void *vs = NULL;
100 	size_t bufsize = sizeof (nvme_version_t);
101 
102 	(void) nvme_ioctl(fd, NVME_IOC_VERSION, &bufsize, &vs, 0, NULL);
103 
104 	return (vs);
105 }
106 
107 nvme_identify_ctrl_t *
nvme_identify_ctrl(int fd)108 nvme_identify_ctrl(int fd)
109 {
110 	void *idctl = NULL;
111 	size_t bufsize = NVME_IDENTIFY_BUFSIZE;
112 
113 	(void) nvme_ioctl(fd, NVME_IOC_IDENTIFY_CTRL, &bufsize, &idctl, 0,
114 	    NULL);
115 
116 	return (idctl);
117 }
118 
119 nvme_identify_nsid_t *
nvme_identify_nsid(int fd)120 nvme_identify_nsid(int fd)
121 {
122 	void *idns = NULL;
123 	size_t bufsize = NVME_IDENTIFY_BUFSIZE;
124 
125 	(void) nvme_ioctl(fd, NVME_IOC_IDENTIFY_NSID, &bufsize, &idns, 0, NULL);
126 
127 	return (idns);
128 }
129 
130 void *
nvme_get_logpage(int fd,uint8_t logpage,size_t * bufsize)131 nvme_get_logpage(int fd, uint8_t logpage, size_t *bufsize)
132 {
133 	void *buf = NULL;
134 
135 	(void) nvme_ioctl(fd, NVME_IOC_GET_LOGPAGE, bufsize, &buf, logpage,
136 	    NULL);
137 
138 	return (buf);
139 }
140 
141 boolean_t
nvme_get_feature(int fd,uint8_t feature,uint32_t arg,uint64_t * res,size_t * bufsize,void ** buf)142 nvme_get_feature(int fd, uint8_t feature, uint32_t arg, uint64_t *res,
143     size_t *bufsize, void **buf)
144 {
145 	return (nvme_ioctl(fd, NVME_IOC_GET_FEATURES, bufsize, buf,
146 	    (uint64_t)feature << 32 | arg, res));
147 }
148 
149 int
nvme_intr_cnt(int fd)150 nvme_intr_cnt(int fd)
151 {
152 	uint64_t res = 0;
153 
154 	if (!nvme_ioctl(fd, NVME_IOC_INTR_CNT, NULL, NULL, 0, &res))
155 		return (-1);
156 
157 	return ((int)res);
158 }
159 
160 boolean_t
nvme_format_nvm(int fd,uint8_t lbaf,uint8_t ses)161 nvme_format_nvm(int fd, uint8_t lbaf, uint8_t ses)
162 {
163 	nvme_format_nvm_t frmt = { 0 };
164 
165 	frmt.b.fm_lbaf = lbaf & 0xf;
166 	frmt.b.fm_ses = ses & 0x7;
167 
168 	return (nvme_ioctl(fd, NVME_IOC_FORMAT, NULL, NULL, frmt.r, NULL));
169 }
170 
171 boolean_t
nvme_detach(int fd)172 nvme_detach(int fd)
173 {
174 	return (nvme_ioctl(fd, NVME_IOC_DETACH, NULL, NULL, 0, NULL));
175 }
176 
177 boolean_t
nvme_attach(int fd)178 nvme_attach(int fd)
179 {
180 	return (nvme_ioctl(fd, NVME_IOC_ATTACH, NULL, NULL, 0, NULL));
181 }
182 
183 boolean_t
nvme_firmware_load(int fd,void * buf,size_t len,offset_t offset,uint16_t * sc)184 nvme_firmware_load(int fd, void *buf, size_t len, offset_t offset, uint16_t *sc)
185 {
186 	boolean_t rv;
187 	uint64_t res;
188 
189 	rv = nvme_ioctl(fd, NVME_IOC_FIRMWARE_DOWNLOAD, &len, &buf, offset,
190 	    &res);
191 
192 	/*
193 	 * If the hardware returned a command-specific status code, we'll get
194 	 * it as a negative value from the driver.
195 	 */
196 	if ((int64_t)res < 0)
197 		*sc = (uint16_t)-(int64_t)res;
198 	else
199 		*sc = 0;
200 
201 	return (rv);
202 }
203 
204 boolean_t
nvme_firmware_commit(int fd,int slot,int action,uint16_t * sc)205 nvme_firmware_commit(int fd, int slot, int action, uint16_t *sc)
206 {
207 	boolean_t rv;
208 	uint64_t res;
209 
210 	rv = nvme_ioctl(fd, NVME_IOC_FIRMWARE_COMMIT, NULL, NULL,
211 	    ((uint64_t)action << 32) | slot, &res);
212 
213 	/*
214 	 * If the hardware returned a command-specific status code, we'll get
215 	 * it as a negative value from the driver.
216 	 */
217 	if ((int64_t)res < 0)
218 		*sc = (uint16_t)-(int64_t)res;
219 	else
220 		*sc = 0;
221 
222 	return (rv);
223 }
224 
225 uint32_t
nvme_namespace_state(int fd)226 nvme_namespace_state(int fd)
227 {
228 	uint64_t res = 0;
229 
230 	/*
231 	 * Ask the driver for the namespace state.
232 	 */
233 	if (nvme_ioctl(fd, NVME_IOC_NS_STATE, NULL, NULL, 0, &res)) {
234 		return (res);
235 	}
236 
237 	/*
238 	 * We're only here if the ioctl failed, which it really shouldnt. If so,
239 	 * we treat this the same as if the namespace was ignored.
240 	 */
241 	return (NVME_NS_STATE_IGNORED);
242 }
243 
244 int
nvme_open(di_minor_t minor,boolean_t excl)245 nvme_open(di_minor_t minor, boolean_t excl)
246 {
247 	char *devpath, *path;
248 	int fd;
249 
250 	if ((devpath = di_devfs_minor_path(minor)) == NULL)
251 		err(-1, "nvme_open()");
252 
253 	if (asprintf(&path, "/devices%s", devpath) < 0) {
254 		di_devfs_path_free(devpath);
255 		err(-1, "nvme_open()");
256 	}
257 
258 	di_devfs_path_free(devpath);
259 
260 	fd = open(path, O_RDWR | (excl ? O_EXCL: 0));
261 
262 	if (fd < 0) {
263 		if (debug)
264 			warn("nvme_open(%s)", path);
265 		free(path);
266 		return (-1);
267 	}
268 	free(path);
269 
270 	return (fd);
271 }
272 
273 void
nvme_close(int fd)274 nvme_close(int fd)
275 {
276 	(void) close(fd);
277 }
278