blob: 0ee8779805f83387d330478c44f53c97ca5cddbc [file] [edit]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Pixel-Specific UFS feature support
*
* Copyright 2021 Google LLC
*
* Authors: Konstantin Vyshetsky <[email protected]>
*
*/
#include <asm/unaligned.h>
#include <crypto/aes.h>
#include <crypto/algapi.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <scsi/scsi_proto.h>
#include "ufs-exynos-gs.h"
#include "ufs-pixel-fips.h"
#include "ufs-pixel-fips_sha256.h"
#undef CREATE_TRACE_POINTS
#include <trace/hooks/ufshcd.h>
#undef pr_fmt
#define pr_fmt(fmt) "ufs-pixel-fips140: " fmt
/*
* FIPS140-3 requires module name and version information to be available. These
* values will be logged to kernel log upon loading.
*/
#define UFS_PIXEL_FIPS140_MODULE_NAME "UFS Pixel FIPS CMVP Module"
#define UFS_PIXEL_FIPS140_MODULE_VERSION "2.0.0"
/*
* As the verification logic will run before GPT data is available, module
* params are passed to cmdline from bootloader
*/
static u32 fips_first_lba;
module_param(fips_first_lba, uint, 0444);
MODULE_PARM_DESC(fips_first_lba, "First LBA of FIPS partition");
static u32 fips_last_lba;
module_param(fips_last_lba, uint, 0444);
MODULE_PARM_DESC(fips_last_lba, "Last LBA of FIPS partition");
static u32 fips_lu;
module_param(fips_lu, uint, 0444);
MODULE_PARM_DESC(fips_lu, "FIPS partition LUN");
static bool use_hw_keys = true;
module_param(use_hw_keys, bool, 0444);
MODULE_PARM_DESC(
use_hw_keys,
"Sets operating mode to be either hardware or software keys mode");
#define UFS_PIXEL_UCD_SIZE (4096)
#define UFS_PIXEL_BUFFER_SIZE (4096)
#define UFS_PIXEL_CRYPTO_DATA_UNIT_SIZE (4096)
#define UFS_PIXEL_MASTER_KEY_INDEX (15)
#define UTRD_CMD_TYPE_UFS_STORAGE (1 << 28)
#define UTRD_DD_SYSTEM_TO_DEVICE (1 << 25) /* Write */
#define UTRD_DD_DEVICE_TO_SYSTEM (1 << 26) /* Read */
#define UTRD_CRYPTO_DISABLE (0)
#define UTRD_CRYPTO_ENABLE (1 << 23)
#define PRDT_FAS_XTS (2 << 28) /* File Algorithm Selector */
#define PRDT_FKL_256 (1 << 26) /* File Key Length */
#define SENSE_DATA_ALLOC_LEN (18)
#define UPIU_TT_COMMAND (1)
#define IO_COMPLETION_TIMEOUT_MS (200)
#define IO_RETRY_COUNT (25)
#define ENCKEY_NUM_WORDS (8)
#define TWKEY_NUM_WORDS (8)
#define MESSAGE_LENGTH (32)
#define ISE_VERSION_REG_OFFSET (0x1C)
#define ISE_VERSION_MAJOR(x) (((x) >> 16) & 0xFF)
#define ISE_VERSION_MINOR(x) (((x) >> 8) & 0xFF)
#define ISE_VERSION_REVISION(x) ((x) & 0xFF)
struct fips_buffer_info {
void *io_buffer;
struct utp_transfer_cmd_desc *ucd_addr;
dma_addr_t io_buffer_dma_addr;
dma_addr_t ucd_dma_addr;
union {
const u8 *key;
u32 mki;
};
const u8 *iv;
};
struct pixel_ufs_prdt_entry {
/* The first four fields correspond to those of ufshcd_sg_entry. */
__le32 des0;
__le32 des1;
__le32 des2;
/*
* The crypto enable bit and keyslot are configured in the high bits of
* des3, whose low bits already contain ufshcd_sg_entry::size.
*/
#define CRYPTO_ENABLE (1U << 31)
#define CRYPTO_KEYSLOT(keyslot) ((keyslot) << 18)
__le32 des3;
/* The IV with all bytes reversed */
__be64 iv[2];
/*
* The key with all bytes reversed. For XTS, the two
* halves of the key are given separately and are
* byte-reversed separately.
*/
__be32 file_enckey[ENCKEY_NUM_WORDS];
__be32 file_twkey[TWKEY_NUM_WORDS];
/* Unused */
__le32 reserved[8];
};
struct sense_data {
u8 data[SENSE_DATA_ALLOC_LEN];
};
struct scsi_cdb {
u8 op_code;
u8 dword1;
__be32 lba;
u8 dword6;
__be16 transfer_len;
u8 dword9;
} __packed;
struct upiu_header {
/* DWORD 0 */
u8 transaction_type;
u8 flags;
u8 lun;
u8 task_tag;
/* DWORD 1 */
u8 rsvd; // 4 bit initiator_id + 4 bit cmd_set_type
u8 function;
u8 response;
u8 status;
/* DWORD 2 */
u8 total_ehs_len;
u8 device_info;
__be16 data_segment_len;
/* DWORD 3 - 7 */
struct {
__be32 edtl; // Expected Data Transfer Length
struct scsi_cdb cdb; // SCSI Command Descriptor Block
};
} __packed;
struct upiu {
struct upiu_header header;
__be16 sense_data_length;
struct sense_data sense_data;
} __packed;
static int ise_available;
/* Configure inline encryption (or decryption) on requests that require it. */
static void ufs_pixel_fips_crypto_fill_prdt_hw_key_mode(void *unused,
struct ufs_hba *hba,
struct ufshcd_lrb *lrbp,
unsigned int segments,
int *err)
{
struct pixel_ufs_prdt_entry *prdt =
(struct pixel_ufs_prdt_entry *)lrbp->ucd_prdt_ptr;
unsigned int i;
/*
* There's nothing to do for unencrypted requests, since the "crypto
* enable" bit is already 0 by default, as it's in the same word as
* ufshcd_sg_entry::size which was already initialized.
*/
if (lrbp->crypto_key_slot < 0)
return;
/*
* FIPS140-3 prohibits access to encryption services if they did not
* pass the algorithm self test. Trigger a panic in such an event.
*/
if (!ise_available)
panic("ISE encryption services are disabled\n");
/* Configure encryption on each segment of the request. */
for (i = 0; i < segments; i++) {
struct pixel_ufs_prdt_entry *ent = &prdt[i];
struct ufshcd_sg_entry *prd = (struct ufshcd_sg_entry *)ent;
/* Each segment must be exactly one data unit. */
if (le32_to_cpu(prd->size) + 1 != UFS_PIXEL_CRYPTO_DATA_UNIT_SIZE) {
pr_err("scatterlist segment is misaligned for crypto\n");
*err = -EIO;
return;
}
/* Enable crypto and set the keyslot. */
ent->des3 |= cpu_to_le32(CRYPTO_ENABLE |
CRYPTO_KEYSLOT(lrbp->crypto_key_slot));
/*
* Set the IV. The DUN is *supposed* to be formatted as a
* little endian integer to produce the 16-byte AES-XTS IV, like
* it is in the UFS standard. But this hardware interprets the
* IV bytes backwards. Therefore, we actually need to format
* the DUN as big endian to get the right ciphertext at the end.
*/
ent->iv[0] = 0;
ent->iv[1] = cpu_to_be64(lrbp->data_unit_num + i);
}
/*
* Unset the keyslot in the ufshcd_lrb so that the keyslot and DUN don't
* get filled into the UTRD according to the UFSHCI standard.
*/
lrbp->crypto_key_slot = -1;
}
/* Configure inline encryption (or decryption) on requests that require it. */
static void ufs_pixel_fips_crypto_fill_prdt_sw_key_mode(void *unused,
struct ufs_hba *hba,
struct ufshcd_lrb *lrbp,
unsigned int segments,
int *err)
{
const struct bio_crypt_ctx *bc;
const u8 *key, *tweak_key;
u64 dun_lo, dun_hi;
struct pixel_ufs_prdt_entry *prdt;
unsigned int i;
/*
* There's nothing to do for unencrypted requests, since the mode field
* ("FAS") is already 0 (FMP_BYPASS_MODE) by default, as it's in the
* same word as ufshcd_sg_entry::size which was already initialized.
*/
bc = scsi_cmd_to_rq(lrbp->cmd)->crypt_ctx;
if (!bc)
return;
key = bc->bc_key->raw;
tweak_key = key + AES_KEYSIZE_256;
dun_lo = bc->bc_dun[0];
dun_hi = bc->bc_dun[1];
/* Reject weak AES-XTS keys. */
if (!crypto_memneq(key, tweak_key, AES_KEYSIZE_256)) {
dev_err(hba->dev, "Can't use weak AES-XTS key\n");
*err = -EIO;
return;
}
/* Configure FMP on each segment of the request. */
prdt = (struct pixel_ufs_prdt_entry *)lrbp->ucd_prdt_ptr;
for (i = 0; i < segments; i++) {
struct pixel_ufs_prdt_entry *ent = &prdt[i];
struct ufshcd_sg_entry *prd = (struct ufshcd_sg_entry *)ent;
int j;
/* Each segment must be exactly one data unit. */
if (le32_to_cpu(prd->size) + 1 !=
UFS_PIXEL_CRYPTO_DATA_UNIT_SIZE) {
pr_err("scatterlist segment is misaligned for crypto\n");
*err = -EIO;
return;
}
/* Set the algorithm and key length. */
ent->des3 |= cpu_to_le32(PRDT_FAS_XTS | PRDT_FKL_256);
/* Set the key. */
for (j = 0; j < ENCKEY_NUM_WORDS; j++) {
ent->file_enckey[ENCKEY_NUM_WORDS - 1 - j] =
get_unaligned_be32(&key[j * 4]);
ent->file_twkey[TWKEY_NUM_WORDS - 1 - j] =
get_unaligned_be32(&tweak_key[j * 4]);
}
/* Set the IV. */
ent->iv[0] = cpu_to_be64(dun_hi);
ent->iv[1] = cpu_to_be64(dun_lo);
/* Increment the data unit number. */
dun_lo++;
if (dun_lo == 0)
dun_hi++;
}
}
static void ufs_pixel_fips_build_utrd(struct ufs_hba *hba,
struct utp_transfer_req_desc *utrd,
struct fips_buffer_info *bi,
u32 data_direction)
{
dma_addr_t ucd_dma_addr = bi->ucd_dma_addr;
u16 response_offset =
offsetof(struct utp_transfer_cmd_desc, response_upiu);
u16 prdt_offset = offsetof(struct utp_transfer_cmd_desc, prd_table);
u16 prdt_length = sizeof(struct pixel_ufs_prdt_entry);
u32 crypto = bi->iv ? UTRD_CRYPTO_ENABLE : UTRD_CRYPTO_DISABLE;
memset(utrd, 0, sizeof(struct utp_transfer_req_desc));
utrd->header.dword_0 = cpu_to_le32(UTRD_CMD_TYPE_UFS_STORAGE |
data_direction | crypto);
utrd->header.dword_1 = 0;
utrd->header.dword_2 = cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
utrd->header.dword_3 = 0;
utrd->command_desc_base_addr = cpu_to_le64(ucd_dma_addr);
if (hba->quirks & UFSHCD_QUIRK_PRDT_BYTE_GRAN) {
utrd->response_upiu_length = cpu_to_le16(ALIGNED_UPIU_SIZE);
utrd->response_upiu_offset = cpu_to_le16(response_offset);
utrd->prd_table_offset = cpu_to_le16(prdt_offset);
utrd->prd_table_length = cpu_to_le16(prdt_length);
} else {
utrd->response_upiu_length =
cpu_to_le16(ALIGNED_UPIU_SIZE >> 2);
utrd->response_upiu_offset = cpu_to_le16(response_offset >> 2);
utrd->prd_table_offset = cpu_to_le16(prdt_offset >> 2);
utrd->prd_table_length = cpu_to_le16(1);
}
}
static void ufs_pixel_fips_build_prdt(struct ufs_hba *hba,
struct fips_buffer_info *bi,
u32 buffer_len)
{
struct utp_transfer_cmd_desc *ucd_addr = bi->ucd_addr;
dma_addr_t buffer_dma_addr = bi->io_buffer_dma_addr;
struct pixel_ufs_prdt_entry *sg_entry =
(struct pixel_ufs_prdt_entry *)ucd_addr->prd_table;
sg_entry->des0 = cpu_to_le32(lower_32_bits(buffer_dma_addr));
sg_entry->des1 = cpu_to_le32(upper_32_bits(buffer_dma_addr));
sg_entry->des2 = 0;
if (!bi->iv) {
sg_entry->des3 = cpu_to_le32(buffer_len - 1);
return;
}
/*
* The hardware interprets the IV in backwards order. Hence we
* reverse each byte of the 16 byte IV.
*/
sg_entry->iv[0] = get_unaligned_be64(bi->iv + 8);
sg_entry->iv[1] = get_unaligned_be64(bi->iv);
if (use_hw_keys) {
sg_entry->des3 =
cpu_to_le32(CRYPTO_ENABLE | CRYPTO_KEYSLOT(bi->mki) |
(buffer_len - 1));
} else {
const u8 *key = bi->key;
const u8 *tweak_key = key + (ENCKEY_NUM_WORDS * 4);
u32 i;
/*
* The verification only needs to test AES-256-XTS algorithm as
* it's the only one being used by the system and the only one
* being certified. The actual gs101 hardware is capable of
* supporting AES-128-CBC, AES-256-CBC, and AES-128-XTS as well.
*/
sg_entry->des3 = cpu_to_le32(PRDT_FAS_XTS | PRDT_FKL_256 |
(buffer_len - 1));
for (i = 0; i < ENCKEY_NUM_WORDS; i++) {
sg_entry->file_enckey[ENCKEY_NUM_WORDS - 1 - i] =
get_unaligned_be32(&key[i * 4]);
sg_entry->file_twkey[TWKEY_NUM_WORDS - 1 - i] =
get_unaligned_be32(&tweak_key[i * 4]);
}
}
}
static void ufs_pixel_fips_build_upiu(struct ufs_hba *hba,
struct utp_transfer_cmd_desc *ucd_addr,
struct scsi_cdb *cdb, u16 flags, u32 lun,
u32 buffer_len, u8 task_tag)
{
struct utp_upiu_req *ucd_req_ptr =
(struct utp_upiu_req *)ucd_addr->command_upiu;
ucd_req_ptr->header.dword_0 = UPIU_HEADER_DWORD(
UPIU_TRANSACTION_COMMAND, flags, lun, task_tag);
ucd_req_ptr->header.dword_1 = 0;
ucd_req_ptr->header.dword_2 = 0;
ucd_req_ptr->sc.exp_data_transfer_len = cpu_to_be32(buffer_len);
memcpy(ucd_req_ptr->sc.cdb, cdb, sizeof(struct scsi_cdb));
}
static int ufs_pixel_fips_send_utrd(struct ufs_hba *hba,
struct utp_transfer_req_desc *utrd,
u8 task_tag)
{
struct utp_transfer_req_desc utrd_temp;
unsigned long timeout;
u32 tr_doorbell;
memcpy(&utrd_temp, hba->utrdl_base_addr + task_tag,
sizeof(struct utp_transfer_req_desc));
memcpy(hba->utrdl_base_addr + task_tag, utrd,
sizeof(struct utp_transfer_req_desc));
if (hba->vops && hba->vops->setup_xfer_req) {
spin_lock_irq(&hba->outstanding_lock);
hba->vops->setup_xfer_req(hba, task_tag, true);
spin_unlock_irq(&hba->outstanding_lock);
}
ufshcd_writel(hba, 1 << task_tag, REG_UTP_TRANSFER_REQ_DOOR_BELL);
/* Make sure that doorbell is committed immediately */
wmb();
/* Wait for completion */
timeout = jiffies + msecs_to_jiffies(IO_COMPLETION_TIMEOUT_MS);
while (time_before(jiffies, timeout)) {
tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
if (!tr_doorbell)
break;
usleep_range(50, 100);
}
memcpy(utrd, hba->utrdl_base_addr + task_tag,
sizeof(struct utp_transfer_req_desc));
memcpy(hba->utrdl_base_addr + task_tag, &utrd_temp,
sizeof(struct utp_transfer_req_desc));
if (tr_doorbell) {
pr_err("Request timed out\n");
return -ETIMEDOUT;
}
return 0;
}
static int ufs_pixel_fips_check_response(struct utp_upiu_rsp *resp, u8 ocs)
{
u8 status = be32_to_cpu(resp->header.dword_1);
u8 response = be32_to_cpu(resp->header.dword_1) >> 8;
if (ocs || status || response) {
uint8_t response_code = resp->sr.sense_data[0] & 0x7F;
if (response_code == 0x70) {
uint8_t key = resp->sr.sense_data[2] & 0xF;
uint8_t asc = resp->sr.sense_data[12];
uint8_t ascq = resp->sr.sense_data[13];
if (!ocs && status == 2 && response == 1 && key == 6 && asc == 0x29)
pr_info("UA Reported\n");
else
pr_warn("I/O Result: OCS=%x status=%X key=%X asc=%X ascq=%X\n",
ocs, status, key, asc, ascq);
} else {
pr_warn("I/O Result: OCS=%x status=%X\n", ocs, status);
}
return -EIO;
}
return 0;
}
int ufs_pixel_fips_send_request(struct ufs_hba *hba, struct scsi_cdb *cdb,
struct fips_buffer_info *bi, u32 buffer_len,
u32 lu)
{
struct utp_transfer_req_desc utrd;
struct utp_upiu_rsp *resp_upiu =
(struct utp_upiu_rsp *)bi->ucd_addr->response_upiu;
u8 task_tag = 0x7;
u8 ocs;
u32 data_direction;
u32 flags;
int ret;
memset(bi->ucd_addr, 0, UFS_PIXEL_UCD_SIZE);
/* Build CDB */
switch (cdb->op_code) {
case REQUEST_SENSE:
data_direction = UTRD_DD_DEVICE_TO_SYSTEM;
flags = UPIU_CMD_FLAGS_READ;
break;
case READ_10:
data_direction = UTRD_DD_DEVICE_TO_SYSTEM;
flags = UPIU_CMD_FLAGS_READ;
break;
case WRITE_10:
data_direction = UTRD_DD_SYSTEM_TO_DEVICE;
flags = UPIU_CMD_FLAGS_WRITE;
break;
default:
pr_err("unsupported scsi op code 0x%02x\n", cdb->op_code);
return -EBADRQC;
}
/* Build UTRD */
ufs_pixel_fips_build_utrd(hba, &utrd, bi, data_direction);
/* Build PRDT */
ufs_pixel_fips_build_prdt(hba, bi, buffer_len);
/* Build UPIU */
ufs_pixel_fips_build_upiu(hba, bi->ucd_addr, cdb, flags, lu, buffer_len,
task_tag);
/* Make sure descriptors are ready before ringing the task doorbell */
wmb();
/* Send */
ret = ufs_pixel_fips_send_utrd(hba, &utrd, task_tag);
if (ret)
return ret;
ocs = le32_to_cpu(utrd.header.dword_2) & 0xFF;
return ufs_pixel_fips_check_response(resp_upiu, ocs);
}
static int ufs_pixel_fips_request_sense(struct ufs_hba *hba,
struct fips_buffer_info *bi)
{
struct scsi_cdb cdb = {};
int ret;
cdb.op_code = REQUEST_SENSE;
cdb.transfer_len = cpu_to_be16(SENSE_DATA_ALLOC_LEN);
ret = ufs_pixel_fips_send_request(hba, &cdb, bi, SENSE_DATA_ALLOC_LEN,
fips_lu);
if (ret)
return -EIO;
return 0;
}
static int ufs_pixel_fips_send_io(struct ufs_hba *hba,
struct fips_buffer_info *bi, u8 op_code)
{
struct scsi_cdb cdb = {};
int ret;
int retry = IO_RETRY_COUNT;
cdb.op_code = op_code;
cdb.lba = cpu_to_be32(fips_first_lba);
cdb.transfer_len = cpu_to_be16(1);
do {
ret = ufs_pixel_fips_send_request(
hba, &cdb, bi, UFS_PIXEL_BUFFER_SIZE, fips_lu);
} while (ret && retry-- > 0);
if (ret)
return -EIO;
return 0;
}
static int ufs_pixel_fips_read(struct ufs_hba *hba, struct fips_buffer_info *bi)
{
return ufs_pixel_fips_send_io(hba, bi, READ_10);
}
static int ufs_pixel_fips_write(struct ufs_hba *hba,
struct fips_buffer_info *bi)
{
return ufs_pixel_fips_send_io(hba, bi, WRITE_10);
}
static const u8 pixel_fips_encryption_pt[] = {
0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, /* "This is " */
0x61, 0x20, 0x33, 0x32, 0x42, 0x20, 0x65, 0x6E, /* "a 32B en" */
0x63, 0x72, 0x79, 0x70, 0x74, 0x20, 0x70, 0x74, /* "crypt pt" */
0x20, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, /* " message" */
};
/* Ciphertext based on the fips_encryption key below used in sw key mode. */
static const u8 pixel_fips_encryption_ct[] = {
0x57, 0x4F, 0x6F, 0xD1, 0x21, 0x33, 0xE5, 0xF4,
0x6F, 0x72, 0x30, 0x63, 0x8B, 0xCE, 0xA7, 0xD4,
0x84, 0x84, 0xF9, 0xE2, 0xC5, 0xB9, 0xE4, 0x48,
0x8D, 0x49, 0x02, 0x88, 0x80, 0xB3, 0x07, 0xE2,
};
/* Ciphertext based on the EFUSE encryption key used in hw key mode. */
static const u8 pixel_fips_encryption_efuse_ct[] = {
0xEE, 0xD2, 0xD3, 0x69, 0xE9, 0x60, 0x48, 0xF1,
0x26, 0xE8, 0xC6, 0xD7, 0x2E, 0xFB, 0x0C, 0x69,
0x2A, 0xC4, 0xF4, 0x32, 0x58, 0xD0, 0x7B, 0xC2,
0x75, 0xA9, 0xB0, 0x4B, 0x4E, 0x39, 0x31, 0x98,
};
static const u8 pixel_fips_encryption_key[] = {
0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, /* "This is " */
0x61, 0x20, 0x33, 0x32, 0x42, 0x20, 0x65, 0x6E, /* "a 32B en" */
0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6F, 0x6E, /* "cryption" */
0x20, 0x6B, 0x65, 0x79, 0x20, 0x77, 0x69, 0x74, /* " key wit" */
0x68, 0x20, 0x61, 0x20, 0x33, 0x32, 0x42, 0x20, /* "h a 32B " */
0x65, 0x6E, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, /* "encrypti" */
0x6F, 0x6E, 0x20, 0x74, 0x77, 0x65, 0x61, 0x6B, /* "on tweak" */
0x20, 0x6B, 0x65, 0x79, 0x20, 0x20, 0x20, 0x20, /* " key " */
};
static const u8 pixel_fips_encryption_iv[] = {
0x41, 0x20, 0x31, 0x36, 0x42, 0x20, 0x65, 0x6E, /* "A 16B en" */
0x63, 0x72, 0x79, 0x70, 0x74, 0x20, 0x49, 0x56, /* "crypt IV" */
};
static u32 ufs_pixel_fips_get_ise_version(struct ufs_hba *hba)
{
struct exynos_ufs *ufs = to_exynos_ufs(hba);
struct ufs_vs_handle *handle = &ufs->handle;
return readl(handle->ufsp + ISE_VERSION_REG_OFFSET);
}
static struct ufs_pixel_fips_info fips_info;
const struct ufs_pixel_fips_info *ufs_pixel_fips_get_info(struct ufs_hba *hba)
{
u32 ise_version = ufs_pixel_fips_get_ise_version(hba);
fips_info.ise_version_major = ISE_VERSION_MAJOR(ise_version);
fips_info.ise_version_minor = ISE_VERSION_MINOR(ise_version);
fips_info.ise_version_revision = ISE_VERSION_REVISION(ise_version);
fips_info.key_delivery_mode =
use_hw_keys ? KEY_DELIVERY_HW : KEY_DELIVERY_SW;
return &fips_info;
}
EXPORT_SYMBOL_GPL(ufs_pixel_fips_get_info);
int ufs_pixel_fips_verify(struct ufs_hba *hba)
{
int ret;
u32 interrupts;
struct fips_buffer_info bi;
static bool print_ise_version = true;
const u8 *encryption_ct = use_hw_keys ? pixel_fips_encryption_efuse_ct :
pixel_fips_encryption_ct;
if (print_ise_version) {
u32 ise_version = ufs_pixel_fips_get_ise_version(hba);
pr_info("ISE HW version %u.%u.%u\n",
ISE_VERSION_MAJOR(ise_version),
ISE_VERSION_MINOR(ise_version),
ISE_VERSION_REVISION(ise_version));
print_ise_version = false;
}
ise_available = 0;
if (!fips_first_lba || !fips_last_lba ||
fips_last_lba < fips_first_lba) {
pr_err("Invalid module params: first_lba=%u last_lba=%u\n",
fips_first_lba, fips_last_lba);
return -EINVAL;
}
bi.io_buffer = dma_alloc_coherent(hba->dev, UFS_PIXEL_BUFFER_SIZE,
&bi.io_buffer_dma_addr,
GFP_NOIO | __GFP_NOFAIL);
if (!bi.io_buffer)
return -ENOMEM;
bi.ucd_addr = dma_alloc_coherent(hba->dev, UFS_PIXEL_UCD_SIZE,
&bi.ucd_dma_addr,
GFP_NOIO | __GFP_NOFAIL);
if (!bi.ucd_addr) {
dma_free_coherent(hba->dev, UFS_PIXEL_BUFFER_SIZE, bi.io_buffer,
bi.io_buffer_dma_addr);
return -ENOMEM;
}
bi.iv = NULL;
bi.key = NULL;
/*
* Enable clocks, exit hibern8, set link as active
* Will release on function exit
*/
ufshcd_hold(hba, false);
/*
* Disable all interrupts except UTP Transfer Request Completion
* Controller will not complete requests without this enabled
* Restore on function exit
*/
interrupts = ufshcd_readl(hba, REG_INTERRUPT_ENABLE);
ufshcd_writel(hba, UTP_TRANSFER_REQ_COMPL, REG_INTERRUPT_ENABLE);
ufs_pixel_fips_request_sense(hba, &bi);
/*
* Verify Encryption:
* Write plaintext with specified crypto parameters, then read raw.
* Compare vs expected ciphertext.
*/
fips_info.encryption_test_attempted++;
memset(bi.io_buffer, 0, UFS_PIXEL_BUFFER_SIZE);
memcpy(bi.io_buffer, pixel_fips_encryption_pt,
sizeof(pixel_fips_encryption_pt));
if (use_hw_keys)
bi.mki = UFS_PIXEL_MASTER_KEY_INDEX;
else
bi.key = pixel_fips_encryption_key;
bi.iv = pixel_fips_encryption_iv;
ret = ufs_pixel_fips_write(hba, &bi);
if (ret)
goto out;
memset(bi.io_buffer, 0, UFS_PIXEL_BUFFER_SIZE);
bi.key = NULL;
bi.iv = NULL;
ret = ufs_pixel_fips_read(hba, &bi);
if (ret)
goto out;
if (memcmp(bi.io_buffer, encryption_ct, MESSAGE_LENGTH)) {
pr_err("Encryption verification failed\n");
ret = -EINVAL;
goto out;
}
fips_info.encryption_test_passed++;
pr_info("Encryption verification passed\n");
/*
* Verify Decryption:
* Since the ciphertext is already stored we just read back with the
* specified crypto parameters.
* Compare vs expected plaintext.
*/
fips_info.decryption_test_attempted++;
memset(bi.io_buffer, 0, UFS_PIXEL_BUFFER_SIZE);
if (use_hw_keys)
bi.mki = UFS_PIXEL_MASTER_KEY_INDEX;
else
bi.key = pixel_fips_encryption_key;
bi.iv = pixel_fips_encryption_iv;
ret = ufs_pixel_fips_read(hba, &bi);
if (ret)
goto out;
if (memcmp(bi.io_buffer, pixel_fips_encryption_pt,
sizeof(pixel_fips_encryption_pt))) {
pr_err("Decryption verification failed\n");
ret = -EINVAL;
goto out;
}
fips_info.decryption_test_passed++;
pr_info("Decryption verification passed\n");
out:
ufshcd_writel(hba, interrupts, REG_INTERRUPT_ENABLE);
ufshcd_release(hba);
memzero_explicit(bi.ucd_addr, UFS_PIXEL_UCD_SIZE);
memzero_explicit(bi.io_buffer, UFS_PIXEL_BUFFER_SIZE);
dma_free_coherent(hba->dev, UFS_PIXEL_UCD_SIZE, bi.ucd_addr,
bi.ucd_dma_addr);
dma_free_coherent(hba->dev, UFS_PIXEL_BUFFER_SIZE, bi.io_buffer,
bi.io_buffer_dma_addr);
ise_available = !ret;
return ret;
}
EXPORT_SYMBOL_GPL(ufs_pixel_fips_verify);
static int __init unapply_text_relocations(void *section, int section_size,
const Elf64_Rela *rela, int numrels)
{
while (numrels--) {
u32 *place = (u32 *)(section + rela->r_offset);
if (rela->r_offset >= section_size) {
pr_err("rela->r_offset(%llu) >= section_size(%u)",
rela->r_offset, section_size);
return -EINVAL;
}
switch (ELF64_R_TYPE(rela->r_info)) {
#ifdef CONFIG_ARM64
case R_AARCH64_JUMP26:
case R_AARCH64_CALL26:
*place &= ~GENMASK(25, 0);
break;
case R_AARCH64_ADR_PREL_LO21:
case R_AARCH64_ADR_PREL_PG_HI21:
case R_AARCH64_ADR_PREL_PG_HI21_NC:
*place &= ~(GENMASK(30, 29) | GENMASK(23, 5));
break;
case R_AARCH64_ADD_ABS_LO12_NC:
case R_AARCH64_LDST8_ABS_LO12_NC:
case R_AARCH64_LDST16_ABS_LO12_NC:
case R_AARCH64_LDST32_ABS_LO12_NC:
case R_AARCH64_LDST64_ABS_LO12_NC:
case R_AARCH64_LDST128_ABS_LO12_NC:
*place &= ~GENMASK(21, 10);
break;
default:
pr_err("unhandled relocation type %llu\n",
ELF64_R_TYPE(rela->r_info));
return -EINVAL;
#else
#error
#endif
}
rela++;
}
return 0;
}
enum {
PACIASP = 0xd503233f,
AUTIASP = 0xd50323bf,
SCS_PUSH = 0xf800865e,
SCS_POP = 0xf85f8e5e,
};
/*
* To make the integrity check work with dynamic Shadow Call Stack (SCS),
* replace all instructions that push or pop from the SCS with the Pointer
* Authentication Code (PAC) instructions that were present originally.
*/
static void __init unapply_scs_patch(void *section, int section_size)
{
#if defined(CONFIG_ARM64) && defined(CONFIG_UNWIND_PATCH_PAC_INTO_SCS)
u32 *insns = section;
int i;
for (i = 0; i < section_size / sizeof(insns[0]); i++) {
if (insns[i] == SCS_PUSH)
insns[i] = PACIASP;
else if (insns[i] == SCS_POP)
insns[i] = AUTIASP;
}
#endif
}
static const u8 ufs_pixel_fips_hmac_message[] = {
0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, /* "This is " */
0x61, 0x20, 0x35, 0x38, 0x42, 0x20, 0x6D, 0x65, /* "a 58B me" */
0x73, 0x73, 0x61, 0x67, 0x65, 0x20, 0x66, 0x6F, /* "ssage fo" */
0x72, 0x20, 0x48, 0x4D, 0x41, 0x43, 0x20, 0x76, /* "r HMAC v" */
0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, /* "erificat" */
0x69, 0x6F, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x46, /* "ion in F" */
0x49, 0x50, 0x53, 0x20, 0x6D, 0x6F, 0x64, 0x75, /* "IPS modu" */
0x6C, 0x65, /* "le" */
};
static const u8 ufs_pixel_fips_hmac_key[] = {
0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, /* "This is " */
0x61, 0x20, 0x35, 0x34, 0x42, 0x20, 0x6B, 0x65, /* "a 54B ke" */
0x79, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x48, 0x4D, /* "y for HM" */
0x41, 0x43, 0x20, 0x76, 0x65, 0x72, 0x69, 0x66, /* "AC verif" */
0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, /* "ication " */
0x69, 0x6E, 0x20, 0x46, 0x49, 0x50, 0x53, 0x20, /* "in FIPS " */
0x6D, 0x6F, 0x64, 0x75, 0x6C, 0x65, /* "module" */
};
static const u8 ufs_pixel_fips_hmac_expected[] = {
0x35, 0x3E, 0xA3, 0xB1, 0xEF, 0x1A, 0x79, 0x46,
0xDA, 0x21, 0x27, 0x64, 0x8F, 0x37, 0x1D, 0xD2,
0x5B, 0x5B, 0x84, 0xF3, 0x60, 0xB6, 0x95, 0x61,
0xF9, 0x06, 0x07, 0x73, 0x18, 0x77, 0xB7, 0x1D,
};
u8 __initdata fips140_integ_hmac_key[] = {
0x54, 0x68, 0x65, 0x20, 0x71, 0x75, 0x69, 0x63, /* "The quic" */
0x6B, 0x20, 0x62, 0x72, 0x6F, 0x77, 0x6E, 0x20, /* "k brown " */
0x66, 0x6F, 0x78, 0x20, 0x6A, 0x75, 0x6D, 0x70, /* "fox jump" */
0x73, 0x20, 0x6F, 0x76, 0x65, 0x72, 0x20, 0x74, /* "s over t" */
0x68, 0x65, 0x20, 0x6C, 0x61, 0x7A, 0x79, 0x20, /* "he lazy " */
0x64, 0x6F, 0x67, 0x00 /* "dog" */
};
u8 __initdata fips140_integ_hmac_digest[UFS_PIXEL_FIPS_SHA256_DIGEST_SIZE];
const u8 __fips140_text_start __section(".text.._start");
const u8 __fips140_text_end __section(".text.._end");
const u8 __fips140_rodata_start __section(".rodata.._start");
const u8 __fips140_rodata_end __section(".rodata.._end");
const u8 *__ufs_pixel_text_start = &__fips140_text_start;
const u8 *__ufs_pixel_rodata_start = &__fips140_rodata_start;
static int __init ufs_pixel_hmac_self_test(void)
{
u8 hmac_digest[UFS_PIXEL_FIPS_SHA256_DIGEST_SIZE];
int ret;
fips_info.hmac_self_test_attempted++;
ufs_pixel_fips_hmac_sha256(ufs_pixel_fips_hmac_message,
sizeof(ufs_pixel_fips_hmac_message),
ufs_pixel_fips_hmac_key,
sizeof(ufs_pixel_fips_hmac_key),
hmac_digest);
ret = memcmp(hmac_digest, ufs_pixel_fips_hmac_expected,
UFS_PIXEL_FIPS_SHA256_DIGEST_SIZE);
memzero_explicit(hmac_digest, sizeof(hmac_digest));
if (!ret)
fips_info.hmac_self_test_passed++;
return ret;
}
extern struct {
u32 offset;
u32 count;
} fips140_rela_text;
static int __init ufs_pixel_self_integrity_test(void)
{
u8 hmac_digest[UFS_PIXEL_FIPS_SHA256_DIGEST_SIZE];
size_t text_len;
size_t rodata_len;
void *hmac_buffer;
int ret;
fips_info.self_integrity_test_attempted++;
text_len = &__fips140_text_end - &__fips140_text_start;
rodata_len = &__fips140_rodata_end - &__fips140_rodata_start;
hmac_buffer = kmalloc(text_len + rodata_len, GFP_KERNEL);
if (!hmac_buffer)
return -ENOMEM;
memcpy(hmac_buffer, __ufs_pixel_text_start, text_len);
memcpy(hmac_buffer + text_len, __ufs_pixel_rodata_start, rodata_len);
ret = unapply_text_relocations(hmac_buffer, text_len,
offset_to_ptr(&fips140_rela_text.offset),
fips140_rela_text.count);
if (ret) {
kfree_sensitive(hmac_buffer);
return ret;
}
unapply_scs_patch(hmac_buffer, text_len);
ufs_pixel_fips_hmac_sha256(hmac_buffer, text_len + rodata_len,
fips140_integ_hmac_key,
strlen(fips140_integ_hmac_key), hmac_digest);
kfree_sensitive(hmac_buffer);
ret = memcmp(hmac_digest, fips140_integ_hmac_digest,
UFS_PIXEL_FIPS_SHA256_DIGEST_SIZE);
memzero_explicit(hmac_digest, sizeof(hmac_digest));
if (!ret)
fips_info.self_integrity_test_passed++;
return ret;
}
static int __init ufs_pixel_fips_init(void)
{
int ret;
pr_info ("%s version %s loading...\n",
UFS_PIXEL_FIPS140_MODULE_NAME,
UFS_PIXEL_FIPS140_MODULE_VERSION);
/* Verify internal HMAC functionality */
if (ufs_pixel_hmac_self_test()) {
pr_err("HMAC self test failed\n");
return -EINVAL;
}
pr_info("HMAC self test passed\n");
/* Perform module self integrity check */
if (ufs_pixel_self_integrity_test()) {
pr_err("Verify self HMAC failed\n");
return -EINVAL;
}
pr_info("Verify self HMAC passed\n");
ret = register_trace_android_vh_ufs_fill_prdt(
use_hw_keys ? ufs_pixel_fips_crypto_fill_prdt_hw_key_mode :
ufs_pixel_fips_crypto_fill_prdt_sw_key_mode,
NULL);
if (ret)
pr_err("Failed to register ufs_pixel_fips_crypto_fill_prdt\n");
return ret;
}
static void ufs_pixel_fips_exit(void)
{
}
module_init(ufs_pixel_fips_init);
module_exit(ufs_pixel_fips_exit);
MODULE_DESCRIPTION(
"FIPS140-3 Compliant SW Driven UFS Inline Encryption Self Test Module");
MODULE_AUTHOR("Konstantin Vyshetsky");
MODULE_LICENSE("GPL v2");
MODULE_VERSION(UFS_PIXEL_FIPS140_MODULE_VERSION);