Skip to content

Commit

Permalink
s390/crypto: cpacf function detection
Browse files Browse the repository at this point in the history
The CPACF code makes some assumptions about the availablity of hardware
support. E.g. if the machine supports KM(AES-256) without chaining it is
assumed that KMC(AES-256) with chaining is available as well. For the
existing CPUs this is true but the architecturally correct way is to
check each CPACF functions on its own. This is what the query function
of each instructions is all about.

Reviewed-by: Harald Freudenberger <freude@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
  • Loading branch information
Martin Schwidefsky committed Aug 29, 2016
1 parent d863d59 commit 69c0e36
Show file tree
Hide file tree
Showing 9 changed files with 223 additions and 253 deletions.
299 changes: 115 additions & 184 deletions arch/s390/crypto/aes_s390.c

Large diffs are not rendered by default.

77 changes: 46 additions & 31 deletions arch/s390/crypto/des_s390.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
static u8 *ctrblk;
static DEFINE_SPINLOCK(ctrblk_lock);

static cpacf_mask_t km_functions, kmc_functions, kmctr_functions;

struct s390_des_ctx {
u8 iv[DES_BLOCK_SIZE];
u8 key[DES3_KEY_SIZE];
Expand All @@ -36,12 +38,12 @@ static int des_setkey(struct crypto_tfm *tfm, const u8 *key,
unsigned int key_len)
{
struct s390_des_ctx *ctx = crypto_tfm_ctx(tfm);
u32 *flags = &tfm->crt_flags;
u32 tmp[DES_EXPKEY_WORDS];

/* check for weak keys */
if (!des_ekey(tmp, key) && (*flags & CRYPTO_TFM_REQ_WEAK_KEY)) {
*flags |= CRYPTO_TFM_RES_WEAK_KEY;
if (!des_ekey(tmp, key) &&
(tfm->crt_flags & CRYPTO_TFM_REQ_WEAK_KEY)) {
tfm->crt_flags |= CRYPTO_TFM_RES_WEAK_KEY;
return -EINVAL;
}

Expand Down Expand Up @@ -238,13 +240,12 @@ static int des3_setkey(struct crypto_tfm *tfm, const u8 *key,
unsigned int key_len)
{
struct s390_des_ctx *ctx = crypto_tfm_ctx(tfm);
u32 *flags = &tfm->crt_flags;

if (!(crypto_memneq(key, &key[DES_KEY_SIZE], DES_KEY_SIZE) &&
crypto_memneq(&key[DES_KEY_SIZE], &key[DES_KEY_SIZE * 2],
DES_KEY_SIZE)) &&
(*flags & CRYPTO_TFM_REQ_WEAK_KEY)) {
*flags |= CRYPTO_TFM_RES_WEAK_KEY;
(tfm->crt_flags & CRYPTO_TFM_REQ_WEAK_KEY)) {
tfm->crt_flags |= CRYPTO_TFM_RES_WEAK_KEY;
return -EINVAL;
}
memcpy(ctx->key, key, key_len);
Expand Down Expand Up @@ -554,39 +555,53 @@ static int __init des_s390_init(void)
{
int ret;

if (!cpacf_query(CPACF_KM, CPACF_KM_DEA) ||
!cpacf_query(CPACF_KM, CPACF_KM_TDEA_192))
return -EOPNOTSUPP;

ret = des_s390_register_alg(&des_alg);
if (ret)
goto out_err;
ret = des_s390_register_alg(&ecb_des_alg);
if (ret)
goto out_err;
ret = des_s390_register_alg(&cbc_des_alg);
if (ret)
goto out_err;
ret = des_s390_register_alg(&des3_alg);
if (ret)
goto out_err;
ret = des_s390_register_alg(&ecb_des3_alg);
if (ret)
goto out_err;
ret = des_s390_register_alg(&cbc_des3_alg);
if (ret)
goto out_err;

if (cpacf_query(CPACF_KMCTR, CPACF_KMCTR_DEA) &&
cpacf_query(CPACF_KMCTR, CPACF_KMCTR_TDEA_192)) {
/* Query available functions for KM, KMC and KMCTR */
cpacf_query(CPACF_KM, &km_functions);
cpacf_query(CPACF_KMC, &kmc_functions);
cpacf_query(CPACF_KMCTR, &kmctr_functions);

if (cpacf_test_func(&km_functions, CPACF_KM_DEA)) {
ret = des_s390_register_alg(&des_alg);
if (ret)
goto out_err;
ret = des_s390_register_alg(&ecb_des_alg);
if (ret)
goto out_err;
}
if (cpacf_test_func(&kmc_functions, CPACF_KMC_DEA)) {
ret = des_s390_register_alg(&cbc_des_alg);
if (ret)
goto out_err;
}
if (cpacf_test_func(&km_functions, CPACF_KM_TDEA_192)) {
ret = des_s390_register_alg(&des3_alg);
if (ret)
goto out_err;
ret = des_s390_register_alg(&ecb_des3_alg);
if (ret)
goto out_err;
}
if (cpacf_test_func(&kmc_functions, CPACF_KMC_TDEA_192)) {
ret = des_s390_register_alg(&cbc_des3_alg);
if (ret)
goto out_err;
}

if (cpacf_test_func(&kmctr_functions, CPACF_KMCTR_DEA) ||
cpacf_test_func(&kmctr_functions, CPACF_KMCTR_TDEA_192)) {
ctrblk = (u8 *) __get_free_page(GFP_KERNEL);
if (!ctrblk) {
ret = -ENOMEM;
goto out_err;
}
}

if (cpacf_test_func(&kmctr_functions, CPACF_KMCTR_DEA)) {
ret = des_s390_register_alg(&ctr_des_alg);
if (ret)
goto out_err;
}
if (cpacf_test_func(&kmctr_functions, CPACF_KMCTR_TDEA_192)) {
ret = des_s390_register_alg(&ctr_des3_alg);
if (ret)
goto out_err;
Expand Down
2 changes: 1 addition & 1 deletion arch/s390/crypto/ghash_s390.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ static struct shash_alg ghash_alg = {

static int __init ghash_mod_init(void)
{
if (!cpacf_query(CPACF_KIMD, CPACF_KIMD_GHASH))
if (!cpacf_query_func(CPACF_KIMD, CPACF_KIMD_GHASH))
return -EOPNOTSUPP;

return crypto_register_shash(&ghash_alg);
Expand Down
4 changes: 2 additions & 2 deletions arch/s390/crypto/prng.c
Original file line number Diff line number Diff line change
Expand Up @@ -757,13 +757,13 @@ static int __init prng_init(void)
int ret;

/* check if the CPU has a PRNG */
if (!cpacf_query(CPACF_KMC, CPACF_KMC_PRNG))
if (!cpacf_query_func(CPACF_KMC, CPACF_KMC_PRNG))
return -EOPNOTSUPP;

/* choose prng mode */
if (prng_mode != PRNG_MODE_TDES) {
/* check for MSA5 support for PPNO operations */
if (!cpacf_query(CPACF_PPNO, CPACF_PPNO_SHA512_DRNG_GEN)) {
if (!cpacf_query_func(CPACF_PPNO, CPACF_PPNO_SHA512_DRNG_GEN)) {
if (prng_mode == PRNG_MODE_SHA512) {
pr_err("The prng module cannot "
"start in SHA-512 mode\n");
Expand Down
2 changes: 1 addition & 1 deletion arch/s390/crypto/sha1_s390.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ static struct shash_alg alg = {

static int __init sha1_s390_init(void)
{
if (!cpacf_query(CPACF_KIMD, CPACF_KIMD_SHA_1))
if (!cpacf_query_func(CPACF_KIMD, CPACF_KIMD_SHA_1))
return -EOPNOTSUPP;
return crypto_register_shash(&alg);
}
Expand Down
2 changes: 1 addition & 1 deletion arch/s390/crypto/sha256_s390.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ static int __init sha256_s390_init(void)
{
int ret;

if (!cpacf_query(CPACF_KIMD, CPACF_KIMD_SHA_256))
if (!cpacf_query_func(CPACF_KIMD, CPACF_KIMD_SHA_256))
return -EOPNOTSUPP;
ret = crypto_register_shash(&sha256_alg);
if (ret < 0)
Expand Down
2 changes: 1 addition & 1 deletion arch/s390/crypto/sha512_s390.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ static int __init init(void)
{
int ret;

if (!cpacf_query(CPACF_KIMD, CPACF_KIMD_SHA_512))
if (!cpacf_query_func(CPACF_KIMD, CPACF_KIMD_SHA_512))
return -EOPNOTSUPP;
if ((ret = crypto_register_shash(&sha512_alg)) < 0)
goto out;
Expand Down
55 changes: 34 additions & 21 deletions arch/s390/include/asm/cpacf.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@
#define CPACF_PPNO_SHA512_DRNG_GEN 0x03
#define CPACF_PPNO_SHA512_DRNG_SEED 0x83

typedef struct { unsigned char bytes[16]; } cpacf_mask_t;

/**
* cpacf_query() - check if a specific CPACF function is available
* @opcode: the opcode of the crypto instruction
Expand All @@ -116,55 +118,66 @@
*
* Returns 1 if @func is available for @opcode, 0 otherwise
*/
static inline void __cpacf_query(unsigned int opcode, unsigned char *status)
static inline void __cpacf_query(unsigned int opcode, cpacf_mask_t *mask)
{
typedef struct { unsigned char _[16]; } status_type;
register unsigned long r0 asm("0") = 0; /* query function */
register unsigned long r1 asm("1") = (unsigned long) status;
register unsigned long r1 asm("1") = (unsigned long) mask;

asm volatile(
" spm 0\n" /* pckmo doesn't change the cc */
/* Parameter registers are ignored, but may not be 0 */
"0: .insn rrf,%[opc] << 16,2,2,2,0\n"
" brc 1,0b\n" /* handle partial completion */
: "=m" (*(status_type *) status)
: "=m" (*mask)
: [fc] "d" (r0), [pba] "a" (r1), [opc] "i" (opcode)
: "cc");
}

static inline int cpacf_query(unsigned int opcode, unsigned int func)
static inline int __cpacf_check_opcode(unsigned int opcode)
{
unsigned char status[16];

switch (opcode) {
case CPACF_KMAC:
case CPACF_KM:
case CPACF_KMC:
case CPACF_KIMD:
case CPACF_KLMD:
if (!test_facility(17)) /* check for MSA */
return 0;
break;
return test_facility(17); /* check for MSA */
case CPACF_PCKMO:
if (!test_facility(76)) /* check for MSA3 */
return 0;
break;
return test_facility(76); /* check for MSA3 */
case CPACF_KMF:
case CPACF_KMO:
case CPACF_PCC:
case CPACF_KMCTR:
if (!test_facility(77)) /* check for MSA4 */
return 0;
break;
return test_facility(77); /* check for MSA4 */
case CPACF_PPNO:
if (!test_facility(57)) /* check for MSA5 */
return 0;
break;
return test_facility(57); /* check for MSA5 */
default:
BUG();
}
__cpacf_query(opcode, status);
return (status[func >> 3] & (0x80 >> (func & 7))) != 0;
}

static inline int cpacf_query(unsigned int opcode, cpacf_mask_t *mask)
{
if (__cpacf_check_opcode(opcode)) {
__cpacf_query(opcode, mask);
return 1;
}
memset(mask, 0, sizeof(*mask));
return 0;
}

static inline int cpacf_test_func(cpacf_mask_t *mask, unsigned int func)
{
return (mask->bytes[func >> 3] & (0x80 >> (func & 7))) != 0;
}

static inline int cpacf_query_func(unsigned int opcode, unsigned int func)
{
cpacf_mask_t mask;

if (cpacf_query(opcode, &mask))
return cpacf_test_func(&mask, func);
return 0;
}

/**
Expand Down
33 changes: 22 additions & 11 deletions arch/s390/kvm/kvm-s390.c
Original file line number Diff line number Diff line change
Expand Up @@ -245,22 +245,33 @@ static void kvm_s390_cpu_feat_init(void)
PTFF_QAF);

if (test_facility(17)) { /* MSA */
__cpacf_query(CPACF_KMAC, kvm_s390_available_subfunc.kmac);
__cpacf_query(CPACF_KMC, kvm_s390_available_subfunc.kmc);
__cpacf_query(CPACF_KM, kvm_s390_available_subfunc.km);
__cpacf_query(CPACF_KIMD, kvm_s390_available_subfunc.kimd);
__cpacf_query(CPACF_KLMD, kvm_s390_available_subfunc.klmd);
__cpacf_query(CPACF_KMAC, (cpacf_mask_t *)
kvm_s390_available_subfunc.kmac);
__cpacf_query(CPACF_KMC, (cpacf_mask_t *)
kvm_s390_available_subfunc.kmc);
__cpacf_query(CPACF_KM, (cpacf_mask_t *)
kvm_s390_available_subfunc.km);
__cpacf_query(CPACF_KIMD, (cpacf_mask_t *)
kvm_s390_available_subfunc.kimd);
__cpacf_query(CPACF_KLMD, (cpacf_mask_t *)
kvm_s390_available_subfunc.klmd);
}
if (test_facility(76)) /* MSA3 */
__cpacf_query(CPACF_PCKMO, kvm_s390_available_subfunc.pckmo);
__cpacf_query(CPACF_PCKMO, (cpacf_mask_t *)
kvm_s390_available_subfunc.pckmo);
if (test_facility(77)) { /* MSA4 */
__cpacf_query(CPACF_KMCTR, kvm_s390_available_subfunc.kmctr);
__cpacf_query(CPACF_KMF, kvm_s390_available_subfunc.kmf);
__cpacf_query(CPACF_KMO, kvm_s390_available_subfunc.kmo);
__cpacf_query(CPACF_PCC, kvm_s390_available_subfunc.pcc);
__cpacf_query(CPACF_KMCTR, (cpacf_mask_t *)
kvm_s390_available_subfunc.kmctr);
__cpacf_query(CPACF_KMF, (cpacf_mask_t *)
kvm_s390_available_subfunc.kmf);
__cpacf_query(CPACF_KMO, (cpacf_mask_t *)
kvm_s390_available_subfunc.kmo);
__cpacf_query(CPACF_PCC, (cpacf_mask_t *)
kvm_s390_available_subfunc.pcc);
}
if (test_facility(57)) /* MSA5 */
__cpacf_query(CPACF_PPNO, kvm_s390_available_subfunc.ppno);
__cpacf_query(CPACF_PPNO, (cpacf_mask_t *)
kvm_s390_available_subfunc.ppno);

if (MACHINE_HAS_ESOP)
allow_cpu_feat(KVM_S390_VM_CPU_FEAT_ESOP);
Expand Down

0 comments on commit 69c0e36

Please sign in to comment.