// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * NVM-Express command line utility.
 *
 * Copyright (c) 2014-2015, Intel Corporation.
 *
 * Written by Keith Busch <kbusch@kernel.org>
 */

#include <libnvme.h>

#include <shared/compiler-attributes-util.h>

#include "cleanup.h"
#include "global-ctx.h"
#include "nvme-print.h"
#include "plugin.h"

static int lockdown_cmd(int argc, char **argv, struct command *acmd, struct plugin *plugin)
{
	const char *desc = "The Lockdown command is used to control the\n"
		"Command and Feature Lockdown capability which configures the\n"
		"prohibition or allowance of execution of the specified command\n"
		"or Set Features command targeting a specific Feature Identifier.";
	const char *ofi_desc = "Opcode or Feature Identifier (OFI)\n"
		"specifies the command opcode or Set Features Feature Identifier\n"
		"identified by the Scope field.";
	const char *ifc_desc =
	    "[0-3] Interface (INF) field identifies the interfaces affected by this command.";
	const char *prhbt_desc = "[0-1]Prohibit(PRHBT) bit specifies whether\n"
		"to prohibit or allow the command opcode or Set Features Feature\n"
		"Identifier specified by this command.";
	const char *scp_desc =
	    "[0-15]Scope(SCP) field specifies the contents of the Opcode or Feature Identifier field.";
	const char *uuid_desc = "UUID Index - If this field is set to a non-zero\n"
		"value, then the value of this field is the index of a UUID in the UUID\n"
		"List that is used by the command.If this field is cleared to 0h,\n"
		"then no UUID index is specified";
	const char *csel_desc = "[0,1,2,15] Controller Select (CSEL) specifies the\n"
		"controller(s) affected by this command: 0 = NVM subsystem,\n"
		"1 = specific controller (see --css), 2 = secondary controllers\n"
		"for a specific primary controller (see --css), 15 = vendor specific.";
	const char *css_desc = "Controller Select Specific (CSS) - meaning depends\n"
		"on --csel: the Controller Identifier if --csel=1, or the Primary\n"
		"Controller Identifier if --csel=2. Ignored if --csel=0.";

	__cleanup_nvme_global_ctx struct libnvme_global_ctx *ctx = NULL;
	__cleanup_nvme_transport_handle struct libnvme_transport_handle *hdl = NULL;
	struct libnvme_passthru_cmd cmd;
	int err = -1;

	struct config {
		__u8	ofi;
		__u8	ifc;
		__u8	prhbt;
		__u8	scp;
		__u8	uuid;
		__u8	csel;
		__u16	css;
	};

	struct config cfg = {
		.ofi	= 0,
		.ifc	= 0,
		.prhbt	= 0,
		.scp	= 0,
		.uuid	= 0,
		.csel	= 0,
		.css	= 0,
	};

	NVME_ARGS(opts,
		  OPT_BYTE("ofi",	'O', &cfg.ofi,      ofi_desc),
		  OPT_BYTE("ifc",	'f', &cfg.ifc,      ifc_desc),
		  OPT_BYTE("prhbt",	'p', &cfg.prhbt,    prhbt_desc),
		  OPT_BYTE("scp",	's', &cfg.scp,      scp_desc),
		  OPT_BYTE("uuid",	'U', &cfg.uuid,     uuid_desc),
		  OPT_BYTE("csel",	'c', &cfg.csel,     csel_desc),
		  OPT_SHRT("css",	'C', &cfg.css,      css_desc));

	err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
	if (err)
		return err;

	/* check for input argument limit */
	if (cfg.ifc > 3) {
		nvme_show_error("invalid interface settings:%d", cfg.ifc);
		return -1;
	}
	if (cfg.prhbt > 1) {
		nvme_show_error("invalid prohibit settings:%d", cfg.prhbt);
		return -1;
	}
	if (cfg.scp > 15) {
		nvme_show_error("invalid scope settings:%d", cfg.scp);
		return -1;
	}
	if (cfg.uuid > 127) {
		nvme_show_error("invalid UUID index settings:%d", cfg.uuid);
		return -1;
	}
	if (cfg.csel != NVME_LOCKDOWN_CSEL_NVM_SUBSYSTEM &&
	    cfg.csel != NVME_LOCKDOWN_CSEL_SPECIFIC_CTRL &&
	    cfg.csel != NVME_LOCKDOWN_CSEL_SECONDARY_CTRLS &&
	    cfg.csel != NVME_LOCKDOWN_CSEL_VENDOR_SPECIFIC) {
		nvme_show_error("invalid controller select settings:%d", cfg.csel);
		return -1;
	}

	nvme_init_lockdown(&cmd, cfg.scp, cfg.prhbt, cfg.ifc, cfg.ofi,
			   cfg.uuid, cfg.csel, cfg.css);
	err = libnvme_exec_admin_passthru(hdl, &cmd);
	if (err) {
		nvme_show_err(err, "lockdown");
		return err;
	}

	nvme_show_verbose_result("Lockdown Command is Successful");

	return err;
}

static struct command lockdown_cmd_cmd = {
	.name = "lockdown",
	.help = "Submit a Lockdown command,return result",
	.fn = lockdown_cmd,
};

static struct command *commands[] = {
	&lockdown_cmd_cmd,
	NULL,
};

static void __shr_constructor register_group(void)
{
	plugin_add_group(&builtin, "Security & Access Control", commands);
}
