xref: /freebsd/sbin/nvmecontrol/format.c (revision 6829dae12bb055451fa467da4589c43bd03b1e64)
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 #define FORMAT_USAGE							       \
47 	"format [-f fmt] [-m mset] [-p pi] [-l pil] [-E] [-C] <controller id|namespace id>\n"
48 
49 static void
50 format(const struct nvme_function *nf, int argc, char *argv[])
51 {
52 	struct nvme_controller_data	cd;
53 	struct nvme_namespace_data	nsd;
54 	struct nvme_pt_command		pt;
55 	char				path[64];
56 	char				*target;
57 	uint32_t			nsid;
58 	int				ch, fd;
59 	int lbaf = -1, mset = -1, pi = -1, pil = -1, ses = 0;
60 
61 	if (argc < 2)
62 		usage(nf);
63 
64 	while ((ch = getopt(argc, argv, "f:m:p:l:EC")) != -1) {
65 		switch ((char)ch) {
66 		case 'f':
67 			lbaf = strtol(optarg, NULL, 0);
68 			break;
69 		case 'm':
70 			mset = strtol(optarg, NULL, 0);
71 			break;
72 		case 'p':
73 			pi = strtol(optarg, NULL, 0);
74 			break;
75 		case 'l':
76 			pil = strtol(optarg, NULL, 0);
77 			break;
78 		case 'E':
79 			if (ses == 2)
80 				errx(1, "-E and -C are mutually exclusive");
81 			ses = 1;
82 			break;
83 		case 'C':
84 			if (ses == 1)
85 				errx(1, "-E and -C are mutually exclusive");
86 			ses = 2;
87 			break;
88 		default:
89 			usage(nf);
90 		}
91 	}
92 
93 	/* Check that a controller or namespace was specified. */
94 	if (optind >= argc)
95 		usage(nf);
96 	target = argv[optind];
97 
98 	/*
99 	 * Check if the specified device node exists before continuing.
100 	 * This is a cleaner check for cases where the correct controller
101 	 * is specified, but an invalid namespace on that controller.
102 	 */
103 	open_dev(target, &fd, 1, 1);
104 
105 	/*
106 	 * If device node contains "ns", we consider it a namespace,
107 	 * otherwise, consider it a controller.
108 	 */
109 	if (strstr(target, NVME_NS_PREFIX) == NULL) {
110 		nsid = NVME_GLOBAL_NAMESPACE_TAG;
111 	} else {
112 		/*
113 		 * We send FORMAT commands to the controller, not the namespace,
114 		 * since it is an admin cmd.  The namespace ID will be specified
115 		 * in the command itself.  So parse the namespace's device node
116 		 * string to get the controller substring and namespace ID.
117 		 */
118 		close(fd);
119 		parse_ns_str(target, path, &nsid);
120 		open_dev(path, &fd, 1, 1);
121 	}
122 
123 	/* Check that controller can execute this command. */
124 	read_controller_data(fd, &cd);
125 	if (((cd.oacs >> NVME_CTRLR_DATA_OACS_FORMAT_SHIFT) &
126 	    NVME_CTRLR_DATA_OACS_FORMAT_MASK) == 0)
127 		errx(1, "controller does not support format");
128 	if (((cd.fna >> NVME_CTRLR_DATA_FNA_CRYPTO_ERASE_SHIFT) &
129 	    NVME_CTRLR_DATA_FNA_CRYPTO_ERASE_MASK) == 0 && ses == 2)
130 		errx(1, "controller does not support cryptographic erase");
131 
132 	if (nsid != NVME_GLOBAL_NAMESPACE_TAG) {
133 		if (((cd.fna >> NVME_CTRLR_DATA_FNA_FORMAT_ALL_SHIFT) &
134 		    NVME_CTRLR_DATA_FNA_FORMAT_ALL_MASK) && ses == 0)
135 			errx(1, "controller does not support per-NS format");
136 		if (((cd.fna >> NVME_CTRLR_DATA_FNA_ERASE_ALL_SHIFT) &
137 		    NVME_CTRLR_DATA_FNA_ERASE_ALL_MASK) && ses != 0)
138 			errx(1, "controller does not support per-NS erase");
139 
140 		/* Try to keep previous namespace parameters. */
141 		read_namespace_data(fd, nsid, &nsd);
142 		if (lbaf < 0)
143 			lbaf = (nsd.flbas >> NVME_NS_DATA_FLBAS_FORMAT_SHIFT)
144 			    & NVME_NS_DATA_FLBAS_FORMAT_MASK;
145 		if (lbaf > nsd.nlbaf)
146 			errx(1, "LBA format is out of range");
147 		if (mset < 0)
148 			mset = (nsd.flbas >> NVME_NS_DATA_FLBAS_EXTENDED_SHIFT)
149 			    & NVME_NS_DATA_FLBAS_EXTENDED_MASK;
150 		if (pi < 0)
151 			pi = (nsd.dps >> NVME_NS_DATA_DPS_MD_START_SHIFT)
152 			    & NVME_NS_DATA_DPS_MD_START_MASK;
153 		if (pil < 0)
154 			pil = (nsd.dps >> NVME_NS_DATA_DPS_PIT_SHIFT)
155 			    & NVME_NS_DATA_DPS_PIT_MASK;
156 	} else {
157 
158 		/* We have no previous parameters, so default to zeroes. */
159 		if (lbaf < 0)
160 			lbaf = 0;
161 		if (mset < 0)
162 			mset = 0;
163 		if (pi < 0)
164 			pi = 0;
165 		if (pil < 0)
166 			pil = 0;
167 	}
168 
169 	memset(&pt, 0, sizeof(pt));
170 	pt.cmd.opc = NVME_OPC_FORMAT_NVM;
171 	pt.cmd.nsid = htole32(nsid);
172 	pt.cmd.cdw10 = htole32((ses << 9) + (pil << 8) + (pi << 5) +
173 	    (mset << 4) + lbaf);
174 
175 	if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0)
176 		err(1, "format request failed");
177 
178 	if (nvme_completion_is_error(&pt.cpl))
179 		errx(1, "format request returned error");
180 	close(fd);
181 	exit(0);
182 }
183 
184 NVME_COMMAND(top, format, format, FORMAT_USAGE);
185