Index: sbin/nvmecontrol/Makefile =================================================================== --- sbin/nvmecontrol/Makefile +++ sbin/nvmecontrol/Makefile @@ -3,7 +3,7 @@ PACKAGE=runtime PROG= nvmecontrol SRCS= nvmecontrol.c devlist.c firmware.c identify.c logpage.c \ - perftest.c reset.c nvme_util.c power.c util.c wdc.c + perftest.c reset.c nvme_util.c power.c util.c wdc.c format.c MAN= nvmecontrol.8 .PATH: ${SRCTOP}/sys/dev/nvme Index: sbin/nvmecontrol/format.c =================================================================== --- /dev/null +++ sbin/nvmecontrol/format.c @@ -0,0 +1,185 @@ + + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nvmecontrol.h" + +/* The LBAF field is 4 bits */ +#define MAX_LBAF 15 +/* The PI field is 3 bits */ +#define MAX_PI 7 +/* The SES field is 3 bits */ +#define MAX_SES 7 + +static void +format_usage(void) +{ + fprintf(stderr, "usage:\n"); + fprintf(stderr, FORMAT_USAGE); + exit(1); +} + +static int +format_nvm(int fd, uint32_t nsid, uint8_t lbaf, uint8_t mset, uint8_t pi, + uint8_t pil, uint8_t ses) +{ + struct nvme_pt_command pt; + + lbaf &= 0xf; + mset &= 0x1; + pi &= 0x7; + pil &= 0x1; + ses &= 0x7; + + memset(&pt, 0, sizeof(pt)); + pt.cmd.opc = NVME_OPC_FORMAT_NVM; + pt.cmd.nsid = nsid; + pt.cmd.cdw10 = lbaf | (mset << 4) | (pi << 5) | (pil << 8) | (ses << 9); + pt.is_read = 0; + + if (ioctl(fd, NVME_PASSTHROUGH_CMD, &pt) < 0) + err(1, "format request failed"); + + if (nvme_completion_is_error(&pt.cpl)) + errx(1, "format request returned error"); + + return 0; +} + +void +format(int argc, char *argv[]) +{ + int fd = -1; + char ch, *p; + uint8_t lbaf = 0, mset = 1, + pi = 0, pil = 0, + ses = 0; + uint32_t nsid = 0; + char path[64]; + struct nvme_controller_data cdata; + struct nvme_namespace_data nsdata; + + while ((ch = getopt(argc, argv, "f:m:p:l:s:")) != -1) { + switch (ch) { + case 'f': + lbaf = strtol(optarg, &p, 0); + if (p != NULL && *p != '\0') { + fprintf(stderr, + "\"%s\" not valid LBAF.\n", + optarg); + format_usage(); + } else if (lbaf > MAX_LBAF) { + fprintf(stderr, + "LBAF %u out of range (max=%u).\n", + lbaf, MAX_LBAF); + format_usage(); + } + break; + case 'm': + mset = strtol(optarg, &p, 0); + if (p != NULL && *p != '\0') { + fprintf(stderr, + "\"%s\" not valid MSET.\n", + optarg); + format_usage(); + } else if (mset > 1) { + fprintf(stderr, + "MSET %u out of range (max=1).\n", + mset); + format_usage(); + } + break; + case 'p': + pi = strtol(optarg, &p, 0); + if (p != NULL && *p != '\0') { + fprintf(stderr, + "\"%s\" not valid PI.\n", + optarg); + format_usage(); + } else if (pi > MAX_PI) { + fprintf(stderr, + "PI %u out of range (max=%u).\n", + pi, MAX_PI); + format_usage(); + } + break; + case 'l': + pil = strtol(optarg, &p, 0); + if (p != NULL && *p != '\0') { + fprintf(stderr, + "\"%s\" not valid PIL.\n", + optarg); + format_usage(); + } else if (pil > 1) { + fprintf(stderr, + "PIL %u out of range (max=1).\n", + pil); + format_usage(); + } + break; + case 's': + ses = strtol(optarg, &p, 0); + if (p != NULL && *p != '\0') { + fprintf(stderr, + "\"%s\" not valid SES.\n", + optarg); + format_usage(); + } else if (ses > MAX_SES) { + fprintf(stderr, + "SES %u out of range (max=%u).\n", + ses, MAX_SES); + } + break; + } + } + + /* Check that a namespace (and not a controller) was specified. */ + if (optind >= argc || strstr(argv[optind], NVME_NS_PREFIX) == NULL) { + printf("expecting NS optind=%d argc=%d %s\n", optind, argc, argv[optind]); + format_usage(); + } + + parse_ns_str(argv[optind], path, &nsid); + open_dev(path, &fd, 1, 1); + read_controller_data(fd, &cdata); + read_namespace_data(fd, nsid, &nsdata); + + if (cdata.oacs.format == 0) { + errx(1, "controller does not support format"); + } + +#define FNA_SUPPORTS_CRYPTO_ERASE(fna) (((fna) & 0x4) == 0x4) +#define SES_CRYPTO_ERASE 0x2 + + if ((ses == SES_CRYPTO_ERASE) && + !FNA_SUPPORTS_CRYPTO_ERASE(cdata.fna)) { + errx(1, "controller does not support cryptographic erase"); + } + + if (lbaf > nsdata.nlbaf) { + errx(1, "LBAF %u out of controller range (max=%u).", lbaf, nsdata.nlbaf); + } + + format_nvm(fd, nsid, lbaf, mset, pi, pil, ses); + + close(fd); + + exit(0); +} Index: sbin/nvmecontrol/nvmecontrol.h =================================================================== --- sbin/nvmecontrol/nvmecontrol.h +++ sbin/nvmecontrol/nvmecontrol.h @@ -69,6 +69,9 @@ #define WDC_USAGE \ " nvmecontrol wdc (cap-diag|drive-log|get-crash-dump|purge|purge-montior)\n" +#define FORMAT_USAGE \ +" nvmecontrol format [-f LBAF] [-m MSET] [-p PI] [-l PIL] [-s SES] \n" + void devlist(int argc, char *argv[]); void identify(int argc, char *argv[]); void perftest(int argc, char *argv[]); @@ -77,6 +80,7 @@ void firmware(int argc, char *argv[]); void power(int argc, char *argv[]); void wdc(int argc, char *argv[]); +void format(int argc, char *argv[]); int open_dev(const char *str, int *fd, int show_error, int exit_on_error); void parse_ns_str(const char *ns_str, char *ctrlr_str, int *nsid); Index: sbin/nvmecontrol/nvmecontrol.8 =================================================================== --- sbin/nvmecontrol/nvmecontrol.8 +++ sbin/nvmecontrol/nvmecontrol.8 @@ -95,6 +95,13 @@ .\" .Nm .\" .Ic wdc purge-monitor .\" .Aq device id +.Nm +.Ic format +.Op Fl f Ar LBAF +.Op Fl m Ar MSET +.Op Fl p Ar PI +.Op Fl s Ar SES +.Aq namespace id .Sh DESCRIPTION NVM Express (NVMe) is a storage protocol standard, for SSDs and other high-speed storage devices over PCI Express. @@ -127,6 +134,22 @@ by .bin. These logs must be sent to the vendor for analysis. This tool only provides a way to extract them. +.Ss format +The format command allows a low level format of the NVM media. Each of the +options mirror the field names defined in the NVM Express specification. +.Fl f +chooses the LBA Format. Valid values are 0-15. +.Fl m +chooses whether metadata is appended to the block (extended LBA) or sent +in a separate buffer. +.Fl p +selects the end-to-end protection information (Type 0 - 3). +.Fl l +selects whether the protection information is first (1) or last (0) in the +metadata. +.Fl s +selects the type of low level format (none = 0, user erase = 1, cryptographic +erase = 2). .Sh EXAMPLES .Dl nvmecontrol devlist .Pp Index: sbin/nvmecontrol/nvmecontrol.c =================================================================== --- sbin/nvmecontrol/nvmecontrol.c +++ sbin/nvmecontrol/nvmecontrol.c @@ -55,6 +55,7 @@ {"firmware", firmware, FIRMWARE_USAGE}, {"power", power, POWER_USAGE}, {"wdc", wdc, WDC_USAGE}, + {"format", format, FORMAT_USAGE}, {NULL, NULL, NULL}, };