/*
 * QEMU model of the Xilinx ECDSA_RSA ECDSA & RSA
 *
 * Copyright (c) 2017-2020 Xilinx Inc.
 *
 * Partially autogenerated by xregqemu.py 2017-04-04.
 * Written by Edgar E. Iglesias.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "qemu/osdep.h"
#include "qapi/error.h"
#include "hw/sysbus.h"
#include "migration/vmstate.h"
#include "hw/qdev-properties.h"
#include "hw/register.h"
#include "hw/irq.h"
#include "qemu/bitops.h"
#include "qemu/log.h"
#include "trace.h"

#include "hw/misc/ipcores-rsa5-4k.h"

#ifndef XILINX_ECDSA_RSA_ERR_DEBUG
#define XILINX_ECDSA_RSA_ERR_DEBUG 0
#endif

#ifdef ASU_RSA
#define TYPE_XILINX_ECDSA_RSA "xlnx.asu-ecdsa-rsa"
#else
#define TYPE_XILINX_ECDSA_RSA "xlnx.versal-ecdsa-rsa"
#endif

#define XILINX_ECDSA_RSA(obj) \
     OBJECT_CHECK(ECDSA_RSA, (obj), TYPE_XILINX_ECDSA_RSA)

/* Certain Windows environments incorrectly define ERROR.  */
#undef ERROR

REG32(RAM_DATA, 0x0)
REG32(RAM_ADDR, 0x4)
    FIELD(RAM_ADDR, WRRD_B, 31, 1)
    FIELD(RAM_ADDR, ADDR, 0, 31)
REG32(CTRL, 0x8)
    FIELD(CTRL, CLR_DATA_BUF, 7, 1)
    FIELD(CTRL, CLR_DONE_ABORT, 6, 1)
    FIELD(CTRL, OPCODE, 0, 3)
REG32(STATUS, 0xc)
    FIELD(STATUS, PROGRESS_CNT, 3, 5)
    FIELD(STATUS, ERROR, 2, 1)
    FIELD(STATUS, BUSY, 1, 1)
    FIELD(STATUS, DONE, 0, 1)
REG32(MINV, 0x10)
REG32(KEY_LENGTH, 0x20)
    FIELD(KEY_LENGTH, BIN_PRIME, 15, 1)
    FIELD(KEY_LENGTH, LENGTH, 0, 15)
REG32(CFG0, 0x28)
    FIELD(CFG0, QSEL, 6, 2)
    FIELD(CFG0, MULTI_PASS, 0, 6)
REG32(CFG1, 0x2c)
    FIELD(CFG1, MONT_DIGIT, 0, 8)
REG32(CFG2, 0x30)
    FIELD(CFG2, MEM_LOC_SIZE, 0, 5)
REG32(CFG3, 0x34)
    FIELD(CFG3, MONT_MOD, 4, 4)
    FIELD(CFG3, SCRATCH, 0, 4)
REG32(CFG4, 0x38)
    FIELD(CFG4, START_ADDR, 0, 8)
REG32(CFG5, 0x3c)
    FIELD(CFG5, NO_GROUPS, 0, 5)
REG32(RESET, 0x40)
    FIELD(RESET, RESET, 0, 1)
#ifndef ASU_RSA
REG32(APB_SLAVE_ERR_CTRL, 0x44)
    FIELD(APB_SLAVE_ERR_CTRL, ENABLE, 0, 1)
REG32(ECDSA_RSA_ISR, 0x48)
    FIELD(ECDSA_RSA_ISR, SLVERR, 1, 1)
    FIELD(ECDSA_RSA_ISR, DONE, 0, 1)
REG32(ECDSA_RSA_IMR, 0x4c)
    FIELD(ECDSA_RSA_IMR, SLVERR, 1, 1)
    FIELD(ECDSA_RSA_IMR, DONE, 0, 1)
