203 lines
5.8 KiB
C
203 lines
5.8 KiB
C
|
// SPDX-License-Identifier: GPL-2.0-only
|
||
|
/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
|
||
|
#include <linux/libnvdimm.h>
|
||
|
#include <asm/unaligned.h>
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/async.h>
|
||
|
#include <linux/slab.h>
|
||
|
#include <linux/memregion.h>
|
||
|
#include "cxlmem.h"
|
||
|
#include "cxl.h"
|
||
|
|
||
|
static unsigned long cxl_pmem_get_security_flags(struct nvdimm *nvdimm,
|
||
|
enum nvdimm_passphrase_type ptype)
|
||
|
{
|
||
|
struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
|
||
|
struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
|
||
|
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
||
|
unsigned long security_flags = 0;
|
||
|
struct cxl_get_security_output {
|
||
|
__le32 flags;
|
||
|
} out;
|
||
|
struct cxl_mbox_cmd mbox_cmd;
|
||
|
u32 sec_out;
|
||
|
int rc;
|
||
|
|
||
|
mbox_cmd = (struct cxl_mbox_cmd) {
|
||
|
.opcode = CXL_MBOX_OP_GET_SECURITY_STATE,
|
||
|
.size_out = sizeof(out),
|
||
|
.payload_out = &out,
|
||
|
};
|
||
|
|
||
|
rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
|
||
|
if (rc < 0)
|
||
|
return 0;
|
||
|
|
||
|
sec_out = le32_to_cpu(out.flags);
|
||
|
if (ptype == NVDIMM_MASTER) {
|
||
|
if (sec_out & CXL_PMEM_SEC_STATE_MASTER_PASS_SET)
|
||
|
set_bit(NVDIMM_SECURITY_UNLOCKED, &security_flags);
|
||
|
else
|
||
|
set_bit(NVDIMM_SECURITY_DISABLED, &security_flags);
|
||
|
if (sec_out & CXL_PMEM_SEC_STATE_MASTER_PLIMIT)
|
||
|
set_bit(NVDIMM_SECURITY_FROZEN, &security_flags);
|
||
|
return security_flags;
|
||
|
}
|
||
|
|
||
|
if (sec_out & CXL_PMEM_SEC_STATE_USER_PASS_SET) {
|
||
|
if (sec_out & CXL_PMEM_SEC_STATE_FROZEN ||
|
||
|
sec_out & CXL_PMEM_SEC_STATE_USER_PLIMIT)
|
||
|
set_bit(NVDIMM_SECURITY_FROZEN, &security_flags);
|
||
|
|
||
|
if (sec_out & CXL_PMEM_SEC_STATE_LOCKED)
|
||
|
set_bit(NVDIMM_SECURITY_LOCKED, &security_flags);
|
||
|
else
|
||
|
set_bit(NVDIMM_SECURITY_UNLOCKED, &security_flags);
|
||
|
} else {
|
||
|
set_bit(NVDIMM_SECURITY_DISABLED, &security_flags);
|
||
|
}
|
||
|
|
||
|
return security_flags;
|
||
|
}
|
||
|
|
||
|
static int cxl_pmem_security_change_key(struct nvdimm *nvdimm,
|
||
|
const struct nvdimm_key_data *old_data,
|
||
|
const struct nvdimm_key_data *new_data,
|
||
|
enum nvdimm_passphrase_type ptype)
|
||
|
{
|
||
|
struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
|
||
|
struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
|
||
|
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
||
|
struct cxl_mbox_cmd mbox_cmd;
|
||
|
struct cxl_set_pass set_pass;
|
||
|
|
||
|
set_pass = (struct cxl_set_pass) {
|
||
|
.type = ptype == NVDIMM_MASTER ? CXL_PMEM_SEC_PASS_MASTER :
|
||
|
CXL_PMEM_SEC_PASS_USER,
|
||
|
};
|
||
|
memcpy(set_pass.old_pass, old_data->data, NVDIMM_PASSPHRASE_LEN);
|
||
|
memcpy(set_pass.new_pass, new_data->data, NVDIMM_PASSPHRASE_LEN);
|
||
|
|
||
|
mbox_cmd = (struct cxl_mbox_cmd) {
|
||
|
.opcode = CXL_MBOX_OP_SET_PASSPHRASE,
|
||
|
.size_in = sizeof(set_pass),
|
||
|
.payload_in = &set_pass,
|
||
|
};
|
||
|
|
||
|
return cxl_internal_send_cmd(cxlds, &mbox_cmd);
|
||
|
}
|
||
|
|
||
|
static int __cxl_pmem_security_disable(struct nvdimm *nvdimm,
|
||
|
const struct nvdimm_key_data *key_data,
|
||
|
enum nvdimm_passphrase_type ptype)
|
||
|
{
|
||
|
struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
|
||
|
struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
|
||
|
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
||
|
struct cxl_disable_pass dis_pass;
|
||
|
struct cxl_mbox_cmd mbox_cmd;
|
||
|
|
||
|
dis_pass = (struct cxl_disable_pass) {
|
||
|
.type = ptype == NVDIMM_MASTER ? CXL_PMEM_SEC_PASS_MASTER :
|
||
|
CXL_PMEM_SEC_PASS_USER,
|
||
|
};
|
||
|
memcpy(dis_pass.pass, key_data->data, NVDIMM_PASSPHRASE_LEN);
|
||
|
|
||
|
mbox_cmd = (struct cxl_mbox_cmd) {
|
||
|
.opcode = CXL_MBOX_OP_DISABLE_PASSPHRASE,
|
||
|
.size_in = sizeof(dis_pass),
|
||
|
.payload_in = &dis_pass,
|
||
|
};
|
||
|
|
||
|
return cxl_internal_send_cmd(cxlds, &mbox_cmd);
|
||
|
}
|
||
|
|
||
|
static int cxl_pmem_security_disable(struct nvdimm *nvdimm,
|
||
|
const struct nvdimm_key_data *key_data)
|
||
|
{
|
||
|
return __cxl_pmem_security_disable(nvdimm, key_data, NVDIMM_USER);
|
||
|
}
|
||
|
|
||
|
static int cxl_pmem_security_disable_master(struct nvdimm *nvdimm,
|
||
|
const struct nvdimm_key_data *key_data)
|
||
|
{
|
||
|
return __cxl_pmem_security_disable(nvdimm, key_data, NVDIMM_MASTER);
|
||
|
}
|
||
|
|
||
|
static int cxl_pmem_security_freeze(struct nvdimm *nvdimm)
|
||
|
{
|
||
|
struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
|
||
|
struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
|
||
|
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
||
|
struct cxl_mbox_cmd mbox_cmd = {
|
||
|
.opcode = CXL_MBOX_OP_FREEZE_SECURITY,
|
||
|
};
|
||
|
|
||
|
return cxl_internal_send_cmd(cxlds, &mbox_cmd);
|
||
|
}
|
||
|
|
||
|
static int cxl_pmem_security_unlock(struct nvdimm *nvdimm,
|
||
|
const struct nvdimm_key_data *key_data)
|
||
|
{
|
||
|
struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
|
||
|
struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
|
||
|
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
||
|
u8 pass[NVDIMM_PASSPHRASE_LEN];
|
||
|
struct cxl_mbox_cmd mbox_cmd;
|
||
|
int rc;
|
||
|
|
||
|
memcpy(pass, key_data->data, NVDIMM_PASSPHRASE_LEN);
|
||
|
mbox_cmd = (struct cxl_mbox_cmd) {
|
||
|
.opcode = CXL_MBOX_OP_UNLOCK,
|
||
|
.size_in = NVDIMM_PASSPHRASE_LEN,
|
||
|
.payload_in = pass,
|
||
|
};
|
||
|
|
||
|
rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
|
||
|
if (rc < 0)
|
||
|
return rc;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int cxl_pmem_security_passphrase_erase(struct nvdimm *nvdimm,
|
||
|
const struct nvdimm_key_data *key,
|
||
|
enum nvdimm_passphrase_type ptype)
|
||
|
{
|
||
|
struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
|
||
|
struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
|
||
|
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
||
|
struct cxl_mbox_cmd mbox_cmd;
|
||
|
struct cxl_pass_erase erase;
|
||
|
int rc;
|
||
|
|
||
|
erase = (struct cxl_pass_erase) {
|
||
|
.type = ptype == NVDIMM_MASTER ? CXL_PMEM_SEC_PASS_MASTER :
|
||
|
CXL_PMEM_SEC_PASS_USER,
|
||
|
};
|
||
|
memcpy(erase.pass, key->data, NVDIMM_PASSPHRASE_LEN);
|
||
|
mbox_cmd = (struct cxl_mbox_cmd) {
|
||
|
.opcode = CXL_MBOX_OP_PASSPHRASE_SECURE_ERASE,
|
||
|
.size_in = sizeof(erase),
|
||
|
.payload_in = &erase,
|
||
|
};
|
||
|
|
||
|
rc = cxl_internal_send_cmd(cxlds, &mbox_cmd);
|
||
|
if (rc < 0)
|
||
|
return rc;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static const struct nvdimm_security_ops __cxl_security_ops = {
|
||
|
.get_flags = cxl_pmem_get_security_flags,
|
||
|
.change_key = cxl_pmem_security_change_key,
|
||
|
.disable = cxl_pmem_security_disable,
|
||
|
.freeze = cxl_pmem_security_freeze,
|
||
|
.unlock = cxl_pmem_security_unlock,
|
||
|
.erase = cxl_pmem_security_passphrase_erase,
|
||
|
.disable_master = cxl_pmem_security_disable_master,
|
||
|
};
|
||
|
|
||
|
const struct nvdimm_security_ops *cxl_security_ops = &__cxl_security_ops;
|