// SPDX-License-Identifier: GPL-2.0-only
/*
* AMD Cryptographic Coprocessor (CCP) driver
*
* Copyright (C) 2016,2017 Advanced Micro Devices, Inc.
*
* Author: Gary R Hook <gary.hook@amd.com>
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/kthread.h>
#include <linux/debugfs.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/compiler.h>
#include <linux/ccp.h>
#include "ccp-dev.h"
/* Allocate the requested number of contiguous LSB slots
* from the LSB bitmap. Look in the private range for this
* queue first; failing that, check the public area.
* If no space is available, wait around.
* Return: first slot number
*/
static u32 ccp_lsb_alloc(struct ccp_cmd_queue *cmd_q, unsigned int count)
{
struct ccp_device *ccp;
int start;
/* First look at the map for the queue */
if (cmd_q->lsb >= 0) {
start = (u32)bitmap_find_next_zero_area(cmd_q->lsbmap,
LSB_SIZE,
0, count, 0);
if (start < LSB_SIZE) {
bitmap_set(cmd_q->lsbmap, start, count);
return start + cmd_q->lsb * LSB_SIZE;
}
}
/* No joy; try to get an entry from the shared blocks */
ccp = cmd_q->ccp;
for (;;) {
mutex_lock(&ccp->sb_mutex);
start = (u32)bitmap_find_next_zero_area(ccp->lsbmap,
MAX_LSB_CNT * LSB_SIZE,
0,
count, 0);
if (start <= MAX_LSB_CNT * LSB_SIZE) {
bitmap_set(ccp->lsbmap, start, count);
mutex_unlock(&ccp->sb_mutex);
return start;
}
ccp->sb_avail = 0;
mutex_unlock(&ccp->sb_mutex);
/* Wait for KSB entries to become available */
if (wait_event_interruptible(ccp->sb_queue, ccp->sb_avail))
return 0;
}
}
/* Free a number of LSB slots from the bitmap, starting at
* the indicated starting slot number.
*/
static void ccp_lsb_free(struct ccp_cmd_queue *cmd_q, unsigned int start,
unsigned int count)
{
if (!start)
return;
if (cmd_q->lsb == start) {
/* An entry from the private LSB */
bitmap_clear(cmd_q->lsbmap, start, count);
} else {
/* From the shared LSBs */
struct ccp_device *ccp = cmd_q->ccp;
mutex_lock(&ccp->sb_mutex);
bitmap_clear(ccp->lsbmap, start, count);
ccp->sb_avail = 1;
mutex_unlock(&ccp->sb_mutex);
wake_up_interruptible_all(&ccp->sb_queue);
}
}
/* CCP version 5: Union to define the function field (cmd_reg1/dword0) */
union ccp_function {
struct {
u16 size:7;
u16 encrypt:1;
u16 mode:5;
u16 type:2;
} aes;
struct {
u16 size:7;
u16 encrypt:1;
u16 rsvd:5;
u16 type:2;
} aes_xts;
struct {
u16 size:7;
u16 encrypt:1;
u16 mode:5;
u16 type:2;
} des3;
struct {
u16 rsvd1:10;
u16 type:4;
u16 rsvd2:1;
} sha;
struct {
u16 mode:3;
u16 size:12;
} rsa;
struct {
u16 byteswap:2;
u16 bitwise:3;
u16 reflect:2;
u16 rsvd:8;
} pt;
struct {
u16 rsvd:13;
} zlib;
struct {
u16 size:10;
u16 type:2;
u16 mode:3;
} ecc;
u16 raw;
};
#define CCP_AES_SIZE(p) ((p)->aes.size)
#define CCP_AES_ENCRYPT(p) ((p)->aes.encrypt)
#define CCP_AES_MODE(p) ((p)->aes.mode)
#define CCP_AES_TYPE(p) ((p)->aes.type)
#define CCP_XTS_SIZE(p) ((p)->aes_xts.size)
#define CCP_XTS_TYPE(p) ((p)->aes_xts.type)
#define CCP_XTS_ENCRYPT(p) ((p)->aes_xts.encrypt)
#define CCP_DES3_SIZE(p) ((p)->des3.size)
#define CCP_DES3_ENCRYPT(p) ((p)->des3.encrypt)
#define CCP_DES3_MODE(p) ((p)->des3.mode)
#define CCP_DES3_TYPE(p) ((p)->des3.type)
#define CCP_SHA_TYPE(p) ((p)->sha.type)
#define CCP_RSA_SIZE(p) ((p)->rsa.size)
#define CCP_PT_BYTESWAP(p) ((p)->pt.byteswap)
#define CCP_PT_BITWISE(p) ((p)->pt.bitwise)
#define CCP_ECC_MODE(p) ((p)->ecc.mode)
#define CCP_ECC_AFFINE(p) ((p)->ecc.one)
/* Word 0 */
#define CCP5_CMD_DW0(p) ((p)->dw0)
#define CCP5_CMD_SOC(p) (CCP5_CMD_DW0(p).soc)
#define CCP5_CMD_IOC(p) (CCP5_CMD_DW0(p).ioc)
#define CCP5_CMD_INIT(p) (CCP5_CMD_DW0(p).init)
#define CCP5_CMD_EOM(p) (CCP5_CMD_DW0(p).eom)
#define CCP5_CMD_FUNCTION(p) (CCP5_CMD_DW0(p).function)
#define CCP5_CMD_ENGINE(p) (CCP5_CMD_DW0(p).engine)
#define CCP5_CMD_PROT(p) (CCP5_CMD_DW0(p).prot)
/* Word 1 */
#define CCP5_CMD_DW1(p) ((p)->length)
#define CCP5_CMD_LEN(p) (CCP5_CMD_DW1(p))
/* Word 2 */
#define CCP5_CMD_DW2(p) ((p)->src_lo)
#define CCP5_CMD_SRC_LO(p) (CCP5_CMD_DW2(p))
/* Word 3 */
#define CCP5_CMD_DW3(p) ((p)->dw3)
#define CCP5_CMD_SRC_MEM(p) ((p)->dw3.src_mem)
#define CCP5_CMD_SRC_HI(p) ((p)->dw3.src_hi)
#define CCP5_CMD_LSB_ID(p) ((p)->dw3.lsb_cxt_id)
#define CCP5_CMD_FIX_SRC(p) ((p)->dw3.fixed)
/* Words 4/5 */
#define CCP5_CMD_DW4(p) ((p)->dw4)
#define CCP5_CMD_DST_LO(p) (CCP5_CMD_DW4(p).dst_lo)
#define CCP5_CMD_DW5(p) ((p)->dw5.fields.dst_hi)
#define CCP5_CMD_DST_HI(p) (CCP5_CMD_DW5(p))
#define CCP5_CMD_DST_MEM(p) ((p)->dw5.fields.dst_mem)
#define CCP5_CMD_FIX_DST(p) ((p)->dw5.fields.fixed)
#define CCP5_CMD_SHA_LO(p) ((p)->dw4.sha_len_lo)
#define CCP5_CMD_SHA_HI(p) ((p)->dw5.sha_len_hi)
/* Word 6/7 */
#define CCP5_CMD_DW6(p) ((p)->key_lo)
#define CCP5_CMD_KEY_LO(p) (CCP5_CMD_DW6(p))
#define CCP5_CMD_DW7(p) ((p)->dw7)
#define CCP5_CMD_KEY_HI(p) ((p)->dw7.key_hi)
#define CCP5_CM