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