REG32(ECDSA_RSA_IER, 0x50)
    FIELD(ECDSA_RSA_IER, SLVERR, 1, 1)
    FIELD(ECDSA_RSA_IER, DONE, 0, 1)
REG32(ECDSA_RSA_IDR, 0x54)
    FIELD(ECDSA_RSA_IDR, SLVERR, 1, 1)
    FIELD(ECDSA_RSA_IDR, DONE, 0, 1)
#else
REG32(ECDSA_RSA_ISR, 0x44)
    FIELD(ECDSA_RSA_ISR, SLVERR, 1, 1)
    FIELD(ECDSA_RSA_ISR, DONE, 0, 1)
REG32(ECDSA_RSA_IMR, 0x48)
    FIELD(ECDSA_RSA_IMR, SLVERR, 1, 1)
    FIELD(ECDSA_RSA_IMR, DONE, 0, 1)
REG32(ECDSA_RSA_IER, 0x4c)
    FIELD(ECDSA_RSA_IER, SLVERR, 1, 1)
    FIELD(ECDSA_RSA_IER, DONE, 0, 1)
REG32(ECDSA_RSA_IDR, 0x50)
    FIELD(ECDSA_RSA_IDR, SLVERR, 1, 1)
    FIELD(ECDSA_RSA_IDR, DONE, 0, 1)
REG32(ECDSA_RSA_ITR, 0x54)
    FIELD(ECDSA_RSA_ITR, DONE, 0, 1)
#endif
REG32(RSA_CFG, 0x58)
    FIELD(RSA_CFG, RD_ENDIANNESS, 1, 1)
    FIELD(RSA_CFG, WR_ENDIANNESS, 0, 1)
REG32(ECDSA_RSA_ECO, 0x60)

#define ECDSA_RSA_R_MAX (R_ECDSA_RSA_ECO + 1)


#define R_CTRL_nop         0x00
#define R_CTRL_exp         0x01
#define R_CTRL_mod         0x02
#define R_CTRL_mul         0x03
#define R_CTRL_rrmod       0x04
#define R_CTRL_exppre      0x05
#define R_CTRL_exec        0x06
#define R_CTRL_reserved    0x07

typedef struct ECDSA_RSA {
    SysBusDevice parent_obj;
    MemoryRegion iomem;
    qemu_irq irq_ecdsa_rsa_imr;

    struct {
        uint32_t ram_nr_words;
    } cfg;

    bool in_reset;

    IPCoresRSA rsa;
    uint8_t guard0[32 * 1024];
    /* Read-Write buffer.  */
    struct word rw_buf;
    uint8_t guard1[32 * 1024];
    uint8_t guard2[32 * 1024];

    uint32_t regs[ECDSA_RSA_R_MAX];
    RegisterInfo regs_info[ECDSA_RSA_R_MAX];
} ECDSA_RSA;

typedef int (*ALUFunc)(IPCoresRSA *, unsigned int, unsigned int);

static ALUFunc alu_ops[] = {
    [R_CTRL_nop] = rsa_do_nop,
    [R_CTRL_exp] = rsa_do_exp,
    [R_CTRL_mod] = rsa_do_mod,
    [R_CTRL_mul] = rsa_do_mul,
    [R_CTRL_rrmod] = rsa_do_rrmod,
    [R_CTRL_exppre] = rsa_do_exppre,
};

static const char *OPCODE_STR[] = {
    [R_CTRL_nop] = "nop",
    [R_CTRL_exp] = "exp",
    [R_CTRL_mod] = "mod",
    [R_CTRL_mul] = "mult",
    [R_CTRL_rrmod] = "RR mod N",
    [R_CTRL_exppre] = "exp(RR mod N)",
    [R_CTRL_exec] = "exec",
    [R_CTRL_reserved] = "reserved"
};

