xref: /haiku/src/add-ons/kernel/drivers/disk/nvme/libnvme/nvme_admin.c (revision d447d747817f39734bdabdd4ac5337a0db55dde3)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (c) Intel Corporation. All rights reserved.
5  *   Copyright (c) 2017, Western Digital Corporation or its affiliates.
6  *
7  *   Redistribution and use in sourete and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of sourete code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "nvme_internal.h"
35 
36 /*
37  * Allocate a request, set its command and submit it
38  * to the controller admin queue.
39  */
nvme_admin_submit_cmd(struct nvme_ctrlr * ctrlr,struct nvme_cmd * cmd,void * buf,uint32_t len,nvme_cmd_cb cb_fn,void * cb_arg)40 static int nvme_admin_submit_cmd(struct nvme_ctrlr *ctrlr,
41 				 struct nvme_cmd *cmd,
42 				 void *buf, uint32_t len,
43 				 nvme_cmd_cb cb_fn, void *cb_arg)
44 {
45 	struct nvme_request *req;
46 
47 	if (buf)
48 		req = nvme_request_allocate_contig(&ctrlr->adminq, buf, len,
49 						   cb_fn, cb_arg);
50 	else
51 		req = nvme_request_allocate_null(&ctrlr->adminq, cb_fn, cb_arg);
52 	if (!req)
53 		return ENOMEM;
54 
55 	memcpy(&req->cmd, cmd, sizeof(req->cmd));
56 
57 	return nvme_qpair_submit_request(&ctrlr->adminq, req);
58 }
59 
60 /*
61  * Poll the controller admin queue waiting for a
62  * command completion.
63  */
nvme_admin_wait_cmd(struct nvme_ctrlr * ctrlr,struct nvme_completion_poll_status * status)64 static int nvme_admin_wait_cmd(struct nvme_ctrlr *ctrlr,
65 			       struct nvme_completion_poll_status *status)
66 {
67 
68 	/* Wait for completion and check result */
69 	while (status->done == false)
70 		nvme_qpair_poll(&ctrlr->adminq, 0);
71 
72 	if (nvme_cpl_is_error(&status->cpl)) {
73 		nvme_notice("Admin command failed\n");
74 		return ENXIO;
75 	}
76 
77 	return 0;
78 }
79 
80 /*
81  * Execute an admin command.
82  */
nvme_admin_exec_cmd(struct nvme_ctrlr * ctrlr,struct nvme_cmd * cmd,void * buf,uint32_t len)83 static int nvme_admin_exec_cmd(struct nvme_ctrlr *ctrlr,
84 			       struct nvme_cmd *cmd,
85 			       void *buf, uint32_t len)
86 {
87 	struct nvme_completion_poll_status status;
88 	int ret;
89 
90 	/* Submit the command */
91 	status.done = false;
92 	ret = nvme_admin_submit_cmd(ctrlr, cmd, buf, len,
93 				    nvme_request_completion_poll_cb,
94 				    &status);
95 	if (ret != 0)
96 		return ret;
97 
98 	/* Wait for the command completion and check result */
99 	return nvme_admin_wait_cmd(ctrlr, &status);
100 }
101 
102 /*
103  * Get a controller information.
104  */
nvme_admin_identify_ctrlr(struct nvme_ctrlr * ctrlr,struct nvme_ctrlr_data * cdata)105 int nvme_admin_identify_ctrlr(struct nvme_ctrlr *ctrlr,
106 			      struct nvme_ctrlr_data *cdata)
107 {
108 	struct nvme_cmd cmd;
109 
110 	/* Setup the command */
111 	memset(&cmd, 0, sizeof(struct nvme_cmd));
112 	cmd.opc = NVME_OPC_IDENTIFY;
113 	cmd.cdw10 = NVME_IDENTIFY_CTRLR;
114 
115 	/* Execute the command */
116 	return nvme_admin_exec_cmd(ctrlr, &cmd,
117 				   cdata, sizeof(struct nvme_ctrlr_data));
118 }
119 
120 /*
121  * Get a controller feature.
122  */
nvme_admin_get_feature(struct nvme_ctrlr * ctrlr,enum nvme_feat_sel sel,enum nvme_feat feature,uint32_t cdw11,uint32_t * attributes)123 int nvme_admin_get_feature(struct nvme_ctrlr *ctrlr,
124 			   enum nvme_feat_sel sel,
125 			   enum nvme_feat feature,
126 			   uint32_t cdw11,
127 			   uint32_t *attributes)
128 {
129 	struct nvme_completion_poll_status status;
130 	struct nvme_cmd cmd;
131 	int ret;
132 
133 	/* Setup the command */
134 	memset(&cmd, 0, sizeof(struct nvme_cmd));
135 	cmd.opc = NVME_OPC_GET_FEATURES;
136 	cmd.cdw10 = (sel << 8) | feature;
137 	cmd.cdw11 = cdw11;
138 
139 	/* Submit the command */
140 	status.done = false;
141 	ret = nvme_admin_submit_cmd(ctrlr, &cmd, NULL, 0,
142 				    nvme_request_completion_poll_cb,
143 				    &status);
144 	if (ret == 0) {
145 		/* Wait for the command completion and check result */
146 		ret = nvme_admin_wait_cmd(ctrlr, &status);
147 		if (ret == 0 && attributes)
148 			*attributes = status.cpl.cdw0;
149 	}
150 
151 	return ret;
152 }
153 
154 /*
155  * Set a feature.
156  */
nvme_admin_set_feature(struct nvme_ctrlr * ctrlr,bool save,enum nvme_feat feature,uint32_t cdw11,uint32_t cdw12,uint32_t * attributes)157 int nvme_admin_set_feature(struct nvme_ctrlr *ctrlr,
158 			   bool save,
159 			   enum nvme_feat feature,
160 			   uint32_t cdw11,
161 			   uint32_t cdw12,
162 			   uint32_t *attributes)
163 {
164 	struct nvme_completion_poll_status status;
165 	struct nvme_cmd cmd;
166 	int ret;
167 
168 	/* Setup the command */
169 	memset(&cmd, 0, sizeof(struct nvme_cmd));
170 	cmd.opc = NVME_OPC_SET_FEATURES;
171 	cmd.cdw10 = feature;
172 	if (save)
173 		cmd.cdw10 |= (1 << 31);
174 	cmd.cdw11 = cdw11;
175 	cmd.cdw12 = cdw12;
176 
177 	/* Submit the command */
178 	status.done = false;
179 	ret = nvme_admin_submit_cmd(ctrlr, &cmd, NULL, 0,
180 				    nvme_request_completion_poll_cb,
181 				    &status);
182 	if (ret == 0) {
183 		/* Wait for the command completion and check result */
184 		ret = nvme_admin_wait_cmd(ctrlr, &status);
185 		if (ret == 0 && attributes)
186 			*attributes = status.cpl.cdw0;
187 	}
188 
189 	return ret;
190 }
191 
192 /*
193  * Create an I/O queue.
194  */
nvme_admin_create_ioq(struct nvme_ctrlr * ctrlr,struct nvme_qpair * qpair,enum nvme_io_queue_type io_qtype)195 int nvme_admin_create_ioq(struct nvme_ctrlr *ctrlr,
196 			  struct nvme_qpair *qpair,
197 			  enum nvme_io_queue_type io_qtype)
198 {
199 	struct nvme_cmd cmd;
200 
201 	/* Setup the command */
202 	memset(&cmd, 0, sizeof(struct nvme_cmd));
203 	switch(io_qtype) {
204 	case NVME_IO_SUBMISSION_QUEUE:
205 		cmd.opc = NVME_OPC_CREATE_IO_SQ;
206 		cmd.cdw11 = (qpair->id << 16) | (qpair->qprio << 1) | 0x1;
207 		cmd.dptr.prp.prp1 = qpair->cmd_bus_addr;
208 		break;
209 	case NVME_IO_COMPLETION_QUEUE:
210 		cmd.opc = NVME_OPC_CREATE_IO_CQ;
211 #ifdef __HAIKU__ // TODO: Option!
212 		cmd.cdw11 = 0x1 | 0x2; /* enable interrupts */
213 #else
214 		cmd.cdw11 = 0x1;
215 #endif
216 		cmd.dptr.prp.prp1 = qpair->cpl_bus_addr;
217 		break;
218 	default:
219 		return EINVAL;
220 	}
221 
222 	cmd.cdw10 = ((qpair->entries - 1) << 16) | qpair->id;
223 
224 	/* Execute the command */
225 	return nvme_admin_exec_cmd(ctrlr, &cmd, NULL, 0);
226 }
227 
228 /*
229  * Delete an I/O queue.
230  */
nvme_admin_delete_ioq(struct nvme_ctrlr * ctrlr,struct nvme_qpair * qpair,enum nvme_io_queue_type io_qtype)231 int nvme_admin_delete_ioq(struct nvme_ctrlr *ctrlr,
232 			  struct nvme_qpair *qpair,
233 			  enum nvme_io_queue_type io_qtype)
234 {
235 	struct nvme_cmd cmd;
236 
237 	/* Setup the command */
238 	memset(&cmd, 0, sizeof(struct nvme_cmd));
239 	switch(io_qtype) {
240 	case NVME_IO_SUBMISSION_QUEUE:
241 		cmd.opc = NVME_OPC_DELETE_IO_SQ;
242 		break;
243 	case NVME_IO_COMPLETION_QUEUE:
244 		cmd.opc = NVME_OPC_DELETE_IO_CQ;
245 		break;
246 	default:
247 		return EINVAL;
248 	}
249 	cmd.cdw10 = qpair->id;
250 
251 	/* Execute the command */
252 	return nvme_admin_exec_cmd(ctrlr, &cmd, NULL, 0);
253 }
254 
255 /*
256  * Get a namespace information.
257  */
nvme_admin_identify_ns(struct nvme_ctrlr * ctrlr,uint16_t nsid,struct nvme_ns_data * nsdata)258 int nvme_admin_identify_ns(struct nvme_ctrlr *ctrlr,
259 			   uint16_t nsid,
260 			   struct nvme_ns_data *nsdata)
261 {
262 	struct nvme_cmd cmd;
263 
264 	/* Setup the command */
265 	memset(&cmd, 0, sizeof(struct nvme_cmd));
266 	cmd.opc = NVME_OPC_IDENTIFY;
267 	cmd.cdw10 = NVME_IDENTIFY_NS;
268 	cmd.nsid = nsid;
269 
270 	/* Execute the command */
271 	return nvme_admin_exec_cmd(ctrlr, &cmd,
272 				   nsdata, sizeof(struct nvme_ns_data));
273 }
274 
275 /*
276  * Attach a namespace.
277  */
nvme_admin_attach_ns(struct nvme_ctrlr * ctrlr,uint32_t nsid,struct nvme_ctrlr_list * clist)278 int nvme_admin_attach_ns(struct nvme_ctrlr *ctrlr,
279 			 uint32_t nsid,
280 			 struct nvme_ctrlr_list *clist)
281 {
282 	struct nvme_cmd cmd;
283 
284 	/* Setup the command */
285 	memset(&cmd, 0, sizeof(struct nvme_cmd));
286 	cmd.opc = NVME_OPC_NS_ATTACHMENT;
287 	cmd.nsid = nsid;
288 	cmd.cdw10 = NVME_NS_CTRLR_ATTACH;
289 
290 	/* Execute the command */
291 	return nvme_admin_exec_cmd(ctrlr, &cmd,
292 				   clist, sizeof(struct nvme_ctrlr_list));
293 }
294 
295 /*
296  * Detach a namespace.
297  */
nvme_admin_detach_ns(struct nvme_ctrlr * ctrlr,uint32_t nsid,struct nvme_ctrlr_list * clist)298 int nvme_admin_detach_ns(struct nvme_ctrlr *ctrlr,
299 			 uint32_t nsid,
300 			 struct nvme_ctrlr_list *clist)
301 {
302 	struct nvme_cmd cmd;
303 
304 	/* Setup the command */
305 	memset(&cmd, 0, sizeof(struct nvme_cmd));
306 	cmd.opc = NVME_OPC_NS_ATTACHMENT;
307 	cmd.nsid = nsid;
308 	cmd.cdw10 = NVME_NS_CTRLR_DETACH;
309 
310 	/* Execute the command */
311 	return nvme_admin_exec_cmd(ctrlr, &cmd,
312 				   clist, sizeof(struct nvme_ctrlr_list));
313 }
314 
315 /*
316  * Create a namespace.
317  */
nvme_admin_create_ns(struct nvme_ctrlr * ctrlr,struct nvme_ns_data * nsdata,unsigned int * nsid)318 int nvme_admin_create_ns(struct nvme_ctrlr *ctrlr,
319 			 struct nvme_ns_data *nsdata,
320 			 unsigned int *nsid)
321 {
322 	struct nvme_completion_poll_status status;
323 	struct nvme_cmd cmd;
324 	int ret;
325 
326 	/* Setup the command */
327 	memset(&cmd, 0, sizeof(struct nvme_cmd));
328 	cmd.opc = NVME_OPC_NS_MANAGEMENT;
329 	cmd.cdw10 = NVME_NS_MANAGEMENT_CREATE;
330 
331 	/* Submit the command */
332 	status.done = false;
333 	ret = nvme_admin_submit_cmd(ctrlr, &cmd,
334 				    nsdata, sizeof(struct nvme_ns_data),
335 				    nvme_request_completion_poll_cb,
336 				    &status);
337 	if (ret == 0)
338 		/* Wait for the command completion and check result */
339 		ret = nvme_admin_wait_cmd(ctrlr, &status);
340 
341 	if (ret != 0)
342 		return ret;
343 
344 	*nsid = status.cpl.cdw0;
345 
346 	return 0;
347 }
348 
349 /*
350  * Delete a namespace.
351  */
nvme_admin_delete_ns(struct nvme_ctrlr * ctrlr,unsigned int nsid)352 int nvme_admin_delete_ns(struct nvme_ctrlr *ctrlr,
353 			 unsigned int nsid)
354 {
355 	struct nvme_cmd cmd;
356 
357 	/* Setup the command */
358 	memset(&cmd, 0, sizeof(struct nvme_cmd));
359 	cmd.opc = NVME_OPC_NS_MANAGEMENT;
360 	cmd.cdw10 = NVME_NS_MANAGEMENT_DELETE;
361 	cmd.nsid = nsid;
362 
363 	/* Execute the command */
364 	return nvme_admin_exec_cmd(ctrlr, &cmd, NULL, 0);
365 }
366 
367 /*
368  * Format media.
369  * (entire device or just the specified namespace)
370  */
nvme_admin_format_nvm(struct nvme_ctrlr * ctrlr,unsigned int nsid,struct nvme_format * format)371 int nvme_admin_format_nvm(struct nvme_ctrlr *ctrlr,
372 			  unsigned int nsid,
373 			  struct nvme_format *format)
374 {
375 	struct nvme_cmd cmd;
376 
377 	/* Setup the command */
378 	memset(&cmd, 0, sizeof(struct nvme_cmd));
379 	cmd.opc = NVME_OPC_FORMAT_NVM;
380 	cmd.nsid = nsid;
381 	memcpy(&cmd.cdw10, format, sizeof(uint32_t));
382 
383 	/* Execute the command */
384 	return nvme_admin_exec_cmd(ctrlr, &cmd, NULL, 0);
385 }
386 
387 /*
388  * Get a log page.
389  */
nvme_admin_get_log_page(struct nvme_ctrlr * ctrlr,uint8_t log_page,uint32_t nsid,void * payload,uint32_t payload_size)390 int nvme_admin_get_log_page(struct nvme_ctrlr *ctrlr,
391 			    uint8_t log_page,
392 			    uint32_t nsid,
393 			    void *payload,
394 			    uint32_t payload_size)
395 {
396 	struct nvme_cmd cmd;
397 
398 	/* Setup the command */
399 	memset(&cmd, 0, sizeof(struct nvme_cmd));
400 	cmd.opc = NVME_OPC_GET_LOG_PAGE;
401 	cmd.nsid = nsid;
402 	cmd.cdw10 = ((payload_size / sizeof(uint32_t)) - 1) << 16;
403 	cmd.cdw10 |= log_page;
404 
405 	/* Execute the command */
406 	return nvme_admin_exec_cmd(ctrlr, &cmd, payload, payload_size);
407 }
408 
409 /*
410  * Abort an admin or an I/O command.
411  */
nvme_admin_abort_cmd(struct nvme_ctrlr * ctrlr,uint16_t cid,uint16_t sqid)412 int nvme_admin_abort_cmd(struct nvme_ctrlr *ctrlr,
413 			 uint16_t cid, uint16_t sqid)
414 {
415 	struct nvme_cmd cmd;
416 
417 	/* Setup the command */
418 	memset(&cmd, 0, sizeof(struct nvme_cmd));
419 	cmd.opc = NVME_OPC_ABORT;
420 	cmd.cdw10 = (cid << 16) | sqid;
421 
422 	/* Execute the command */
423 	return nvme_admin_exec_cmd(ctrlr, &cmd, NULL, 0);
424 }
425 
426 /*
427  * Validate a FW.
428  */
nvme_admin_fw_commit(struct nvme_ctrlr * ctrlr,const struct nvme_fw_commit * fw_commit)429 int nvme_admin_fw_commit(struct nvme_ctrlr *ctrlr,
430 			 const struct nvme_fw_commit *fw_commit)
431 {
432 	struct nvme_cmd cmd;
433 
434 	/* Setup the command */
435 	memset(&cmd, 0, sizeof(struct nvme_cmd));
436 	cmd.opc = NVME_OPC_FIRMWARE_COMMIT;
437 	memcpy(&cmd.cdw10, fw_commit, sizeof(uint32_t));
438 
439 	/* Execute the command */
440 	return nvme_admin_exec_cmd(ctrlr, &cmd, NULL, 0);
441 }
442 
443 /*
444  * Download to the device a firmware.
445  */
nvme_admin_fw_image_dl(struct nvme_ctrlr * ctrlr,void * fw,uint32_t size,uint32_t offset)446 int nvme_admin_fw_image_dl(struct nvme_ctrlr *ctrlr,
447 			   void *fw, uint32_t size,
448 			   uint32_t offset)
449 {
450 	struct nvme_cmd cmd;
451 
452 	/* Setup the command */
453 	memset(&cmd, 0, sizeof(struct nvme_cmd));
454 	cmd.opc = NVME_OPC_FIRMWARE_IMAGE_DOWNLOAD;
455 	cmd.cdw10 = (size >> 2) - 1;
456 	cmd.cdw11 = offset >> 2;
457 
458 	/* Execute the command */
459 	return nvme_admin_exec_cmd(ctrlr, &cmd, fw, size);
460 }
461