/*
 * Generic interface to implementation(s) of pseudorandom number
 * generators for AMD/Xilinx random number generator devices.
 *
 * The interface is somewhat following NIST SP800-90a; however,
 * the following parameters are fixed by implementation instead
 * of caller specified:
 * > security strength
 * > derivation function, if any
 * > prediction-resistence, if supported
 *
 * Although the interface is modelled after SP800-90a, an actual
 * implementation may or may not be of SP800-90a, and may not
 * even be of crypto strength at all.
 *
 * Copyright (c) 2024 Advanced Micro Devices, Inc.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */
#ifndef HW_MISC_XLNX_PRNG_IF_H
#define HW_MISC_XLNX_PRNG_IF_H

#include "qemu/bswap.h"
#include "qom/object.h"

#define TYPE_XLNX_PRNG_IF "xlnx-prng-if"

typedef struct XlnxPRngIfClass XlnxPRngIfClass;
typedef struct XlnxPRngIf XlnxPRngIf;

DECLARE_CLASS_CHECKERS(XlnxPRngIfClass, XLNX_PRNG_IF, TYPE_XLNX_PRNG_IF)
#define XLNX_PRNG_IF(O) INTERFACE_CHECK(XlnxPRngIf, (O), TYPE_XLNX_PRNG_IF)

/**
 * find_bits_changed - Returns a mask of bits changed.
 * @ref_bits: the reference bits against which the test is made.
 * @chk_bits: the bits to be checked.
 */
static inline unsigned long find_bits_changed(unsigned long ref_bits,
                                              unsigned long chk_bits)
{
    return ref_bits ^ chk_bits;
}

/**
 * find_bits_to_1 - Returns a mask of bits changed from 0 to 1.
 * @ref_bits: the reference bits against which the test is made.
 * @chk_bits: the bits to be checked.
 */
static inline unsigned long find_bits_to_1(unsigned long ref_bits,
                                           unsigned long chk_bits)
{
    return find_bits_changed(ref_bits, chk_bits) & chk_bits;
}

/**
 * find_bits_to_0 - Returns a mask of bits changed from 1 to 0.
 * @ref_bits: the reference bits against which the test is made.
 * @chk_bits: the bits to be checked.
 */
static inline unsigned long find_bits_to_0(unsigned long ref_bits,
                                           unsigned long chk_bits)
{
    return find_bits_to_1(chk_bits, ref_bits);
}

/**
 * struct XlnxPRngIfClass:
 * @instantiate: This function places the PRNG into an initial state,
 *   with 1st seed applied. The input is seed_material instead of nonce
 *   and personalization_string. Thus, this function is more like the
 *   latter part of "instantiate" of sp800-90a, 9.1, with a call to
 *   @gen_seed to prepare the seed material first to have a complete
 *   "instantiate".
 * @uninstantiate: This function places the PRNG into a non-operational
 *   state as defined by "uninstantiate" of sp800-90a, 9.4, with all
 *   internal resources released. However, the object remains valid.
 * @reseed: This function reseeds the given PRNG instance as defined
 *   by "reseed" of sp800-90a, 9.2, but with input being seed_material
 *   obtained through @gen_seed.
 * @generate: This function generates the requested number of random
 *   octets. Unlike "generation" of sp800-90a, 9.3, generated values
 *   are not returned but should be retrieved by @get_data calls.
 * @get_data: This function retrieves the requested number of random
 *   octets generated by a previous call to @generate.
 *   If applicable, the returned octets are such that [0] be the 1st
 *   8 bits generated, and [bcnt - 1] is the last generated. The
 *   entire octet array can also be viewed as a big-endian number of
 *   (8 * bcnt) bits.
 * @gen_seed: This function can be called to create the seed material
 *   as required by @instantiate and @reseed, from input data of
 *   arbitary length.  The caller must release the returned GArray.
 *
 * A concrete class must implement all interface methods, i.e., non-NULL.
 */
struct XlnxPRngIfClass {
    InterfaceClass parent;

    void (*instantiate)(XlnxPRngIf *h, const GArray *seed);
    void (*reseed)(XlnxPRngIf *h, const GArray *seed);
    void (*uninstantiate)(XlnxPRngIf *h);
    void (*generate)(XlnxPRngIf *h, size_t bcnt, const void *adi, size_t alen);
    ssize_t (*get_data)(XlnxPRngIf *h, void *out, size_t bcnt);
    GArray *(*gen_seed)(XlnxPRngIf *h, const void *input, size_t len);
};

/**
 * xlnx_prng_get_entropy - return entropy bits as an array.
 * @len: number of entropy bytes to return
 * @fake_ctx: a 64-bit context to generate fake (deterministic) entropy
 * @fake_ent: if non-NULL and non-zero, the returned entropy is fake.
 *
 * Generate a byte array of entropy, typically part of input to a PRNG
 * implementation's create(), reseed(), or gen_seed() method. When
 * @fake_ent is non-NULL and non-zero and @fake_cnt is non-NULL, the
 * returned entropy is fake (i.e., deterministic); this is typically
 * used by a device to behave like getting entropy while it needs
 * to reproduce/debug data-dependent defects.
 */
GArray *xlnx_prng_get_entropy(size_t len, uint64_t *fake_ctx,
                              const uint64_t *fake_ent);

/**
 * xlnx_prng_ldn_be_p - return a uint64_t value from big-endian
 * octet array with 1 to 8 elements.
 * @p: base of the octet array
 * @n: the number of octets in array @p
 */
static inline uint64_t xlnx_prng_ldn_be_p(const void *p, size_t n)
{
    uint64_t u64 = 0;

    /* ldn_be_p() supports only power-of-2 lengths */
    n = n > 8 ? 8 : n;
    while (n--) {
        u64 = (u64 << 8) | *(const uint8_t *)p++;
    }

    return u64;
}

/**
 * xlnx_prng_be32_to_cpus - In-place big-endian to host conversion of
 * octet array as an array of 32-bit values.
 * @p: the base of octet array to be converted
 * @n: the number of octets in the array @p
 *
 * If (@n % 4) == 3, the last fragment is treated as 24-bit be.
 * If (@n % 4) == 2, the last fragment is treated as 16-bit be.
 */
static inline void xlnx_prng_be32_to_cpus(void *p, size_t n)
{
    void *e;

    /* be*_to_cpus() do not support unaligned pointer  */
    for (e = p + ROUND_DOWN(n, 4); p < e; p += 4) {
        stl_he_p(p, ldl_be_p(p));
    }

    switch (n % 4) {
    case 3:
        n = ldub_p(p);
        stb_p(p, ldub_p(p + 2));
        stb_p(p + 2, n);
        break;
    case 2:
        stw_he_p(p, lduw_be_p(p));
        break;
    }
}

#endif