static void ecdsa_rsa_imr_update_irq(ECDSA_RSA *s)
{
    bool pending;
    bool done;

    /* Propagate the DONE status bit.  */
    done = ARRAY_FIELD_EX32(s->regs, STATUS, DONE);
    ARRAY_FIELD_DP32(s->regs, ECDSA_RSA_ISR, DONE, done);

    pending = s->regs[R_ECDSA_RSA_ISR] & ~s->regs[R_ECDSA_RSA_IMR];
    qemu_set_irq(s->irq_ecdsa_rsa_imr, pending);
}

static void ecdsa_rsa_isr_postw(RegisterInfo *reg, uint64_t val64)
{
    ECDSA_RSA *s = XILINX_ECDSA_RSA(reg->opaque);
    ecdsa_rsa_imr_update_irq(s);
}

static uint64_t ecdsa_rsa_ier_prew(RegisterInfo *reg, uint64_t val64)
{
    ECDSA_RSA *s = XILINX_ECDSA_RSA(reg->opaque);
    uint32_t val = val64;

    s->regs[R_ECDSA_RSA_IMR] &= ~val;
    ecdsa_rsa_imr_update_irq(s);
    return 0;
}

static uint64_t ecdsa_rsa_idr_prew(RegisterInfo *reg, uint64_t val64)
{
    ECDSA_RSA *s = XILINX_ECDSA_RSA(reg->opaque);
    uint32_t val = val64;

    s->regs[R_ECDSA_RSA_IMR] |= val;
    ecdsa_rsa_imr_update_irq(s);
    return 0;
}

static void ecdsa_rsa_reset(DeviceState *dev)
{
    ECDSA_RSA *s = XILINX_ECDSA_RSA(dev);
    unsigned int i;

    s->in_reset = true;

    for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
        register_reset(&s->regs_info[i]);
    }

    rsa_reset(&s->rsa);
    ecdsa_rsa_imr_update_irq(s);

    s->in_reset = false;
}

static uint64_t ecdsa_rsa_ram_data_prew(RegisterInfo *reg, uint64_t val)
{
    ECDSA_RSA *s = XILINX_ECDSA_RSA(reg->opaque);
    uint32_t r = (uint32_t) val;

    if (ARRAY_FIELD_EX32(s->regs, RSA_CFG, WR_ENDIANNESS)) {
        r = bswap32(r);
    }
    return r;
}

static uint64_t ecdsa_rsa_ram_data_postr(RegisterInfo *reg, uint64_t val64)
{
    ECDSA_RSA *s = XILINX_ECDSA_RSA(reg->opaque);
    uint32_t r;

    r = s->rw_buf.u32[0];
    memmove(&s->rw_buf.u32[0], &s->rw_buf.u32[1],
            sizeof s->rw_buf.u32 - sizeof s->rw_buf.u32[0]);
    if (ARRAY_FIELD_EX32(s->regs, RSA_CFG, RD_ENDIANNESS)) {
        r = bswap32(r);
    }
    return r;
}

static void ecdsa_rsa_ram_data_postw(RegisterInfo *reg, uint64_t val64)
{
    ECDSA_RSA *s = XILINX_ECDSA_RSA(reg->opaque);

    /*
     * Six writes fill the write-buffer. LSB is written first.
     * We shift down at every write.
     */
    memmove(&s->rw_buf.u32[0], &s->rw_buf.u32[1],
            sizeof s->rw_buf.u32 - sizeof s->rw_buf.u32[0]);
    s->rw_buf.u32[ARRAY_SIZE(s->rw_buf.u32) - 1] = val64;
}

