xref: /freebsd/sbin/nvmecontrol/format.c (revision 9544e6dcf1f338af481614dc5daf232e1ed5860b)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (C) 2018 Alexander Motin <mav@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/ioccom.h>
34 
35 #include <ctype.h>
36 #include <err.h>
37 #include <fcntl.h>
38 #include <stddef.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 
44 #include "nvmecontrol.h"
45 
46 static void
47 format_usage(void)
48 {
49 	fprintf(stderr, "usage:\n");
50 	fprintf(stderr, FORMAT_USAGE);
51 	exit(1);
52 }
53 
54 void
55 format(int argc, char *argv[])
56 {
57 	struct nvme_controller_data	cd;
58 	struct nvme_namespace_data	nsd;
59 	struct nvme_pt_command		pt;
60 	char				path[64];
61 	char				*target;
62 	uint32_t			nsid;
63 	int				ch, fd;
64 	int lbaf = -1, mset = -1, pi = -1, pil = -1, ses = 0;
65 
66 	if (argc < 2)
67 		format_usage();
68 
69 	while ((ch = getopt(argc, argv, "f:m:p:l:EC")) != -1) {
70 		switch ((char)ch) {
71 		case 'f':
72 			lbaf = strtol(optarg, NULL, 0);
73 			break;
74 		case 'm':
75 			mset = strtol(optarg, NULL, 0);
76 			break;
77 		case 'p':
78 			pi = strtol(optarg, NULL, 0);
79 			break;
80 		case 'l':
81 			pil = strtol(optarg, NULL, 0);
82 			break;
83 		case 'E':
84 			if (ses == 2)
85 				errx(1, "-E and -C are mutually exclusive");
86 			ses = 1;
87 			break;
88 		case 'C':
89 			if (ses == 1)
90 				errx(1, "-E and -C are mutually exclusive");
91 			ses = 2;
92 			break;
93 		default:
94 			format_usage();
95 		}
96 	}
97 
98 	/* Check that a controller or namespace was specified. */
99 	if (optind >= argc)
100 		format_usage();
101 	target = argv[optind];
102 
103 	/*
104 	 * Check if the specified device node exists before continuing.
105 	 * This is a cleaner check for cases where the correct controller
106 	 * is specified, but an invalid namespace on that controller.
107 	 */
108 	open_dev(target, &fd, 1, 1);
109 
110 	/*
111 	 * If device node contains "ns", we consider it a namespace,
112 	 * otherwise, consider it a controller.
113 	 */
114 	if (strstr(target, NVME_NS_PREFIX) == NULL) {
115 		nsid = NVME_GLOBAL_NAMESPACE_TAG;
116 	} else {
117 		/*
118 		 * We send FORMAT commands to the controller, not the namespace,
119 		 * since it is an admin cmd.  The namespace ID will be specified
120 		 * in the command itself.  So parse the namespace's device node
121 		 * string to get the controller substring and namespace ID.
122 		 */
123 		close(fd);
124 		parse_ns_str(target, path, &nsid);
125 		open_dev(path, &fd, 1, 1);
126 	}
127 
128 	/* Check that controller can execute this command. */
129 	read_controller_data(fd, &cd);
130 	if (((cd.oacs >> NVME_CTRLR_DATA_OACS_FORMAT_SHIFT) &
131 	    NVME_CTRLR_DATA_OACS_FORMAT_MASK) == 0)
132 		errx(1, "controller does not support format");
133 	if (((cd.fna >> NVME_CTRLR_DATA_FNA_CRYPTO_ERASE_SHIFT) &
134 	    NVME_CTRLR_DATA_FNA_CRYPTO_ERASE_MASK) == 0 && ses == 2)
135 		errx(1, "controller does not support cryptographic erase");
136 
137 	if (nsid != NVME_GLOBAL_NAMESPACE_TAG) {
138 		if (((cd.fna >> NVME_CTRLR_DATA_FNA_FORMAT_ALL_SHIFT) &
139 		    NVME_CTRLR_DATA_FNA_FORMAT_ALL_MASK) && ses == 0)
140 			errx(1, "controller does not support per-NS format");
141 		if (((cd.fna >> NVME_CTRLR_DATA_FNA_ERASE_ALL_SHIFT) &
142 		    NVME_CTRLR_DATA_FNA_ERASE_ALL_MASK) && ses != 0)
143 			errx(1, "controller does not support per-NS erase");
144 
145 		/* Try to keep previous namespace parameters. */
146 		read_namespace_data(fd, nsid, &nsd);
147 		if (lbaf < 0)
148 			lbaf = (nsd.flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT)
149 			    & NVME_NS_DATA_FLBAS_FORMAT_MASK;
150 		if (lbaf > nsd.nlbaf)
151 			errx(1, "LBA format is out of range");
152 		if (mset < 0)
153 			mset = (nsd.flbas >> NVME_NS_DATA_FLBAS_EXTENDED_SHIFT)
154 			    & NVME_NS_DATA_FLBAS_EXTENDED_MASK;
155 		if (pi < 0)
156 			pi = (nsd.dps >> NVME_NS_DATA_DPS_MD_START_SHIFT)
157 			    & NVME_NS_DATA_DPS_MD_START_MASK;
158 		if (pil < 0)
159 			pil = (nsd.dps >> NVME_NS_DATA_DPS_PIT_SHIFT)
160 			    & NVME_NS_DATA_DPS_PIT_MASK;
161 	} else {
162 
163 		/* We have no previous parameters, so default to zeroes. */
164 		if (lbaf < 0)
165 			lbaf = 0;
166 		if (mset < 0)
167 			mset = 0;
168 		if (pi < 0)
169 			pi = 0;
170 		if (pil < 0)
171 			pil = 0;
172 	}
173 
174 	memset(&pt, 0, sizeof(pt));
175 	pt.cmd.opc = NVME_OPC_FORMAT_NVM;
176 	pt.cmd.nsid = htole32(nsid);
177 	pt.cmd.cdw10 = htole32((ses << 9) + (pil << 8) + (pi << 5) +
178 	    (mset << 4) + lbaf);
179 
180 	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
181 		err(1, "format request failed");
182 
183 	if (nvme_completion_is_error(&pt.cpl))
184 		errx(1, "format request returned error");
185 	close(fd);
186 	exit(0);
187 }
188