Adding upstream version 2.8.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e08cb5ae68
commit
8229b972f0
407 changed files with 2341 additions and 926 deletions
|
@ -29,4 +29,7 @@ if json_c_dep.found()
|
|||
]
|
||||
subdir('solidigm')
|
||||
subdir('ocp')
|
||||
if conf.has('HAVE_SED_OPAL')
|
||||
subdir('sed')
|
||||
endif
|
||||
endif
|
||||
|
|
4
plugins/sed/meson.build
Normal file
4
plugins/sed/meson.build
Normal file
|
@ -0,0 +1,4 @@
|
|||
sources += [
|
||||
'plugins/sed/sed.c',
|
||||
'plugins/sed/sedopal_cmd.c',
|
||||
]
|
178
plugins/sed/sed.c
Normal file
178
plugins/sed/sed.c
Normal file
|
@ -0,0 +1,178 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
#include <linux/fs.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "nvme.h"
|
||||
#include "libnvme.h"
|
||||
#include "nvme-print.h"
|
||||
#include "sedopal_cmd.h"
|
||||
#include <linux/sed-opal.h>
|
||||
|
||||
#define CREATE_CMD
|
||||
#include "sed.h"
|
||||
|
||||
OPT_ARGS(no_opts) = {
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
OPT_ARGS(key_opts) = {
|
||||
OPT_FLAG("ask-key", 'k', &sedopal_ask_key,
|
||||
"prompt for SED authentication key"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
OPT_ARGS(revert_opts) = {
|
||||
OPT_FLAG("destructive", 'e', &sedopal_destructive_revert,
|
||||
"destructive revert"),
|
||||
OPT_FLAG("psid", 'p', &sedopal_psid_revert, "PSID revert"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Open the NVMe device specified on the command line. It must be the
|
||||
* NVMe block device (e.g. /dev/nvme0n1).
|
||||
*/
|
||||
static int sed_opal_open_device(struct nvme_dev **dev, int argc, char **argv,
|
||||
const char *desc, struct argconfig_commandline_options *opts)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = parse_and_open(dev, argc, argv, desc, opts);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!S_ISBLK((*dev)->direct.stat.st_mode)) {
|
||||
fprintf(stderr,
|
||||
"ERROR : The NVMe block device must be specified\n");
|
||||
err = -EINVAL;
|
||||
dev_close(*dev);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sed_opal_discover(int argc, char **argv, struct command *cmd,
|
||||
struct plugin *plugin)
|
||||
{
|
||||
int err;
|
||||
const char *desc = "Query SED device and display locking features";
|
||||
struct nvme_dev *dev;
|
||||
|
||||
err = sed_opal_open_device(&dev, argc, argv, desc, no_opts);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = sedopal_cmd_discover(dev->direct.fd);
|
||||
|
||||
dev_close(dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sed_opal_initialize(int argc, char **argv, struct command *cmd,
|
||||
struct plugin *plugin)
|
||||
{
|
||||
int err;
|
||||
const char *desc = "Initialize a SED device for locking";
|
||||
struct nvme_dev *dev;
|
||||
|
||||
err = sed_opal_open_device(&dev, argc, argv, desc, no_opts);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = sedopal_cmd_initialize(dev->direct.fd);
|
||||
if (err != 0)
|
||||
fprintf(stderr, "initialize: SED error - %s\n",
|
||||
sedopal_error_to_text(err));
|
||||
|
||||
dev_close(dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sed_opal_revert(int argc, char **argv, struct command *cmd,
|
||||
struct plugin *plugin)
|
||||
{
|
||||
int err;
|
||||
const char *desc = "Revert a SED device from locking state";
|
||||
struct nvme_dev *dev;
|
||||
|
||||
err = sed_opal_open_device(&dev, argc, argv, desc, revert_opts);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = sedopal_cmd_revert(dev->direct.fd);
|
||||
if (err != 0)
|
||||
fprintf(stderr, "revert: SED error - %s\n",
|
||||
sedopal_error_to_text(err));
|
||||
|
||||
dev_close(dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sed_opal_lock(int argc, char **argv, struct command *cmd,
|
||||
struct plugin *plugin)
|
||||
{
|
||||
int err;
|
||||
const char *desc = "Lock a SED device";
|
||||
struct nvme_dev *dev;
|
||||
|
||||
err = sed_opal_open_device(&dev, argc, argv, desc, key_opts);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = sedopal_cmd_lock(dev->direct.fd);
|
||||
if (err != 0)
|
||||
fprintf(stderr, "lock: SED error - %s\n",
|
||||
sedopal_error_to_text(err));
|
||||
|
||||
dev_close(dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sed_opal_unlock(int argc, char **argv, struct command *cmd,
|
||||
struct plugin *plugin)
|
||||
{
|
||||
int err;
|
||||
const char *desc = "Unlock a SED device";
|
||||
struct nvme_dev *dev;
|
||||
|
||||
err = sed_opal_open_device(&dev, argc, argv, desc, key_opts);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = sedopal_cmd_unlock(dev->direct.fd);
|
||||
if (err != 0)
|
||||
fprintf(stderr, "unlock: SED error - %s\n",
|
||||
sedopal_error_to_text(err));
|
||||
|
||||
dev_close(dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sed_opal_password(int argc, char **argv, struct command *cmd,
|
||||
struct plugin *plugin)
|
||||
{
|
||||
int err;
|
||||
const char *desc = "Change the locking password of a SED device";
|
||||
struct nvme_dev *dev;
|
||||
|
||||
err = sed_opal_open_device(&dev, argc, argv, desc, no_opts);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = sedopal_cmd_password(dev->direct.fd);
|
||||
if (err != 0)
|
||||
fprintf(stderr, "password: SED error - %s\n",
|
||||
sedopal_error_to_text(err));
|
||||
|
||||
dev_close(dev);
|
||||
return err;
|
||||
}
|
19
plugins/sed/sed.h
Normal file
19
plugins/sed/sed.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#undef CMD_INC_FILE
|
||||
#define CMD_INC_FILE plugins/sed/sed
|
||||
|
||||
#include "cmd.h"
|
||||
#include <linux/sed-opal.h>
|
||||
|
||||
PLUGIN(NAME("sed", "SED Opal Command Set", NVME_VERSION),
|
||||
COMMAND_LIST(
|
||||
ENTRY("discover", "Discover SED Opal Locking Features", sed_opal_discover, "1")
|
||||
ENTRY("initialize", "Initialize a SED Opal Device for locking", sed_opal_initialize)
|
||||
ENTRY("revert", "Revert a SED Opal Device from locking", sed_opal_revert)
|
||||
ENTRY("lock", "Lock a SED Opal Device", sed_opal_lock)
|
||||
ENTRY("unlock", "Unlock a SED Opal Device", sed_opal_unlock)
|
||||
ENTRY("password", "Change the SED Opal Device password", sed_opal_password)
|
||||
)
|
||||
);
|
||||
|
||||
#include "define_cmd.h"
|
512
plugins/sed/sedopal_cmd.c
Normal file
512
plugins/sed/sedopal_cmd.c
Normal file
|
@ -0,0 +1,512 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mount.h>
|
||||
#include <linux/sed-opal.h>
|
||||
#include "sedopal_spec.h"
|
||||
#include "sedopal_cmd.h"
|
||||
|
||||
/*
|
||||
* ask user for key rather than obtaining it from kernel keyring
|
||||
*/
|
||||
bool sedopal_ask_key;
|
||||
|
||||
/*
|
||||
* initiate dialog to ask for and confirm new password
|
||||
*/
|
||||
bool sedopal_ask_new_key;
|
||||
|
||||
/*
|
||||
* perform a destructive drive revert
|
||||
*/
|
||||
bool sedopal_destructive_revert;
|
||||
|
||||
/*
|
||||
* perform a PSID drive revert
|
||||
*/
|
||||
bool sedopal_psid_revert;
|
||||
|
||||
/*
|
||||
* Map method status codes to error text
|
||||
*/
|
||||
static const char * const sedopal_errors[] = {
|
||||
[SED_STATUS_SUCCESS] = "Success",
|
||||
[SED_STATUS_NOT_AUTHORIZED] = "Host Not Authorized",
|
||||
[SED_STATUS_OBSOLETE_1] = "Obsolete",
|
||||
[SED_STATUS_SP_BUSY] = "SP Session Busy",
|
||||
[SED_STATUS_SP_FAILED] = "SP Failed",
|
||||
[SED_STATUS_SP_DISABLED] = "SP Disabled",
|
||||
[SED_STATUS_SP_FROZEN] = "SP Frozen",
|
||||
[SED_STATUS_NO_SESSIONS_AVAILABLE] = "No Sessions Available",
|
||||
[SED_STATUS_UNIQUENESS_CONFLICT] = "Uniqueness Conflict",
|
||||
[SED_STATUS_INSUFFICIENT_SPACE] = "Insufficient Space",
|
||||
[SED_STATUS_INSUFFICIENT_ROWS] = "Insufficient Rows",
|
||||
[SED_STATUS_OBSOLETE_2] = "Obsolete",
|
||||
[SED_STATUS_INVALID_PARAMETER] = "Invalid Parameter",
|
||||
[SED_STATUS_OBSOLETE_3] = "Obsolete",
|
||||
[SED_STATUS_OBSOLETE_4] = "Obsolete",
|
||||
[SED_STATUS_TPER_MALFUNCTION] = "TPER Malfunction",
|
||||
[SED_STATUS_TRANSACTION_FAILURE] = "Transaction Failure",
|
||||
[SED_STATUS_RESPONSE_OVERFLOW] = "Response Overflow",
|
||||
[SED_STATUS_AUTHORITY_LOCKED_OUT] = "Authority Locked Out",
|
||||
};
|
||||
|
||||
const char *sedopal_error_to_text(int code)
|
||||
{
|
||||
if (code == SED_STATUS_FAIL)
|
||||
return "Failed";
|
||||
|
||||
if (code == SED_STATUS_NO_METHOD_STATUS)
|
||||
return "Method returned no status";
|
||||
|
||||
if (code < SED_STATUS_SUCCESS ||
|
||||
code > SED_STATUS_AUTHORITY_LOCKED_OUT)
|
||||
return("Unknown Error");
|
||||
|
||||
return sedopal_errors[code];
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a user entered password and do some basic validity checks.
|
||||
*/
|
||||
char *sedopal_get_password(char *prompt)
|
||||
{
|
||||
char *pass;
|
||||
int len;
|
||||
|
||||
pass = getpass(prompt);
|
||||
if (pass == NULL)
|
||||
return NULL;
|
||||
|
||||
len = strlen(pass);
|
||||
if (len < SEDOPAL_MIN_PASSWORD_LEN)
|
||||
return NULL;
|
||||
|
||||
if (len > SEDOPAL_MAX_PASSWORD_LEN)
|
||||
return NULL;
|
||||
|
||||
return pass;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize a SED Opal key. The key can either specify that the actual
|
||||
* key should be looked up in the kernel keyring, or it should be
|
||||
* populated in the key by prompting the user.
|
||||
*/
|
||||
int sedopal_set_key(struct opal_key *key)
|
||||
{
|
||||
#if !HAVE_KEY_TYPE
|
||||
/*
|
||||
* If key_type isn't avaialable, force key prompt
|
||||
*/
|
||||
sedopal_ask_key = true;
|
||||
#endif
|
||||
|
||||
if (sedopal_ask_key) {
|
||||
char *pass;
|
||||
char *prompt;
|
||||
|
||||
/*
|
||||
* set proper prompt
|
||||
*/
|
||||
if (sedopal_ask_new_key)
|
||||
prompt = SEDOPAL_NEW_PW_PROMPT;
|
||||
else {
|
||||
if (sedopal_psid_revert)
|
||||
prompt = SEDOPAL_PSID_PROMPT;
|
||||
else
|
||||
prompt = SEDOPAL_CURRENT_PW_PROMPT;
|
||||
}
|
||||
|
||||
pass = sedopal_get_password(prompt);
|
||||
if (pass == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
#if HAVE_KEY_TYPE
|
||||
key->key_type = OPAL_INCLUDED;
|
||||
#endif
|
||||
key->key_len = strlen(pass);
|
||||
memcpy(key->key, pass, key->key_len);
|
||||
|
||||
/*
|
||||
* If getting a new key, ask for it to be re-entered
|
||||
* and verify the two entries are the same.
|
||||
*/
|
||||
if (sedopal_ask_new_key) {
|
||||
pass = sedopal_get_password(SEDOPAL_REENTER_PW_PROMPT);
|
||||
if (strncmp((char *)key->key, pass, key->key_len)) {
|
||||
fprintf(stderr,
|
||||
"Error: passwords don't match\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#if HAVE_KEY_TYPE
|
||||
key->key_type = OPAL_KEYRING;
|
||||
#endif
|
||||
key->key_len = 0;
|
||||
}
|
||||
|
||||
key->lr = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare a drive for SED Opal locking.
|
||||
*/
|
||||
int sedopal_cmd_initialize(int fd)
|
||||
{
|
||||
int rc;
|
||||
struct opal_key key;
|
||||
struct opal_lr_act lr_act = {};
|
||||
struct opal_user_lr_setup lr_setup = {};
|
||||
|
||||
sedopal_ask_key = true;
|
||||
rc = sedopal_set_key(&key);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* take ownership of the device
|
||||
*/
|
||||
rc = ioctl(fd, IOC_OPAL_TAKE_OWNERSHIP, &key);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr,
|
||||
"Error: failed to take device ownership - %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* activate lsp
|
||||
*/
|
||||
lr_act.num_lrs = 1;
|
||||
lr_act.sum = false;
|
||||
lr_act.key = key;
|
||||
|
||||
rc = ioctl(fd, IOC_OPAL_ACTIVATE_LSP, &lr_act);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "Error: failed to activate LSP - %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* setup global locking range
|
||||
*/
|
||||
lr_setup.range_start = 0;
|
||||
lr_setup.range_length = 0;
|
||||
lr_setup.RLE = true;
|
||||
lr_setup.WLE = true;
|
||||
|
||||
lr_setup.session.opal_key = key;
|
||||
lr_setup.session.sum = 0;
|
||||
lr_setup.session.who = OPAL_ADMIN1;
|
||||
|
||||
rc = ioctl(fd, IOC_OPAL_LR_SETUP, &lr_setup);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr,
|
||||
"Error: failed to setup locking range - %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lock a SED Opal drive
|
||||
*/
|
||||
int sedopal_cmd_lock(int fd)
|
||||
{
|
||||
|
||||
return sedopal_lock_unlock(fd, OPAL_LK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlock a SED Opal drive
|
||||
*/
|
||||
int sedopal_cmd_unlock(int fd)
|
||||
{
|
||||
|
||||
return sedopal_lock_unlock(fd, OPAL_RW);
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare and issue an ioctl to lock/unlock a drive
|
||||
*/
|
||||
int sedopal_lock_unlock(int fd, int lock_state)
|
||||
{
|
||||
int rc;
|
||||
struct opal_lock_unlock opal_lu = {};
|
||||
|
||||
rc = sedopal_set_key(&opal_lu.session.opal_key);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
opal_lu.session.sum = 0;
|
||||
opal_lu.session.who = OPAL_ADMIN1;
|
||||
opal_lu.l_state = lock_state;
|
||||
|
||||
rc = ioctl(fd, IOC_OPAL_LOCK_UNLOCK, &opal_lu);
|
||||
if (rc != 0)
|
||||
fprintf(stderr,
|
||||
"Error: failed locking or unlocking - %d\n", rc);
|
||||
|
||||
/*
|
||||
* If the unlock was successful, force a re-read of the
|
||||
* partition table.
|
||||
*/
|
||||
if (rc == 0) {
|
||||
rc = ioctl(fd, BLKRRPART, 0);
|
||||
if (rc != 0)
|
||||
fprintf(stderr,
|
||||
"Error: failed re-reading partition\n");
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Confirm a destructive drive so that data is inadvertently erased
|
||||
*/
|
||||
static bool sedopal_confirm_revert(void)
|
||||
{
|
||||
int rc;
|
||||
char ans;
|
||||
bool confirmed = false;
|
||||
|
||||
/*
|
||||
* verify that destructive revert is really the intention
|
||||
*/
|
||||
fprintf(stdout,
|
||||
"Destructive revert erases drive data. Continue (y/n)? ");
|
||||
rc = fscanf(stdin, " %c", &ans);
|
||||
if ((rc == 1) && (ans == 'y' || ans == 'Y')) {
|
||||
fprintf(stdout, "Are you sure (y/n)? ");
|
||||
rc = fscanf(stdin, " %c", &ans);
|
||||
if ((rc == 1) && (ans == 'y' || ans == 'Y'))
|
||||
confirmed = true;
|
||||
}
|
||||
|
||||
return confirmed;
|
||||
}
|
||||
|
||||
/*
|
||||
* perform a destructive drive revert
|
||||
*/
|
||||
static int sedopal_revert_destructive(int fd)
|
||||
{
|
||||
struct opal_key key;
|
||||
int rc;
|
||||
|
||||
if (!sedopal_confirm_revert()) {
|
||||
fprintf(stderr, "Aborting destructive revert\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* for destructive revert, require that key is provided
|
||||
*/
|
||||
sedopal_ask_key = true;
|
||||
|
||||
rc = sedopal_set_key(&key);
|
||||
if (rc == 0)
|
||||
rc = ioctl(fd, IOC_OPAL_REVERT_TPR, &key);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* perform a PSID drive revert
|
||||
*/
|
||||
static int sedopal_revert_psid(int fd)
|
||||
{
|
||||
#ifdef IOC_OPAL_PSID_REVERT_TPR
|
||||
struct opal_key key;
|
||||
int rc;
|
||||
|
||||
if (!sedopal_confirm_revert()) {
|
||||
fprintf(stderr, "Aborting PSID revert\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = sedopal_set_key(&key);
|
||||
if (rc == 0) {
|
||||
rc = ioctl(fd, IOC_OPAL_PSID_REVERT_TPR, &key);
|
||||
if (rc != 0)
|
||||
fprintf(stderr, "PSID_REVERT_TPR rc %d\n", rc);
|
||||
}
|
||||
|
||||
return rc;
|
||||
#else
|
||||
fprintf(stderr, "ERROR : PSID revert is not supported\n");
|
||||
return -EOPNOTSUPP;
|
||||
#endif /* IOC_OPAL_PSID_REVERT_TPR */
|
||||
}
|
||||
|
||||
/*
|
||||
* revert a drive from the provisioned state to a state where locking
|
||||
* is disabled.
|
||||
*/
|
||||
int sedopal_cmd_revert(int fd)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* for revert, require that key/PSID is provided
|
||||
*/
|
||||
sedopal_ask_key = true;
|
||||
|
||||
if (sedopal_psid_revert) {
|
||||
rc = sedopal_revert_psid(fd);
|
||||
} else if (sedopal_destructive_revert) {
|
||||
rc = sedopal_revert_destructive(fd);
|
||||
} else {
|
||||
#ifdef IOC_OPAL_REVERT_LSP
|
||||
struct opal_revert_lsp revert_lsp;
|
||||
|
||||
rc = sedopal_set_key(&revert_lsp.key);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
revert_lsp.options = OPAL_PRESERVE;
|
||||
revert_lsp.__pad = 0;
|
||||
|
||||
rc = ioctl(fd, IOC_OPAL_REVERT_LSP, &revert_lsp);
|
||||
#else
|
||||
rc = -EOPNOTSUPP;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (rc != 0)
|
||||
fprintf(stderr, "Error: failed reverting drive - %d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the password of a drive. The existing password must be
|
||||
* provided and the new password is confirmed by re-entry.
|
||||
*/
|
||||
int sedopal_cmd_password(int fd)
|
||||
{
|
||||
int rc;
|
||||
struct opal_new_pw new_pw = {};
|
||||
|
||||
new_pw.new_user_pw.who = OPAL_ADMIN1;
|
||||
new_pw.new_user_pw.opal_key.lr = 0;
|
||||
new_pw.session.who = OPAL_ADMIN1;
|
||||
new_pw.session.sum = 0;
|
||||
new_pw.session.opal_key.lr = 0;
|
||||
|
||||
/*
|
||||
* get current key
|
||||
*/
|
||||
sedopal_ask_key = true;
|
||||
if (sedopal_set_key(&new_pw.session.opal_key) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* get new key
|
||||
*/
|
||||
sedopal_ask_new_key = true;
|
||||
if (sedopal_set_key(&new_pw.new_user_pw.opal_key) != 0)
|
||||
return -EINVAL;
|
||||
|
||||
rc = ioctl(fd, IOC_OPAL_SET_PW, &new_pw);
|
||||
if (rc != 0)
|
||||
fprintf(stderr, "Error: failed setting password - %d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print the state of locking features.
|
||||
*/
|
||||
void sedopal_print_locking_features(uint8_t features)
|
||||
{
|
||||
printf("Locking Features:\n");
|
||||
printf("\tLocking Supported: %s\n",
|
||||
(features & OPAL_FEATURE_LOCKING_SUPPORTED) ? "Yes" : "No");
|
||||
printf("\tLocking Feature Enabled: %s\n",
|
||||
(features & OPAL_FEATURE_LOCKING_ENABLED) ? "Yes" : "No");
|
||||
printf("\tLocked: %s\n",
|
||||
(features & OPAL_FEATURE_LOCKED) ? "Yes" : "No");
|
||||
}
|
||||
|
||||
/*
|
||||
* Query a drive to determine if it's SED Opal capable and
|
||||
* it's current locking status.
|
||||
*/
|
||||
int sedopal_cmd_discover(int fd)
|
||||
{
|
||||
#ifdef IOC_OPAL_DISCOVERY
|
||||
int rc;
|
||||
bool sedopal_locking_supported = false;
|
||||
struct opal_discovery discover;
|
||||
struct level_0_discovery_header *dh;
|
||||
struct level_0_discovery_features *feat;
|
||||
struct level_0_discovery_features *feat_end;
|
||||
uint16_t code;
|
||||
uint8_t locking_flags;
|
||||
char buf[4096];
|
||||
|
||||
discover.data = (__u64)buf;
|
||||
discover.size = sizeof(buf);
|
||||
|
||||
rc = ioctl(fd, IOC_OPAL_DISCOVERY, &discover);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Error: ioctl IOC_OPAL_DISCOVERY failed\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* The returned buffer contains a level 0 discovery header
|
||||
* folowed by an array of level 0 feature records.
|
||||
*
|
||||
* TCG Opal Specification v2.0.2 section 3.1.1
|
||||
*/
|
||||
dh = (struct level_0_discovery_header *)buf;
|
||||
feat = (struct level_0_discovery_features *)(dh + 1);
|
||||
feat_end = (struct level_0_discovery_features *)
|
||||
(buf + be32toh(dh->parameter_length));
|
||||
|
||||
/*
|
||||
* iterate through all the features that were returned
|
||||
*/
|
||||
while (feat < feat_end) {
|
||||
code = be16toh(feat->code);
|
||||
switch (code) {
|
||||
case OPAL_FEATURE_CODE_LOCKING:
|
||||
locking_flags = feat->feature;
|
||||
break;
|
||||
case OPAL_FEATURE_CODE_OPALV2:
|
||||
sedopal_locking_supported = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
feat++;
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
if (!sedopal_locking_supported) {
|
||||
fprintf(stderr, "Error: device does not support SED Opal\n");
|
||||
rc = -1;
|
||||
} else
|
||||
sedopal_print_locking_features(locking_flags);
|
||||
|
||||
return rc;
|
||||
#else /* IOC_OPAL_DISCOVERY */
|
||||
fprintf(stderr, "ERROR : NVMe device discovery is not supported\n");
|
||||
return -EOPNOTSUPP;
|
||||
#endif
|
||||
}
|
55
plugins/sed/sedopal_cmd.h
Normal file
55
plugins/sed/sedopal_cmd.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifndef _SED_OPAL_CMD_H
|
||||
#define _SED_OPAL_CMD_H
|
||||
|
||||
#define SEDOPAL_CURRENT_PW_PROMPT "Password: "
|
||||
#define SEDOPAL_NEW_PW_PROMPT "New Password: "
|
||||
#define SEDOPAL_REENTER_PW_PROMPT "Re-enter New Password: "
|
||||
#define SEDOPAL_PSID_PROMPT "PSID: "
|
||||
|
||||
#define SEDOPAL_MIN_PASSWORD_LEN 8
|
||||
#define SEDOPAL_MAX_PASSWORD_LEN 32
|
||||
|
||||
#define NVME_DEV_PATH "/dev/nvme"
|
||||
|
||||
extern bool sedopal_ask_key;
|
||||
extern bool sedopal_ask_new_key;
|
||||
extern bool sedopal_destructive_revert;
|
||||
extern bool sedopal_psid_revert;
|
||||
|
||||
/*
|
||||
* Sub-commands supported by the sedopal command
|
||||
*/
|
||||
enum sedopal_cmds {
|
||||
SEDOPAL_CMD_NOT_SPECIFIED = -1,
|
||||
SEDOPAL_CMD_INITIALIZE = 0,
|
||||
SEDOPAL_CMD_LOCK = 1,
|
||||
SEDOPAL_CMD_UNLOCK = 2,
|
||||
SEDOPAL_CMD_REVERT = 3,
|
||||
SEDOPAL_CMD_PASSWORD = 4,
|
||||
SEDOPAL_CMD_DISCOVER = 5,
|
||||
};
|
||||
|
||||
struct cmd_table {
|
||||
int (*cmd_handler)(int fd);
|
||||
};
|
||||
|
||||
/*
|
||||
* command handlers
|
||||
*/
|
||||
int sedopal_cmd_initialize(int fd);
|
||||
int sedopal_cmd_lock(int fd);
|
||||
int sedopal_cmd_unlock(int fd);
|
||||
int sedopal_cmd_revert(int fd);
|
||||
int sedopal_cmd_password(int fd);
|
||||
int sedopal_cmd_discover(int fd);
|
||||
|
||||
/*
|
||||
* utility functions
|
||||
*/
|
||||
int sedopal_open_nvme_device(char *device);
|
||||
int sedopal_lock_unlock(int fd, int lock_state);
|
||||
const char *sedopal_error_to_text(int code);
|
||||
|
||||
#endif /* _SED_OPAL_CMD_H */
|
71
plugins/sed/sedopal_spec.h
Normal file
71
plugins/sed/sedopal_spec.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifndef _SED_OPAL_SPEC_H
|
||||
#define _SED_OPAL_SPEC_H
|
||||
|
||||
/*
|
||||
* TCP Storage Architecture Core Specification Version 2.01
|
||||
* section 5.1.5 Method Status Codes
|
||||
*/
|
||||
enum sed_status_codes {
|
||||
SED_STATUS_SUCCESS = 0x00,
|
||||
SED_STATUS_NOT_AUTHORIZED = 0x01,
|
||||
SED_STATUS_OBSOLETE_1 = 0x02,
|
||||
SED_STATUS_SP_BUSY = 0x03,
|
||||
SED_STATUS_SP_FAILED = 0x04,
|
||||
SED_STATUS_SP_DISABLED = 0x05,
|
||||
SED_STATUS_SP_FROZEN = 0x06,
|
||||
SED_STATUS_NO_SESSIONS_AVAILABLE = 0x07,
|
||||
SED_STATUS_UNIQUENESS_CONFLICT = 0x08,
|
||||
SED_STATUS_INSUFFICIENT_SPACE = 0x09,
|
||||
SED_STATUS_INSUFFICIENT_ROWS = 0x0A,
|
||||
SED_STATUS_OBSOLETE_2 = 0x0B,
|
||||
SED_STATUS_INVALID_PARAMETER = 0x0C,
|
||||
SED_STATUS_OBSOLETE_3 = 0x0D,
|
||||
SED_STATUS_OBSOLETE_4 = 0x0E,
|
||||
SED_STATUS_TPER_MALFUNCTION = 0x0F,
|
||||
SED_STATUS_TRANSACTION_FAILURE = 0x10,
|
||||
SED_STATUS_RESPONSE_OVERFLOW = 0x11,
|
||||
SED_STATUS_AUTHORITY_LOCKED_OUT = 0x12,
|
||||
SED_STATUS_FAIL = 0x3F,
|
||||
SED_STATUS_NO_METHOD_STATUS = 0x89,
|
||||
};
|
||||
|
||||
/*
|
||||
* Definitions from TCG Opal Specification v2.0.2
|
||||
*/
|
||||
|
||||
/*
|
||||
* level 0 feature codes - section 3.1.1
|
||||
*/
|
||||
#define OPAL_FEATURE_CODE_LOCKING 0x0002
|
||||
#define OPAL_FEATURE_CODE_OPALV2 0x0203
|
||||
|
||||
/* locking features */
|
||||
#define OPAL_FEATURE_LOCKING_SUPPORTED 0x01
|
||||
#define OPAL_FEATURE_LOCKING_ENABLED 0x02
|
||||
#define OPAL_FEATURE_LOCKED 0x04
|
||||
|
||||
|
||||
/*
|
||||
* discovery header as specified in section 3.1.1.1
|
||||
*/
|
||||
struct level_0_discovery_header {
|
||||
uint32_t parameter_length;
|
||||
uint32_t revision;
|
||||
uint64_t reserved;
|
||||
uint8_t vendor_specific[32];
|
||||
};
|
||||
|
||||
/*
|
||||
* level 0 features as specified in section 3.1.1.3
|
||||
*/
|
||||
struct level_0_discovery_features {
|
||||
uint16_t code;
|
||||
uint8_t version;
|
||||
uint8_t length;
|
||||
uint8_t feature;
|
||||
uint8_t reserved[11];
|
||||
};
|
||||
|
||||
#endif /* _SED_OPAL_SPEC_H */
|
|
@ -13,6 +13,7 @@
|
|||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
#include <linux/limits.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "nvme.h"
|
||||
|
@ -122,7 +123,7 @@ struct nlog_dump_header4_1 {
|
|||
|
||||
struct config {
|
||||
__u32 namespace_id;
|
||||
char *file_prefix;
|
||||
char *dir_prefix;
|
||||
char *type;
|
||||
bool verbose;
|
||||
};
|
||||
|
@ -222,6 +223,7 @@ static int dump_assert_logs(struct nvme_dev *dev, struct config cfg)
|
|||
__u8 buf[INTERNAL_LOG_MAX_BYTE_TRANSFER];
|
||||
__u8 head_buf[INTERNAL_LOG_MAX_BYTE_TRANSFER];
|
||||
char file_path[PATH_MAX];
|
||||
char file_name[] = "AssertLog.bin";
|
||||
struct assert_dump_header *ad = (struct assert_dump_header *) head_buf;
|
||||
struct nvme_passthru_cmd cmd = {
|
||||
.opcode = 0xd2,
|
||||
|
@ -236,7 +238,8 @@ static int dump_assert_logs(struct nvme_dev *dev, struct config cfg)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
sprintf(file_path, "%s_AssertLog.bin", cfg.file_prefix);
|
||||
snprintf(file_path, sizeof(file_path), "%.*s/%s",
|
||||
(int) (sizeof(file_path) - sizeof(file_name) - 1), cfg.dir_prefix, file_name);
|
||||
output = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
||||
if (output < 0)
|
||||
return -errno;
|
||||
|
@ -291,7 +294,7 @@ static int dump_event_logs(struct nvme_dev *dev, struct config cfg)
|
|||
err = read_header(&cmd, dev_fd(dev));
|
||||
if (err)
|
||||
return err;
|
||||
sprintf(file_path, "%s_EventLog.bin", cfg.file_prefix);
|
||||
snprintf(file_path, sizeof(file_path), "%s/EventLog.bin", cfg.dir_prefix);
|
||||
output = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
||||
if (output < 0)
|
||||
return -errno;
|
||||
|
@ -387,7 +390,8 @@ static int dump_nlogs(struct nvme_dev *dev, struct config cfg, int core)
|
|||
count = nlog_header->totalnlogs;
|
||||
core_num = core < 0 ? nlog_header->corecount : 0;
|
||||
if (!header_size) {
|
||||
sprintf(file_path, "%s_NLog.bin", cfg.file_prefix);
|
||||
snprintf(file_path, sizeof(file_path), "%s/NLog.bin",
|
||||
cfg.dir_prefix);
|
||||
output = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
||||
if (output < 0)
|
||||
return -errno;
|
||||
|
@ -423,25 +427,32 @@ enum telemetry_type {
|
|||
|
||||
static int dump_telemetry(struct nvme_dev *dev, struct config cfg, enum telemetry_type ttype)
|
||||
{
|
||||
struct nvme_telemetry_log *log = NULL;
|
||||
_cleanup_free_ struct nvme_telemetry_log *log = NULL;
|
||||
size_t log_size = 0;
|
||||
int err = 0, output;
|
||||
int err = 0;
|
||||
__u8 *buffer = NULL;
|
||||
size_t bytes_remaining = 0;
|
||||
enum nvme_telemetry_da da;
|
||||
size_t max_data_tx;
|
||||
char file_path[PATH_MAX];
|
||||
char *log_name;
|
||||
char *file_name;
|
||||
char *log_descr;
|
||||
struct stat sb;
|
||||
|
||||
_cleanup_file_ int output = -1;
|
||||
|
||||
switch (ttype) {
|
||||
case HOSTGENNEW:
|
||||
log_name = "TelemetryHostGenNew";
|
||||
file_name = "lid_0x07_lsp_0x01_lsi_0x0000.bin";
|
||||
log_descr = "Generated Host Initiated";
|
||||
break;
|
||||
case HOSTGENOLD:
|
||||
log_name = "TelemetryHostGenOld";
|
||||
file_name = "lid_0x07_lsp_0x00_lsi_0x0000.bin";
|
||||
log_descr = "Existing Host Initiated";
|
||||
break;
|
||||
case CONTROLLER:
|
||||
log_name = "TelemetryController";
|
||||
file_name = "lid_0x08_lsp_0x00_lsi_0x0000.bin";
|
||||
log_descr = "Controller Initiated";
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -453,11 +464,6 @@ static int dump_telemetry(struct nvme_dev *dev, struct config cfg, enum telemetr
|
|||
if (max_data_tx > DRIVER_MAX_TX_256K)
|
||||
max_data_tx = DRIVER_MAX_TX_256K;
|
||||
|
||||
sprintf(file_path, "%s_%s.bin", cfg.file_prefix, log_name);
|
||||
output = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if (output < 0)
|
||||
return -errno;
|
||||
|
||||
switch (ttype) {
|
||||
case HOSTGENNEW:
|
||||
err = nvme_get_telemetry_log(dev_fd(dev), true, false, false, max_data_tx, da,
|
||||
|
@ -474,7 +480,20 @@ static int dump_telemetry(struct nvme_dev *dev, struct config cfg, enum telemetr
|
|||
}
|
||||
|
||||
if (err)
|
||||
goto tele_close_output;
|
||||
return err;
|
||||
|
||||
snprintf(file_path, sizeof(file_path), "%s/log_pages", cfg.dir_prefix);
|
||||
if (!(stat(file_path, &sb) == 0 && S_ISDIR(sb.st_mode))) {
|
||||
if (mkdir(file_path, 777) != 0) {
|
||||
perror(file_path);
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(file_path, sizeof(file_path), "%s/log_pages/%s", cfg.dir_prefix, file_name);
|
||||
output = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if (output < 0)
|
||||
return -errno;
|
||||
|
||||
bytes_remaining = log_size;
|
||||
buffer = (__u8 *)log;
|
||||
|
@ -486,45 +505,49 @@ static int dump_telemetry(struct nvme_dev *dev, struct config cfg, enum telemetr
|
|||
err = -errno;
|
||||
goto tele_close_output;
|
||||
}
|
||||
|
||||
bytes_remaining -= bytes_written;
|
||||
buffer += bytes_written;
|
||||
}
|
||||
printf("Successfully wrote log to %s\n", file_path);
|
||||
printf("Successfully wrote %s Telemetry log to %s\n", log_descr, file_path);
|
||||
|
||||
tele_close_output:
|
||||
free(log);
|
||||
close(output);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int solidigm_get_internal_log(int argc, char **argv, struct command *command,
|
||||
struct plugin *plugin)
|
||||
{
|
||||
char folder[PATH_MAX];
|
||||
char zip_name[PATH_MAX];
|
||||
char *output_path;
|
||||
char sn_prefix[sizeof(((struct nvme_id_ctrl *)0)->sn)+1];
|
||||
int log_count = 0;
|
||||
int err;
|
||||
struct nvme_dev *dev;
|
||||
_cleanup_nvme_dev_ struct nvme_dev *dev = NULL;
|
||||
bool all = false;
|
||||
time_t t;
|
||||
struct tm tm;
|
||||
|
||||
const char *desc = "Get Debug Firmware Logs and save them.";
|
||||
const char *type =
|
||||
"Log type: ALL, CONTROLLERINITTELEMETRY, HOSTINITTELEMETRY, HOSTINITTELEMETRYNOGEN, NLOG, ASSERT, EVENT. Defaults to ALL.";
|
||||
const char *prefix = "Output file prefix; defaults to device serial number.";
|
||||
const char *prefix = "Output dir prefix; defaults to device serial number.";
|
||||
const char *verbose = "To print out verbose info.";
|
||||
const char *namespace_id = "Namespace to get logs from.";
|
||||
|
||||
|
||||
struct config cfg = {
|
||||
.namespace_id = NVME_NSID_ALL,
|
||||
.file_prefix = NULL,
|
||||
.dir_prefix = NULL,
|
||||
.type = NULL,
|
||||
};
|
||||
|
||||
OPT_ARGS(opts) = {
|
||||
OPT_STR("type", 't', &cfg.type, type),
|
||||
OPT_UINT("namespace-id", 'n', &cfg.namespace_id, namespace_id),
|
||||
OPT_FILE("file-prefix", 'p', &cfg.file_prefix, prefix),
|
||||
OPT_FILE("dir-prefix", 'p', &cfg.dir_prefix, prefix),
|
||||
OPT_FLAG("verbose", 'v', &cfg.verbose, verbose),
|
||||
OPT_END()
|
||||
};
|
||||
|
@ -533,12 +556,22 @@ int solidigm_get_internal_log(int argc, char **argv, struct command *command,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
if (!cfg.file_prefix) {
|
||||
if (!cfg.dir_prefix) {
|
||||
err = get_serial_number(sn_prefix, dev_fd(dev));
|
||||
if (err)
|
||||
goto out_dev;
|
||||
cfg.file_prefix = sn_prefix;
|
||||
return err;
|
||||
cfg.dir_prefix = sn_prefix;
|
||||
}
|
||||
t = time(NULL);
|
||||
tm = *localtime(&t);
|
||||
snprintf(folder, sizeof(folder), "%s-%d%02d%02d%02d%02d%02d", cfg.dir_prefix,
|
||||
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
|
||||
if (mkdir(folder, 0777) != 0) {
|
||||
perror("mkdir");
|
||||
return -errno;
|
||||
}
|
||||
cfg.dir_prefix = folder;
|
||||
output_path = folder;
|
||||
|
||||
if (!cfg.type)
|
||||
cfg.type = "ALL";
|
||||
|
@ -547,8 +580,9 @@ int solidigm_get_internal_log(int argc, char **argv, struct command *command,
|
|||
*p = toupper(*p);
|
||||
}
|
||||
|
||||
if (!strcmp(cfg.type, "ALL"))
|
||||
if (!strcmp(cfg.type, "ALL")) {
|
||||
all = true;
|
||||
}
|
||||
if (all || !strcmp(cfg.type, "ASSERT")) {
|
||||
err = dump_assert_logs(dev, cfg);
|
||||
if (err == 0)
|
||||
|
@ -592,14 +626,32 @@ int solidigm_get_internal_log(int argc, char **argv, struct command *command,
|
|||
perror("Error retrieving Telemetry Host Initiated");
|
||||
}
|
||||
|
||||
if (log_count > 0) {
|
||||
int ret_cmd;
|
||||
char cmd[ARG_MAX];
|
||||
char *where_err = cfg.verbose ? "" : ">/dev/null 2>&1";
|
||||
|
||||
snprintf(zip_name, sizeof(zip_name), "%s.zip", cfg.dir_prefix);
|
||||
snprintf(cmd, sizeof(cmd), "cd \"%s\" && zip -r \"../%s\" ./* %s", cfg.dir_prefix,
|
||||
zip_name, where_err);
|
||||
printf("Compressing logs to %s\n", zip_name);
|
||||
ret_cmd = system(cmd);
|
||||
if (ret_cmd == -1)
|
||||
perror(cmd);
|
||||
else {
|
||||
output_path = zip_name;
|
||||
snprintf(cmd, sizeof(cmd), "rm -rf %s", cfg.dir_prefix);
|
||||
printf("Removing %s\n", cfg.dir_prefix);
|
||||
if (system(cmd) != 0)
|
||||
perror("Failed removing logs folder");
|
||||
}
|
||||
}
|
||||
|
||||
if (log_count == 0) {
|
||||
if (err > 0)
|
||||
nvme_show_status(err);
|
||||
} else if ((log_count > 1) || cfg.verbose)
|
||||
printf("Total: %d log files with prefix: %s\n", log_count, cfg.file_prefix);
|
||||
out_dev:
|
||||
/* Redundant close() to make static code analysis happy */
|
||||
close(dev->direct.fd);
|
||||
dev_close(dev);
|
||||
printf("Total: %d log files in %s\n", log_count, output_path);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
#include "cmd.h"
|
||||
|
||||
#define SOLIDIGM_PLUGIN_VERSION "1.0"
|
||||
#define SOLIDIGM_PLUGIN_VERSION "1.1"
|
||||
|
||||
PLUGIN(NAME("solidigm", "Solidigm vendor specific extensions", SOLIDIGM_PLUGIN_VERSION),
|
||||
COMMAND_LIST(
|
||||
|
|
|
@ -94,10 +94,40 @@
|
|||
#define WDC_NVME_SN520_DEV_ID 0x5003
|
||||
#define WDC_NVME_SN520_DEV_ID_1 0x5004
|
||||
#define WDC_NVME_SN520_DEV_ID_2 0x5005
|
||||
#define WDC_NVME_SN530_DEV_ID 0x5009
|
||||
#define WDC_NVME_SN530_DEV_ID_1 0x501d
|
||||
|
||||
#define WDC_NVME_SN530_DEV_ID_1 0x5007
|
||||
#define WDC_NVME_SN530_DEV_ID_2 0x5008
|
||||
#define WDC_NVME_SN530_DEV_ID_3 0x5009
|
||||
#define WDC_NVME_SN530_DEV_ID_4 0x500b
|
||||
#define WDC_NVME_SN530_DEV_ID_5 0x501d
|
||||
|
||||
#define WDC_NVME_SN350_DEV_ID 0x5019
|
||||
|
||||
#define WDC_NVME_SN570_DEV_ID 0x501A
|
||||
|
||||
#define WDC_NVME_SN850X_DEV_ID 0x5030
|
||||
|
||||
#define WDC_NVME_SN5000_DEV_ID_1 0x5034
|
||||
#define WDC_NVME_SN5000_DEV_ID_2 0x5035
|
||||
#define WDC_NVME_SN5000_DEV_ID_3 0x5036
|
||||
#define WDC_NVME_SN5000_DEV_ID_4 0x504A
|
||||
|
||||
#define WDC_NVME_SN7000S_DEV_ID_1 0x5039
|
||||
|
||||
#define WDC_NVME_SN7150_DEV_ID_1 0x503b
|
||||
#define WDC_NVME_SN7150_DEV_ID_2 0x503c
|
||||
#define WDC_NVME_SN7150_DEV_ID_3 0x503d
|
||||
#define WDC_NVME_SN7150_DEV_ID_4 0x503e
|
||||
#define WDC_NVME_SN7150_DEV_ID_5 0x503f
|
||||
|
||||
#define WDC_NVME_SN7100_DEV_ID_1 0x5043
|
||||
#define WDC_NVME_SN7100_DEV_ID_2 0x5044
|
||||
#define WDC_NVME_SN7100_DEV_ID_3 0x5045
|
||||
|
||||
#define WDC_NVME_SN8000S_DEV_ID 0x5049
|
||||
|
||||
#define WDC_NVME_SN720_DEV_ID 0x5002
|
||||
#define WDC_NVME_SN730A_DEV_ID 0x5006
|
||||
#define WDC_NVME_SN730_DEV_ID 0x5006
|
||||
#define WDC_NVME_SN740_DEV_ID 0x5015
|
||||
#define WDC_NVME_SN740_DEV_ID_1 0x5016
|
||||
#define WDC_NVME_SN740_DEV_ID_2 0x5017
|
||||
|
@ -989,7 +1019,7 @@ struct wdc_e6_log_hdr {
|
|||
/* DUI log header */
|
||||
struct wdc_dui_log_section {
|
||||
__le16 section_type;
|
||||
__le16 data_area_id;
|
||||
__le16 reserved;
|
||||
__le32 section_size;
|
||||
};
|
||||
|
||||
|
@ -1671,6 +1701,7 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev)
|
|||
WDC_DRIVE_CAP_DRIVE_LOG | WDC_DRIVE_CAP_CRASH_DUMP | WDC_DRIVE_CAP_PFAIL_DUMP |
|
||||
WDC_DRIVE_CAP_PURGE);
|
||||
break;
|
||||
|
||||
case WDC_NVME_SN200_DEV_ID:
|
||||
capabilities = (WDC_DRIVE_CAP_CAP_DIAG | WDC_DRIVE_CAP_INTERNAL_LOG | WDC_DRIVE_CAP_CLEAR_PCIE |
|
||||
WDC_DRIVE_CAP_DRIVE_LOG | WDC_DRIVE_CAP_CRASH_DUMP | WDC_DRIVE_CAP_PFAIL_DUMP |
|
||||
|
@ -1686,10 +1717,12 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev)
|
|||
WDC_NVME_ADD_LOG_OPCODE))
|
||||
capabilities |= WDC_DRIVE_CAP_C1_LOG_PAGE;
|
||||
break;
|
||||
|
||||
default:
|
||||
capabilities = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case WDC_NVME_VID_2:
|
||||
switch (read_device_id) {
|
||||
case WDC_NVME_SN630_DEV_ID:
|
||||
|
@ -1708,6 +1741,7 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev)
|
|||
WDC_NVME_GET_VU_SMART_LOG_OPCODE))
|
||||
capabilities |= WDC_DRIVE_CAP_D0_LOG_PAGE;
|
||||
break;
|
||||
|
||||
case WDC_NVME_SN640_DEV_ID:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN640_DEV_ID_1:
|
||||
|
@ -1780,6 +1814,7 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev)
|
|||
capabilities |= (WDC_DRIVE_CAP_CLEAR_FW_ACT_HISTORY | WDC_DRIVE_CAP_CLEAR_PCIE);
|
||||
|
||||
break;
|
||||
|
||||
case WDC_NVME_SN840_DEV_ID:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN840_DEV_ID_1:
|
||||
|
@ -1810,6 +1845,7 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev)
|
|||
WDC_NVME_GET_VU_SMART_LOG_OPCODE))
|
||||
capabilities |= WDC_DRIVE_CAP_D0_LOG_PAGE;
|
||||
break;
|
||||
|
||||
case WDC_NVME_SN650_DEV_ID:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN650_DEV_ID_1:
|
||||
|
@ -1869,6 +1905,7 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev)
|
|||
WDC_DRIVE_CAP_CLEAR_PCIE);
|
||||
|
||||
break;
|
||||
|
||||
case WDC_NVME_SN861_DEV_ID:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN861_DEV_ID_1:
|
||||
|
@ -1886,28 +1923,28 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev)
|
|||
WDC_DRIVE_CAP_LOG_PAGE_DIR |
|
||||
WDC_DRIVE_CAP_SET_LATENCY_MONITOR);
|
||||
break;
|
||||
|
||||
default:
|
||||
capabilities = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case WDC_NVME_SNDK_VID:
|
||||
switch (read_device_id) {
|
||||
case WDC_NVME_SXSLCL_DEV_ID:
|
||||
capabilities = WDC_DRIVE_CAP_DRIVE_ESSENTIALS;
|
||||
break;
|
||||
|
||||
case WDC_NVME_SN520_DEV_ID:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN520_DEV_ID_1:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN520_DEV_ID_2:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN530_DEV_ID:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN530_DEV_ID_1:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN810_DEV_ID:
|
||||
capabilities = WDC_DRIVE_CAP_DUI_DATA;
|
||||
break;
|
||||
|
||||
case WDC_NVME_SN820CL_DEV_ID:
|
||||
capabilities = WDC_DRIVE_CAP_DUI_DATA |
|
||||
WDC_DRIVE_CAP_CLOUD_BOOT_SSD_VERSION |
|
||||
|
@ -1916,15 +1953,62 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev)
|
|||
WDC_DRIVE_CAP_VU_FID_CLEAR_PCIE | WDC_DRIVE_CAP_NAND_STATS |
|
||||
WDC_DRIVE_CAP_DEVICE_WAF | WDC_DRIVE_CAP_TEMP_STATS;
|
||||
break;
|
||||
|
||||
case WDC_NVME_SN720_DEV_ID:
|
||||
capabilities = WDC_DRIVE_CAP_DUI_DATA | WDC_DRIVE_CAP_NAND_STATS |
|
||||
WDC_DRIVE_CAP_NS_RESIZE;
|
||||
break;
|
||||
case WDC_NVME_SN730A_DEV_ID:
|
||||
|
||||
case WDC_NVME_SN730_DEV_ID:
|
||||
capabilities = WDC_DRIVE_CAP_DUI | WDC_DRIVE_CAP_NAND_STATS |
|
||||
WDC_DRIVE_CAP_INFO | WDC_DRIVE_CAP_TEMP_STATS |
|
||||
WDC_DRIVE_CAP_VUC_CLEAR_PCIE | WDC_DRIVE_CAP_PCIE_STATS;
|
||||
break;
|
||||
|
||||
case WDC_NVME_SN530_DEV_ID_1:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN530_DEV_ID_2:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN530_DEV_ID_3:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN530_DEV_ID_4:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN530_DEV_ID_5:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN350_DEV_ID:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN570_DEV_ID:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN850X_DEV_ID:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN5000_DEV_ID_1:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN5000_DEV_ID_2:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN5000_DEV_ID_3:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN5000_DEV_ID_4:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN7000S_DEV_ID_1:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN7150_DEV_ID_1:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN7150_DEV_ID_2:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN7150_DEV_ID_3:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN7150_DEV_ID_4:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN7150_DEV_ID_5:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN7100_DEV_ID_1:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN7100_DEV_ID_2:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN7100_DEV_ID_3:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN8000S_DEV_ID:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN740_DEV_ID:
|
||||
fallthrough;
|
||||
case WDC_NVME_SN740_DEV_ID_1:
|
||||
|
@ -1936,6 +2020,7 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev)
|
|||
case WDC_NVME_SN340_DEV_ID:
|
||||
capabilities = WDC_DRIVE_CAP_DUI;
|
||||
break;
|
||||
|
||||
case WDC_NVME_ZN350_DEV_ID:
|
||||
fallthrough;
|
||||
case WDC_NVME_ZN350_DEV_ID_1:
|
||||
|
@ -1945,6 +2030,7 @@ static __u64 wdc_get_drive_capabilities(nvme_root_t r, struct nvme_dev *dev)
|
|||
WDC_DRIVE_CAP_FW_ACTIVATE_HISTORY_C2 | WDC_DRIVE_CAP_INFO |
|
||||
WDC_DRIVE_CAP_CLOUD_SSD_VERSION | WDC_DRIVE_CAP_LOG_PAGE_DIR;
|
||||
break;
|
||||
|
||||
default:
|
||||
capabilities = 0;
|
||||
}
|
||||
|
@ -2632,6 +2718,16 @@ static int wdc_do_dump_e6(int fd, __u32 opcode, __u32 data_len,
|
|||
int i;
|
||||
struct nvme_passthru_cmd admin_cmd;
|
||||
|
||||
/* if data_len is not 4 byte aligned */
|
||||
if (data_len & 0x00000003) {
|
||||
/* Round down to the next 4 byte aligned value */
|
||||
fprintf(stderr, "%s: INFO: data_len 0x%x not 4 byte aligned.\n",
|
||||
__func__, data_len);
|
||||
fprintf(stderr, "%s: INFO: Round down to 0x%x.\n",
|
||||
__func__, (data_len &= 0xFFFFFFFC));
|
||||
data_len &= 0xFFFFFFFC;
|
||||
}
|
||||
|
||||
dump_data = (__u8 *)malloc(sizeof(__u8) * data_len);
|
||||
|
||||
if (!dump_data) {
|
||||
|
@ -2649,7 +2745,8 @@ static int wdc_do_dump_e6(int fd, __u32 opcode, __u32 data_len,
|
|||
admin_cmd.opcode = opcode;
|
||||
admin_cmd.cdw12 = cdw12;
|
||||
|
||||
log_size = data_len;
|
||||
/* subtract off the header size since that was already copied into the buffer */
|
||||
log_size = (data_len - curr_data_offset);
|
||||
while (log_size > 0) {
|
||||
xfer_size = min(xfer_size, log_size);
|
||||
|
||||
|
@ -2890,6 +2987,8 @@ static int wdc_do_cap_dui_v1(int fd, char *file, __u32 xfer_size, int data_area,
|
|||
fprintf(stderr, "INFO: WDC: Capture V1 Device Unit Info log, data area = %d\n",
|
||||
data_area);
|
||||
fprintf(stderr, "INFO: WDC: DUI Header Version = 0x%x\n", log_hdr->hdr_version);
|
||||
fprintf(stderr, "INFO: WDC: DUI section count = 0x%x\n", log_hdr->section_count);
|
||||
fprintf(stderr, "INFO: WDC: DUI log size = 0x%x\n", log_hdr->log_size);
|
||||
}
|
||||
|
||||
if (!cap_dui_length) {
|
||||
|
@ -2899,23 +2998,15 @@ static int wdc_do_cap_dui_v1(int fd, char *file, __u32 xfer_size, int data_area,
|
|||
|
||||
/* parse log header for all sections up to specified data area inclusively */
|
||||
if (data_area != WDC_NVME_DUI_MAX_DATA_AREA) {
|
||||
for (j = 0; j < WDC_NVME_DUI_MAX_SECTION; j++) {
|
||||
if (log_hdr->log_section[j].data_area_id <= data_area &&
|
||||
log_hdr->log_section[j].data_area_id) {
|
||||
log_size += log_hdr->log_section[j].section_size;
|
||||
if (verbose)
|
||||
fprintf(stderr,
|
||||
"%s: Data area ID %d : section size 0x%x, total size = 0x%x\n",
|
||||
__func__, log_hdr->log_section[j].data_area_id,
|
||||
(unsigned int)log_hdr->log_section[j].section_size,
|
||||
(unsigned int)log_size);
|
||||
for (j = 0; j < log_hdr->section_count; j++) {
|
||||
log_size += log_hdr->log_section[j].section_size;
|
||||
if (verbose)
|
||||
fprintf(stderr,
|
||||
"%s: section size 0x%x, total size = 0x%x\n",
|
||||
__func__,
|
||||
(unsigned int)log_hdr->log_section[j].section_size,
|
||||
(unsigned int)log_size);
|
||||
|
||||
} else {
|
||||
if (verbose)
|
||||
fprintf(stderr, "%s: break, total size = 0x%x\n", __func__,
|
||||
(unsigned int)log_size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log_size = cap_dui_length;
|
||||
|
@ -8112,7 +8203,7 @@ static int wdc_vs_smart_add_log(int argc, char **argv, struct command *command,
|
|||
struct ocp_cloud_smart_log log;
|
||||
char buf[2 * sizeof(log.log_page_guid) + 3];
|
||||
|
||||
ret = validate_output_format(output_format, &fmt);
|
||||
ret = validate_output_format(cfg.output_format, &fmt);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Invalid output format: %s\n", cfg.output_format);
|
||||
goto out;
|
||||
|
@ -11496,7 +11587,7 @@ static int wdc_vs_drive_info(int argc, char **argv,
|
|||
}
|
||||
}
|
||||
break;
|
||||
case WDC_NVME_SN730A_DEV_ID:
|
||||
case WDC_NVME_SN730_DEV_ID:
|
||||
memcpy(vsData, &ctrl.vs[0], 32);
|
||||
|
||||
major_rev = ctrl.sn[12];
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#if !defined(WDC_NVME) || defined(CMD_HEADER_MULTI_READ)
|
||||
#define WDC_NVME
|
||||
|
||||
#define WDC_PLUGIN_VERSION "2.3.5"
|
||||
#define WDC_PLUGIN_VERSION "2.7.0"
|
||||
#include "cmd.h"
|
||||
|
||||
PLUGIN(NAME("wdc", "Western Digital vendor specific extensions", WDC_PLUGIN_VERSION),
|
||||
|
|
|
@ -192,15 +192,5 @@ bool wdc_CheckUuidListSupport(struct nvme_dev *dev, struct nvme_id_uuid_list *uu
|
|||
|
||||
bool wdc_UuidEqual(struct nvme_id_uuid_list_entry *entry1, struct nvme_id_uuid_list_entry *entry2)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
if (entry1->uuid[i] != entry2->uuid[i])
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == 16)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
return !memcmp(entry1, entry2, NVME_UUID_LEN);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue