xref: /openbmc/qemu/target/s390x/tcg/crypto_helper.c (revision 73c19706)
1c9274b6bSCho, Yu-Chen /*
2c9274b6bSCho, Yu-Chen  *  s390x crypto helpers
3c9274b6bSCho, Yu-Chen  *
49f17bfdaSJason A. Donenfeld  *  Copyright (C) 2022 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
5c9274b6bSCho, Yu-Chen  *  Copyright (c) 2017 Red Hat Inc
6c9274b6bSCho, Yu-Chen  *
7c9274b6bSCho, Yu-Chen  *  Authors:
8c9274b6bSCho, Yu-Chen  *   David Hildenbrand <david@redhat.com>
99f17bfdaSJason A. Donenfeld  *   Jason A. Donenfeld <Jason@zx2c4.com>
10c9274b6bSCho, Yu-Chen  *
11c9274b6bSCho, Yu-Chen  * This work is licensed under the terms of the GNU GPL, version 2 or later.
12c9274b6bSCho, Yu-Chen  * See the COPYING file in the top-level directory.
13c9274b6bSCho, Yu-Chen  */
14c9274b6bSCho, Yu-Chen 
15c9274b6bSCho, Yu-Chen #include "qemu/osdep.h"
16*3dbc5fdaSJason A. Donenfeld #include "qemu/guest-random.h"
17c9274b6bSCho, Yu-Chen #include "s390x-internal.h"
18c9274b6bSCho, Yu-Chen #include "tcg_s390x.h"
19c9274b6bSCho, Yu-Chen #include "exec/helper-proto.h"
20c9274b6bSCho, Yu-Chen #include "exec/exec-all.h"
21c9274b6bSCho, Yu-Chen #include "exec/cpu_ldst.h"
22c9274b6bSCho, Yu-Chen 
R(uint64_t x,int c)239f17bfdaSJason A. Donenfeld static uint64_t R(uint64_t x, int c)
249f17bfdaSJason A. Donenfeld {
259f17bfdaSJason A. Donenfeld     return (x >> c) | (x << (64 - c));
269f17bfdaSJason A. Donenfeld }
Ch(uint64_t x,uint64_t y,uint64_t z)279f17bfdaSJason A. Donenfeld static uint64_t Ch(uint64_t x, uint64_t y, uint64_t z)
289f17bfdaSJason A. Donenfeld {
299f17bfdaSJason A. Donenfeld     return (x & y) ^ (~x & z);
309f17bfdaSJason A. Donenfeld }
Maj(uint64_t x,uint64_t y,uint64_t z)319f17bfdaSJason A. Donenfeld static uint64_t Maj(uint64_t x, uint64_t y, uint64_t z)
329f17bfdaSJason A. Donenfeld {
339f17bfdaSJason A. Donenfeld     return (x & y) ^ (x & z) ^ (y & z);
349f17bfdaSJason A. Donenfeld }
Sigma0(uint64_t x)359f17bfdaSJason A. Donenfeld static uint64_t Sigma0(uint64_t x)
369f17bfdaSJason A. Donenfeld {
379f17bfdaSJason A. Donenfeld     return R(x, 28) ^ R(x, 34) ^ R(x, 39);
389f17bfdaSJason A. Donenfeld }
Sigma1(uint64_t x)399f17bfdaSJason A. Donenfeld static uint64_t Sigma1(uint64_t x)
409f17bfdaSJason A. Donenfeld {
419f17bfdaSJason A. Donenfeld     return R(x, 14) ^ R(x, 18) ^ R(x, 41);
429f17bfdaSJason A. Donenfeld }
sigma0(uint64_t x)439f17bfdaSJason A. Donenfeld static uint64_t sigma0(uint64_t x)
449f17bfdaSJason A. Donenfeld {
459f17bfdaSJason A. Donenfeld     return R(x, 1) ^ R(x, 8) ^ (x >> 7);
469f17bfdaSJason A. Donenfeld }
sigma1(uint64_t x)479f17bfdaSJason A. Donenfeld static uint64_t sigma1(uint64_t x)
489f17bfdaSJason A. Donenfeld {
499f17bfdaSJason A. Donenfeld     return R(x, 19) ^ R(x, 61) ^ (x >> 6);
509f17bfdaSJason A. Donenfeld }
519f17bfdaSJason A. Donenfeld 
529f17bfdaSJason A. Donenfeld static const uint64_t K[80] = {
539f17bfdaSJason A. Donenfeld     0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL,
549f17bfdaSJason A. Donenfeld     0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
559f17bfdaSJason A. Donenfeld     0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL,
569f17bfdaSJason A. Donenfeld     0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
579f17bfdaSJason A. Donenfeld     0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL,
589f17bfdaSJason A. Donenfeld     0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
599f17bfdaSJason A. Donenfeld     0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL,
609f17bfdaSJason A. Donenfeld     0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
619f17bfdaSJason A. Donenfeld     0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL,
629f17bfdaSJason A. Donenfeld     0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
639f17bfdaSJason A. Donenfeld     0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL,
649f17bfdaSJason A. Donenfeld     0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
659f17bfdaSJason A. Donenfeld     0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL,
669f17bfdaSJason A. Donenfeld     0x92722c851482353bULL, 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
679f17bfdaSJason A. Donenfeld     0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL,
689f17bfdaSJason A. Donenfeld     0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
699f17bfdaSJason A. Donenfeld     0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL,
709f17bfdaSJason A. Donenfeld     0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
719f17bfdaSJason A. Donenfeld     0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL,
729f17bfdaSJason A. Donenfeld     0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
739f17bfdaSJason A. Donenfeld     0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL,
749f17bfdaSJason A. Donenfeld     0xc67178f2e372532bULL, 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
759f17bfdaSJason A. Donenfeld     0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL,
769f17bfdaSJason A. Donenfeld     0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
779f17bfdaSJason A. Donenfeld     0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL,
789f17bfdaSJason A. Donenfeld     0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
799f17bfdaSJason A. Donenfeld     0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
809f17bfdaSJason A. Donenfeld };
819f17bfdaSJason A. Donenfeld 
829f17bfdaSJason A. Donenfeld /* a is icv/ocv, w is a single message block. w will get reused internally. */
sha512_bda(uint64_t a[8],uint64_t w[16])839f17bfdaSJason A. Donenfeld static void sha512_bda(uint64_t a[8], uint64_t w[16])
849f17bfdaSJason A. Donenfeld {
859f17bfdaSJason A. Donenfeld     uint64_t t, z[8], b[8];
869f17bfdaSJason A. Donenfeld     int i, j;
879f17bfdaSJason A. Donenfeld 
889f17bfdaSJason A. Donenfeld     memcpy(z, a, sizeof(z));
899f17bfdaSJason A. Donenfeld     for (i = 0; i < 80; i++) {
909f17bfdaSJason A. Donenfeld         memcpy(b, a, sizeof(b));
919f17bfdaSJason A. Donenfeld 
929f17bfdaSJason A. Donenfeld         t = a[7] + Sigma1(a[4]) + Ch(a[4], a[5], a[6]) + K[i] + w[i % 16];
939f17bfdaSJason A. Donenfeld         b[7] = t + Sigma0(a[0]) + Maj(a[0], a[1], a[2]);
949f17bfdaSJason A. Donenfeld         b[3] += t;
959f17bfdaSJason A. Donenfeld         for (j = 0; j < 8; ++j) {
969f17bfdaSJason A. Donenfeld             a[(j + 1) % 8] = b[j];
979f17bfdaSJason A. Donenfeld         }
989f17bfdaSJason A. Donenfeld         if (i % 16 == 15) {
999f17bfdaSJason A. Donenfeld             for (j = 0; j < 16; ++j) {
1009f17bfdaSJason A. Donenfeld                 w[j] += w[(j + 9) % 16] + sigma0(w[(j + 1) % 16]) +
1019f17bfdaSJason A. Donenfeld                         sigma1(w[(j + 14) % 16]);
1029f17bfdaSJason A. Donenfeld             }
1039f17bfdaSJason A. Donenfeld         }
1049f17bfdaSJason A. Donenfeld     }
1059f17bfdaSJason A. Donenfeld 
1069f17bfdaSJason A. Donenfeld     for (i = 0; i < 8; i++) {
1079f17bfdaSJason A. Donenfeld         a[i] += z[i];
1089f17bfdaSJason A. Donenfeld     }
1099f17bfdaSJason A. Donenfeld }
1109f17bfdaSJason A. Donenfeld 
1119f17bfdaSJason A. Donenfeld /* a is icv/ocv, w is a single message block that needs be64 conversion. */
sha512_bda_be64(uint64_t a[8],uint64_t w[16])1129f17bfdaSJason A. Donenfeld static void sha512_bda_be64(uint64_t a[8], uint64_t w[16])
1139f17bfdaSJason A. Donenfeld {
1149f17bfdaSJason A. Donenfeld     uint64_t t[16];
1159f17bfdaSJason A. Donenfeld     int i;
1169f17bfdaSJason A. Donenfeld 
1179f17bfdaSJason A. Donenfeld     for (i = 0; i < 16; i++) {
1189f17bfdaSJason A. Donenfeld         t[i] = be64_to_cpu(w[i]);
1199f17bfdaSJason A. Donenfeld     }
1209f17bfdaSJason A. Donenfeld     sha512_bda(a, t);
1219f17bfdaSJason A. Donenfeld }
1229f17bfdaSJason A. Donenfeld 
sha512_read_icv(CPUS390XState * env,uint64_t addr,uint64_t a[8],uintptr_t ra)1239f17bfdaSJason A. Donenfeld static void sha512_read_icv(CPUS390XState *env, uint64_t addr,
1249f17bfdaSJason A. Donenfeld                             uint64_t a[8], uintptr_t ra)
1259f17bfdaSJason A. Donenfeld {
1269f17bfdaSJason A. Donenfeld     int i;
1279f17bfdaSJason A. Donenfeld 
1289f17bfdaSJason A. Donenfeld     for (i = 0; i < 8; i++, addr += 8) {
1299f17bfdaSJason A. Donenfeld         addr = wrap_address(env, addr);
1309f17bfdaSJason A. Donenfeld         a[i] = cpu_ldq_be_data_ra(env, addr, ra);
1319f17bfdaSJason A. Donenfeld     }
1329f17bfdaSJason A. Donenfeld }
1339f17bfdaSJason A. Donenfeld 
sha512_write_ocv(CPUS390XState * env,uint64_t addr,uint64_t a[8],uintptr_t ra)1349f17bfdaSJason A. Donenfeld static void sha512_write_ocv(CPUS390XState *env, uint64_t addr,
1359f17bfdaSJason A. Donenfeld                              uint64_t a[8], uintptr_t ra)
1369f17bfdaSJason A. Donenfeld {
1379f17bfdaSJason A. Donenfeld     int i;
1389f17bfdaSJason A. Donenfeld 
1399f17bfdaSJason A. Donenfeld     for (i = 0; i < 8; i++, addr += 8) {
1409f17bfdaSJason A. Donenfeld         addr = wrap_address(env, addr);
1419f17bfdaSJason A. Donenfeld         cpu_stq_be_data_ra(env, addr, a[i], ra);
1429f17bfdaSJason A. Donenfeld     }
1439f17bfdaSJason A. Donenfeld }
1449f17bfdaSJason A. Donenfeld 
sha512_read_block(CPUS390XState * env,uint64_t addr,uint64_t a[16],uintptr_t ra)1459f17bfdaSJason A. Donenfeld static void sha512_read_block(CPUS390XState *env, uint64_t addr,
1469f17bfdaSJason A. Donenfeld                               uint64_t a[16], uintptr_t ra)
1479f17bfdaSJason A. Donenfeld {
1489f17bfdaSJason A. Donenfeld     int i;
1499f17bfdaSJason A. Donenfeld 
1509f17bfdaSJason A. Donenfeld     for (i = 0; i < 16; i++, addr += 8) {
1519f17bfdaSJason A. Donenfeld         addr = wrap_address(env, addr);
1529f17bfdaSJason A. Donenfeld         a[i] = cpu_ldq_be_data_ra(env, addr, ra);
1539f17bfdaSJason A. Donenfeld     }
1549f17bfdaSJason A. Donenfeld }
1559f17bfdaSJason A. Donenfeld 
sha512_read_mbl_be64(CPUS390XState * env,uint64_t addr,uint8_t a[16],uintptr_t ra)1569f17bfdaSJason A. Donenfeld static void sha512_read_mbl_be64(CPUS390XState *env, uint64_t addr,
1579f17bfdaSJason A. Donenfeld                                  uint8_t a[16], uintptr_t ra)
1589f17bfdaSJason A. Donenfeld {
1599f17bfdaSJason A. Donenfeld     int i;
1609f17bfdaSJason A. Donenfeld 
1619f17bfdaSJason A. Donenfeld     for (i = 0; i < 16; i++, addr += 1) {
1629f17bfdaSJason A. Donenfeld         addr = wrap_address(env, addr);
1639f17bfdaSJason A. Donenfeld         a[i] = cpu_ldub_data_ra(env, addr, ra);
1649f17bfdaSJason A. Donenfeld     }
1659f17bfdaSJason A. Donenfeld }
1669f17bfdaSJason A. Donenfeld 
cpacf_sha512(CPUS390XState * env,uintptr_t ra,uint64_t param_addr,uint64_t * message_reg,uint64_t * len_reg,uint32_t type)1679f17bfdaSJason A. Donenfeld static int cpacf_sha512(CPUS390XState *env, uintptr_t ra, uint64_t param_addr,
1689f17bfdaSJason A. Donenfeld                       uint64_t *message_reg, uint64_t *len_reg, uint32_t type)
1699f17bfdaSJason A. Donenfeld {
1709f17bfdaSJason A. Donenfeld     enum { MAX_BLOCKS_PER_RUN = 64 }; /* Arbitrary: keep interactivity. */
1719f17bfdaSJason A. Donenfeld     uint64_t len = *len_reg, a[8], processed = 0;
1729f17bfdaSJason A. Donenfeld     int i, message_reg_len = 64;
1739f17bfdaSJason A. Donenfeld 
1749f17bfdaSJason A. Donenfeld     g_assert(type == S390_FEAT_TYPE_KIMD || type == S390_FEAT_TYPE_KLMD);
1759f17bfdaSJason A. Donenfeld 
1769f17bfdaSJason A. Donenfeld     if (!(env->psw.mask & PSW_MASK_64)) {
1779f17bfdaSJason A. Donenfeld         len = (uint32_t)len;
1789f17bfdaSJason A. Donenfeld         message_reg_len = (env->psw.mask & PSW_MASK_32) ? 32 : 24;
1799f17bfdaSJason A. Donenfeld     }
1809f17bfdaSJason A. Donenfeld 
1819f17bfdaSJason A. Donenfeld     /* KIMD: length has to be properly aligned. */
1829f17bfdaSJason A. Donenfeld     if (type == S390_FEAT_TYPE_KIMD && !QEMU_IS_ALIGNED(len, 128)) {
1839f17bfdaSJason A. Donenfeld         tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
1849f17bfdaSJason A. Donenfeld     }
1859f17bfdaSJason A. Donenfeld 
1869f17bfdaSJason A. Donenfeld     sha512_read_icv(env, param_addr, a, ra);
1879f17bfdaSJason A. Donenfeld 
1889f17bfdaSJason A. Donenfeld     /* Process full blocks first. */
1899f17bfdaSJason A. Donenfeld     for (; len >= 128; len -= 128, processed += 128) {
1909f17bfdaSJason A. Donenfeld         uint64_t w[16];
1919f17bfdaSJason A. Donenfeld 
1929f17bfdaSJason A. Donenfeld         if (processed >= MAX_BLOCKS_PER_RUN * 128) {
1939f17bfdaSJason A. Donenfeld             break;
1949f17bfdaSJason A. Donenfeld         }
1959f17bfdaSJason A. Donenfeld 
1969f17bfdaSJason A. Donenfeld         sha512_read_block(env, *message_reg + processed, w, ra);
1979f17bfdaSJason A. Donenfeld         sha512_bda(a, w);
1989f17bfdaSJason A. Donenfeld     }
1999f17bfdaSJason A. Donenfeld 
2009f17bfdaSJason A. Donenfeld     /* KLMD: Process partial/empty block last. */
2019f17bfdaSJason A. Donenfeld     if (type == S390_FEAT_TYPE_KLMD && len < 128) {
2029f17bfdaSJason A. Donenfeld         uint8_t x[128];
2039f17bfdaSJason A. Donenfeld 
2049f17bfdaSJason A. Donenfeld         /* Read the remainder of the message byte-per-byte. */
2059f17bfdaSJason A. Donenfeld         for (i = 0; i < len; i++) {
2069f17bfdaSJason A. Donenfeld             uint64_t addr = wrap_address(env, *message_reg + processed + i);
2079f17bfdaSJason A. Donenfeld 
2089f17bfdaSJason A. Donenfeld             x[i] = cpu_ldub_data_ra(env, addr, ra);
2099f17bfdaSJason A. Donenfeld         }
2109f17bfdaSJason A. Donenfeld         /* Pad the remainder with zero and set the top bit. */
2119f17bfdaSJason A. Donenfeld         memset(x + len, 0, 128 - len);
2129f17bfdaSJason A. Donenfeld         x[len] = 128;
2139f17bfdaSJason A. Donenfeld 
2149f17bfdaSJason A. Donenfeld         /*
2159f17bfdaSJason A. Donenfeld          * Place the MBL either into this block (if there is space left),
2169f17bfdaSJason A. Donenfeld          * or use an additional one.
2179f17bfdaSJason A. Donenfeld          */
2189f17bfdaSJason A. Donenfeld         if (len < 112) {
2199f17bfdaSJason A. Donenfeld             sha512_read_mbl_be64(env, param_addr + 64, x + 112, ra);
2209f17bfdaSJason A. Donenfeld         }
2219f17bfdaSJason A. Donenfeld         sha512_bda_be64(a, (uint64_t *)x);
2229f17bfdaSJason A. Donenfeld 
2239f17bfdaSJason A. Donenfeld         if (len >= 112) {
2249f17bfdaSJason A. Donenfeld             memset(x, 0, 112);
2259f17bfdaSJason A. Donenfeld             sha512_read_mbl_be64(env, param_addr + 64, x + 112, ra);
2269f17bfdaSJason A. Donenfeld             sha512_bda_be64(a, (uint64_t *)x);
2279f17bfdaSJason A. Donenfeld         }
2289f17bfdaSJason A. Donenfeld 
2299f17bfdaSJason A. Donenfeld         processed += len;
2309f17bfdaSJason A. Donenfeld         len = 0;
2319f17bfdaSJason A. Donenfeld     }
2329f17bfdaSJason A. Donenfeld 
2339f17bfdaSJason A. Donenfeld     /*
2349f17bfdaSJason A. Donenfeld      * Modify memory after we read all inputs and modify registers only after
2359f17bfdaSJason A. Donenfeld      * writing memory succeeded.
2369f17bfdaSJason A. Donenfeld      *
2379f17bfdaSJason A. Donenfeld      * TODO: if writing fails halfway through (e.g., when crossing page
2389f17bfdaSJason A. Donenfeld      * boundaries), we're in trouble. We'd need something like access_prepare().
2399f17bfdaSJason A. Donenfeld      */
2409f17bfdaSJason A. Donenfeld     sha512_write_ocv(env, param_addr, a, ra);
2419f17bfdaSJason A. Donenfeld     *message_reg = deposit64(*message_reg, 0, message_reg_len,
2429f17bfdaSJason A. Donenfeld                              *message_reg + processed);
2439f17bfdaSJason A. Donenfeld     *len_reg -= processed;
2449f17bfdaSJason A. Donenfeld     return !len ? 0 : 3;
2459f17bfdaSJason A. Donenfeld }
2469f17bfdaSJason A. Donenfeld 
fill_buf_random(CPUS390XState * env,uintptr_t ra,uint64_t * buf_reg,uint64_t * len_reg)247*3dbc5fdaSJason A. Donenfeld static void fill_buf_random(CPUS390XState *env, uintptr_t ra,
248*3dbc5fdaSJason A. Donenfeld                             uint64_t *buf_reg, uint64_t *len_reg)
249*3dbc5fdaSJason A. Donenfeld {
250*3dbc5fdaSJason A. Donenfeld     uint8_t tmp[256];
251*3dbc5fdaSJason A. Donenfeld     uint64_t len = *len_reg;
252*3dbc5fdaSJason A. Donenfeld     int buf_reg_len = 64;
253*3dbc5fdaSJason A. Donenfeld 
254*3dbc5fdaSJason A. Donenfeld     if (!(env->psw.mask & PSW_MASK_64)) {
255*3dbc5fdaSJason A. Donenfeld         len = (uint32_t)len;
256*3dbc5fdaSJason A. Donenfeld         buf_reg_len = (env->psw.mask & PSW_MASK_32) ? 32 : 24;
257*3dbc5fdaSJason A. Donenfeld     }
258*3dbc5fdaSJason A. Donenfeld 
259*3dbc5fdaSJason A. Donenfeld     while (len) {
260*3dbc5fdaSJason A. Donenfeld         size_t block = MIN(len, sizeof(tmp));
261*3dbc5fdaSJason A. Donenfeld 
262*3dbc5fdaSJason A. Donenfeld         qemu_guest_getrandom_nofail(tmp, block);
263*3dbc5fdaSJason A. Donenfeld         for (size_t i = 0; i < block; ++i) {
264*3dbc5fdaSJason A. Donenfeld             cpu_stb_data_ra(env, wrap_address(env, *buf_reg), tmp[i], ra);
265*3dbc5fdaSJason A. Donenfeld             *buf_reg = deposit64(*buf_reg, 0, buf_reg_len, *buf_reg + 1);
266*3dbc5fdaSJason A. Donenfeld             --*len_reg;
267*3dbc5fdaSJason A. Donenfeld         }
268*3dbc5fdaSJason A. Donenfeld         len -= block;
269*3dbc5fdaSJason A. Donenfeld     }
270*3dbc5fdaSJason A. Donenfeld }
271*3dbc5fdaSJason A. Donenfeld 
HELPER(msa)272c9274b6bSCho, Yu-Chen uint32_t HELPER(msa)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t r3,
273c9274b6bSCho, Yu-Chen                      uint32_t type)
274c9274b6bSCho, Yu-Chen {
275c9274b6bSCho, Yu-Chen     const uintptr_t ra = GETPC();
276c9274b6bSCho, Yu-Chen     const uint8_t mod = env->regs[0] & 0x80ULL;
277c9274b6bSCho, Yu-Chen     const uint8_t fc = env->regs[0] & 0x7fULL;
278c9274b6bSCho, Yu-Chen     uint8_t subfunc[16] = { 0 };
279c9274b6bSCho, Yu-Chen     uint64_t param_addr;
280c9274b6bSCho, Yu-Chen     int i;
281c9274b6bSCho, Yu-Chen 
282c9274b6bSCho, Yu-Chen     switch (type) {
283c9274b6bSCho, Yu-Chen     case S390_FEAT_TYPE_KMAC:
284c9274b6bSCho, Yu-Chen     case S390_FEAT_TYPE_KIMD:
285c9274b6bSCho, Yu-Chen     case S390_FEAT_TYPE_KLMD:
286c9274b6bSCho, Yu-Chen     case S390_FEAT_TYPE_PCKMO:
287c9274b6bSCho, Yu-Chen     case S390_FEAT_TYPE_PCC:
288c9274b6bSCho, Yu-Chen         if (mod) {
289c9274b6bSCho, Yu-Chen             tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
290c9274b6bSCho, Yu-Chen         }
291c9274b6bSCho, Yu-Chen         break;
292c9274b6bSCho, Yu-Chen     }
293c9274b6bSCho, Yu-Chen 
294c9274b6bSCho, Yu-Chen     s390_get_feat_block(type, subfunc);
295c9274b6bSCho, Yu-Chen     if (!test_be_bit(fc, subfunc)) {
296c9274b6bSCho, Yu-Chen         tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
297c9274b6bSCho, Yu-Chen     }
298c9274b6bSCho, Yu-Chen 
299c9274b6bSCho, Yu-Chen     switch (fc) {
300c9274b6bSCho, Yu-Chen     case 0: /* query subfunction */
301c9274b6bSCho, Yu-Chen         for (i = 0; i < 16; i++) {
302c9274b6bSCho, Yu-Chen             param_addr = wrap_address(env, env->regs[1] + i);
303c9274b6bSCho, Yu-Chen             cpu_stb_data_ra(env, param_addr, subfunc[i], ra);
304c9274b6bSCho, Yu-Chen         }
305c9274b6bSCho, Yu-Chen         break;
3069f17bfdaSJason A. Donenfeld     case 3: /* CPACF_*_SHA_512 */
3079f17bfdaSJason A. Donenfeld         return cpacf_sha512(env, ra, env->regs[1], &env->regs[r2],
3089f17bfdaSJason A. Donenfeld                             &env->regs[r2 + 1], type);
309*3dbc5fdaSJason A. Donenfeld     case 114: /* CPACF_PRNO_TRNG */
310*3dbc5fdaSJason A. Donenfeld         fill_buf_random(env, ra, &env->regs[r1], &env->regs[r1 + 1]);
311*3dbc5fdaSJason A. Donenfeld         fill_buf_random(env, ra, &env->regs[r2], &env->regs[r2 + 1]);
312*3dbc5fdaSJason A. Donenfeld         break;
313c9274b6bSCho, Yu-Chen     default:
314c9274b6bSCho, Yu-Chen         /* we don't implement any other subfunction yet */
315c9274b6bSCho, Yu-Chen         g_assert_not_reached();
316c9274b6bSCho, Yu-Chen     }
317c9274b6bSCho, Yu-Chen 
318c9274b6bSCho, Yu-Chen     return 0;
319c9274b6bSCho, Yu-Chen }
320