static void ecdsa_rsa_ram_addr_postw(RegisterInfo *reg, uint64_t val64)
{
    ECDSA_RSA *s = XILINX_ECDSA_RSA(reg->opaque);
    uint32_t val32 = val64;
    uint32_t addr = FIELD_EX32(val32, RAM_ADDR, ADDR);


    if (addr >= s->cfg.ram_nr_words) {
        qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid RAM address %x!\n",
                      __func__, addr);
        return;
    }

    if (FIELD_EX32(val32, RAM_ADDR, WRRD_B)) {
        memcpy(&s->rsa.mem.words[addr].u8[0], &s->rw_buf.u8[0],
               sizeof s->rw_buf.u8);
        s->rsa.word_def[addr] = true;

        rsa_mem_dirty(&s->rsa, addr, 1);
    } else {
        memcpy(&s->rw_buf.u8[0], &s->rsa.mem.words[addr].u8[0],
               sizeof s->rw_buf.u8);
    }
}

static void ecdsa_rsa_run_microcode(ECDSA_RSA *s,
                                    unsigned int start_word,
                                    unsigned int digits,
                                    unsigned int loc_size,
                                    unsigned int m2_loc,
                                    bool binary)
{
    const char *BIN_INSN_STR[] = {
        [0] = "stop", [1] = "bin-montmul", [2] = "xor", [3] = "xor",
        [4] = "gf-mod", [5] = "undef-5", [6] = "undef-6", [7] = "undef-7",
        [8] = "undef-8", [9] = "undef-9", [10] = "undef-10",
        [11] = "undef-11", [12] = "undef-12", [13] = "undef-13",
        [14] = "undef-14", [15] = "undef-15",
    };

    const char *INSN_STR[] = {
        [0] = "stop", [1] = "montmul", [2] = "add", [3] = "sub",
        [4] = "mod", [5] = "undef-5", [6] = "undef-6", [7] = "undef-7",
        [8] = "undef-8", [9] = "undef-9", [10] = "undef-10",
        [11] = "undef-11", [12] = "undef-12", [13] = "undef-13",
        [14] = "undef-14", [15] = "undef-15",
    };

    unsigned int word = start_word;
    int pc = (BITS_PER_WORD / 16) - 1;
    struct {
        unsigned int result;
        unsigned int opcode;
        unsigned int args[2];
    } insn;
    uint16_t ir;

    do {
        ir = s->rsa.mem.words[word].u16[pc];

        insn.result = extract32(ir, 0, 4);
        insn.args[1] = extract32(ir, 4, 4);
        insn.args[0] = extract32(ir, 8, 4);
        insn.opcode = extract32(ir, 12, 4);

        if (insn.opcode != 0) {
            const char *insn_str = binary
                ? BIN_INSN_STR[insn.opcode]
                : INSN_STR[insn.opcode];

            trace_xlnx_versal_ecdsa_rsa_exec_ucode(word, pc,
                                                   insn_str,
                                                   digits * 4 * 8,
                                                   insn.result * loc_size,
                                                   insn.args[0] * loc_size,
                                                   insn.args[1] * loc_size);
        } else {
            trace_xlnx_versal_ecdsa_rsa_exec_stop(word, pc);
        }

        switch (insn.opcode) {
        case 0:
            break;
        case 1:
            if (binary) {
                rsa_do_bin_mont(&s->rsa,
                               insn.args[0] * loc_size, insn.args[1] * loc_size,
                               insn.result * loc_size, m2_loc * loc_size,
                               digits);
            } else {
                rsa_do_montmul(&s->rsa,
                               insn.args[0] * loc_size, insn.args[1] * loc_size,
                               insn.result * loc_size, m2_loc * loc_size,
                               digits);
            }
            break;
        case 2:
            if (binary) {
                rsa_do_xor(&s->rsa,
                           insn.args[0] * loc_size, insn.args[1] * loc_size,
                           insn.result * loc_size, m2_loc * loc_size,
                           digits);
            } else {
                rsa_do_add(&s->rsa,
                           insn.args[0] * loc_size, insn.args[1] * loc_size,
                           insn.result * loc_size, m2_loc * loc_size,
                           digits);
            }
            break;
        case 3:
            if (binary) {
                rsa_do_xor(&s->rsa,
                           insn.args[0] * loc_size, insn.args[1] * loc_size,
                           insn.result * loc_size, m2_loc * loc_size,
                           digits);
            } else {
                rsa_do_sub(&s->rsa,
                           insn.args[0] * loc_size, insn.args[1] * loc_size,
                           insn.result * loc_size, m2_loc * loc_size,
                           digits);
            }
            break;
        case 4:
            if (binary) {
                rsa_do_gf_mod(&s->rsa,
                              insn.args[0] * loc_size,
                              insn.args[1] * loc_size,
                              insn.result * loc_size, m2_loc * loc_size,
                              digits);
            } else {
                rsa_do_mod_addr(&s->rsa,
                                insn.args[0] * loc_size,
                                insn.args[1] * loc_size,
                                insn.result * loc_size, m2_loc * loc_size,
                                digits);
            }
            break;
        default:
            abort();
            break;
        };

        /* PC starts at top of word and counts down.  */
        pc--;
        if (pc < 0) {
            word++;
            pc = (BITS_PER_WORD / 16) - 1;
        }
    } while (insn.opcode != 0);
}

