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