static void ecdsa_rsa_ctrl_postw(RegisterInfo *reg, uint64_t val64)
{
    ECDSA_RSA *s = XILINX_ECDSA_RSA(reg->opaque);
    uint32_t val32 = val64;
    unsigned int op = FIELD_EX32(val32, CTRL, OPCODE);
    bool clr_done_abort = FIELD_EX32(val32, CTRL, CLR_DONE_ABORT);
    bool clr_data_buf = FIELD_EX32(val32, CTRL, CLR_DATA_BUF);
    bool binary = ARRAY_FIELD_EX32(s->regs, KEY_LENGTH, BIN_PRIME);
    unsigned int bitlen = ARRAY_FIELD_EX32(s->regs, KEY_LENGTH, LENGTH);
    unsigned int digits = ARRAY_FIELD_EX32(s->regs, CFG1, MONT_DIGIT);
    unsigned int loc_size = ARRAY_FIELD_EX32(s->regs, CFG2, MEM_LOC_SIZE);
    unsigned int start_addr = ARRAY_FIELD_EX32(s->regs, CFG4, START_ADDR);
    unsigned int m2_loc = ARRAY_FIELD_EX32(s->regs, CFG3, MONT_MOD);
    int err;

    s->rsa.mul_pass = ARRAY_FIELD_EX32(s->regs, CFG0, MULTI_PASS);

    if (clr_data_buf) {
        memset(&s->rw_buf.u8[0], 0, sizeof s->rw_buf.u8);
    }

    if (clr_done_abort) {
        ARRAY_FIELD_DP32(s->regs, STATUS, DONE, 0);
        if (op) {
            qemu_log_mask(LOG_GUEST_ERROR, "RSA: clr-done-abort op=%x\n", op);
        }
        goto done;
    }

    if (op == R_CTRL_nop) {
        goto done;
    }

    /* Clear the ERROR status for every new OP.  */
    ARRAY_FIELD_DP32(s->regs, STATUS, ERROR, 0);

    if (op <= R_CTRL_exppre) {
        trace_xlnx_versal_ecdsa_rsa_exec_opcode(OPCODE_STR[op]);
        err = alu_ops[op](&s->rsa, bitlen, digits);
    } else {
        ecdsa_rsa_run_microcode(s, start_addr, digits,
                                loc_size, m2_loc, binary);
        err = 0;
    }

    if (err) {
        ARRAY_FIELD_DP32(s->regs, STATUS, ERROR, 1);
        qemu_log_mask(LOG_GUEST_ERROR, "RSA: Detected an error: %s\n",
                      rsa_strerror(err));
    } else {
        ARRAY_FIELD_DP32(s->regs, STATUS, DONE, 1);
    }

done:
    ecdsa_rsa_imr_update_irq(s);
}

static void ecdsa_rsa_minv_postw(RegisterInfo *reg, uint64_t val64)
{
    ECDSA_RSA *s = XILINX_ECDSA_RSA(reg->opaque);

    rsa_set_minv(&s->rsa, val64);
}

static void ecdsa_rsa_reset_postw(RegisterInfo *reg, uint64_t val64)
{
    ECDSA_RSA *s = XILINX_ECDSA_RSA(reg->opaque);

    if (!s->in_reset) {
        ecdsa_rsa_reset(DEVICE(s));
    }
}

static void ecdsa_rsa_update_r_inv(RegisterInfo *reg, uint64_t val64)
{
    ECDSA_RSA *s = XILINX_ECDSA_RSA(reg->opaque);

    s->rsa.m2_addr = ARRAY_FIELD_EX32(s->regs, CFG3, MONT_MOD);
    s->rsa.mont_digit = ARRAY_FIELD_EX32(s->regs, CFG1, MONT_DIGIT);

    rsa_invalidate_r_inv(&s->rsa);
}

static const RegisterAccessInfo ecdsa_rsa_regs_info[] = {
    {   .name = "RAM_DATA",  .addr = A_RAM_DATA,
        .post_read = ecdsa_rsa_ram_data_postr,
        .post_write = ecdsa_rsa_ram_data_postw,
        .pre_write = ecdsa_rsa_ram_data_prew,
    },{ .name = "RAM_ADDR",  .addr = A_RAM_ADDR,
        .post_write = ecdsa_rsa_ram_addr_postw,
    },{ .name = "CTRL",  .addr = A_CTRL,
        .rsvd = 0x38,
        .post_write = ecdsa_rsa_ctrl_postw,
    },{ .name = "STATUS",  .addr = A_STATUS,
        .ro = 0xff,
    },{ .name = "MINV",  .addr = A_MINV,
        .post_write = ecdsa_rsa_minv_postw,
    },{ .name = "KEY_LENGTH",  .addr = A_KEY_LENGTH,
    },{ .name = "CFG0",  .addr = A_CFG0,
    },{ .name = "CFG1",  .addr = A_CFG1,
        .post_write = ecdsa_rsa_update_r_inv,
    },{ .name = "CFG2",  .addr = A_CFG2,
    },{ .name = "CFG3",  .addr = A_CFG3,
        .post_write = ecdsa_rsa_update_r_inv,
    },{ .name = "CFG4",  .addr = A_CFG4,
    },{ .name = "CFG5",  .addr = A_CFG5,
    },{ .name = "RESET",  .addr = A_RESET,
        .reset = 0x1,
        .post_write = ecdsa_rsa_reset_postw,
#ifndef ASU_RSA
    },{ .name = "APB_SLAVE_ERR_CTRL",  .addr = A_APB_SLAVE_ERR_CTRL,
#endif
    },{ .name = "ECDSA_RSA_ISR",  .addr = A_ECDSA_RSA_ISR,
        .w1c = 0x3,
        .post_write = ecdsa_rsa_isr_postw,
    },{ .name = "ECDSA_RSA_IMR",  .addr = A_ECDSA_RSA_IMR,
        .reset = 0x3,
        .ro = 0x3,
    },{ .name = "ECDSA_RSA_IER",  .addr = A_ECDSA_RSA_IER,
        .pre_write = ecdsa_rsa_ier_prew,
    },{ .name = "ECDSA_RSA_IDR",  .addr = A_ECDSA_RSA_IDR,
        .pre_write = ecdsa_rsa_idr_prew,
    },
#ifdef ASU_RSA
    { .name = "ECDSA_RSA_ITR", .addr = A_ECDSA_RSA_ITR,
    },
#endif
    { .name = "RSA_CFG",  .addr = A_RSA_CFG,
    },{ .name = "ECDSA_RSA_ECO",  .addr = A_ECDSA_RSA_ECO,
    }
};

static void check_guard(const char *name, uint8_t *g, int size)
{
    {
        int i;

        for (i = 0; i < size; i++) {
            if (g[i]) {
                qemu_log("MEM OVERWRITE! %s[%d]=%x\n", name, i, g[i]);
            }
        }
    }
}

static void ecdsa_rsa_write(void *opaque, hwaddr addr, uint64_t value,
                      unsigned size)
{
    ECDSA_RSA *s = XILINX_ECDSA_RSA(opaque);

    register_write_memory(opaque, addr, value, size);

    check_guard("guard0", s->guard0, ARRAY_SIZE(s->guard0));
    check_guard("guard1", s->guard1, ARRAY_SIZE(s->guard1));
    check_guard("guard2", s->guard2, ARRAY_SIZE(s->guard2));
}

static const MemoryRegionOps ecdsa_rsa_ops = {
    .read = register_read_memory,
    .write = ecdsa_rsa_write,
    .write = register_write_memory,
    .endianness = DEVICE_LITTLE_ENDIAN,
    .valid = {
        .min_access_size = 1,
        .max_access_size = 4,
    },
};

static void ecdsa_rsa_realize(DeviceState *dev, Error **errp)
{
    ECDSA_RSA *s = XILINX_ECDSA_RSA(dev);

    if (s->cfg.ram_nr_words > ARRAY_SIZE(s->rsa.mem.words)) {
        error_setg(errp, "RSA: Nr of words %d is larger than MAX %zd",
                   s->cfg.ram_nr_words, ARRAY_SIZE(s->rsa.mem.words));
    }
}

static void ecdsa_rsa_init(Object *obj)
{
    ECDSA_RSA *s = XILINX_ECDSA_RSA(obj);
    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    RegisterInfoArray *reg_array;

    memory_region_init(&s->iomem, obj, TYPE_XILINX_ECDSA_RSA,
                       ECDSA_RSA_R_MAX * 4);
    reg_array =
        register_init_block32(DEVICE(obj), ecdsa_rsa_regs_info,
                              ARRAY_SIZE(ecdsa_rsa_regs_info),
                              s->regs_info, s->regs,
                              &ecdsa_rsa_ops,
                              XILINX_ECDSA_RSA_ERR_DEBUG,
                              ECDSA_RSA_R_MAX * 4);
    memory_region_add_subregion(&s->iomem,
                                0x0,
                                &reg_array->mem);
    sysbus_init_mmio(sbd, &s->iomem);
    sysbus_init_irq(sbd, &s->irq_ecdsa_rsa_imr);
}

static Property ecdsa_rsa_properties[] = {
    DEFINE_PROP_UINT32("ram-nr-words", ECDSA_RSA, cfg.ram_nr_words, 144),
    DEFINE_PROP_END_OF_LIST(),
};

static const VMStateDescription vmstate_ecdsa_rsa = {
    .name = TYPE_XILINX_ECDSA_RSA,
    .version_id = 1,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
        VMSTATE_UINT32_ARRAY(regs, ECDSA_RSA, ECDSA_RSA_R_MAX),
        VMSTATE_END_OF_LIST(),
    }
};

static void ecdsa_rsa_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);

    dc->reset = ecdsa_rsa_reset;
    dc->realize = ecdsa_rsa_realize;
    dc->vmsd = &vmstate_ecdsa_rsa;
    device_class_set_props(dc, ecdsa_rsa_properties);
}

static const TypeInfo ecdsa_rsa_info = {
    .name          = TYPE_XILINX_ECDSA_RSA,
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(ECDSA_RSA),
    .class_init    = ecdsa_rsa_class_init,
    .instance_init = ecdsa_rsa_init,
};

static void ecdsa_rsa_register_types(void)
{
    type_register_static(&ecdsa_rsa_info);
}

type_init(ecdsa_rsa_register_types)
