#
# Copyright (C) 2023, Advanced Micro Devices, Inc.  All rights reserved.
#
# SPDX-License-Identifier: MIT
#

QSPI_SIZE ?= "0x2280000"

# Register values
IDN_REG ?= "0x4D554241"
VERSION_REG ?= "0x1"
LENGTH_REG ?= "0x4"
PERSISTENT_REG ?= "0x01010000"

# QSPI Offsets
IMAGE_SELECTOR_OFFSET ?= "0x0"
IMAGE_SELECTOR_BACKUP_OFFSET ?= "0x80000"
PERSISTENT_REG_OFFSET ?= "0x100000"
PERSISTENT_REG_BACKUP_OFFSET ?= "0x120000"
IMAGE_A_OFFSET ?= "0x200000"
IMAGE_A_IMGSEL_OFFSET ?= "0xF00000"
IMAGE_B_OFFSET ?= "0xF80000"
IMAGE_B_IMGSEL_OFFSET ?= "0x1C80000"
IMAGE_RCVRY_OFFSET ?= "0x1E00000"
IMAGE_RCVRY_BACKUP_OFFSET ?= "0x2000000"
VERSION_OFFSET ?= "0x2240000"
CHECKSUM_OFFSET ?= "0x2250000"

def generate_spi_image(d):

    import io
    import hashlib
    import time

    qspi_size = int(d.getVar("QSPI_SIZE") or '0', 0)
    int(d.getVar("QSPI_SIZE") or '0', 0)

    # Register values
    idn_reg = int(d.getVar("IDN_REG") or '0', 0)
    version_reg = int(d.getVar("VERSION_REG") or '0', 0)
    length_reg = int(d.getVar("LENGTH_REG") or '0', 0)
    persistent_reg = int(d.getVar("PERSISTENT_REG") or '0', 0)

    # QSPI Offsets
    image_selector_offset = int(d.getVar("IMAGE_SELECTOR_OFFSET") or '0', 0)
    image_selector_backup_offset = int(d.getVar("IMAGE_SELECTOR_BACKUP_OFFSET") or '0', 0)
    persistent_reg_offset = int(d.getVar("PERSISTENT_REG_OFFSET") or '0', 0)
    persistent_reg_backup_offset = int(d.getVar("PERSISTENT_REG_BACKUP_OFFSET") or '0', 0)
    image_a_offset = int(d.getVar("IMAGE_A_OFFSET") or '0', 0)
    image_a_imgsel_offset = int(d.getVar("IMAGE_A_IMGSEL_OFFSET") or '0', 0)
    image_b_offset = int(d.getVar("IMAGE_B_OFFSET") or '0', 0)
    image_b_imgsel_offset = int(d.getVar("IMAGE_B_IMGSEL_OFFSET") or '0', 0)
    image_rcvry_offset = int(d.getVar("IMAGE_RCVRY_OFFSET") or '0', 0)
    image_rcvry_backup_offset = int(d.getVar("IMAGE_RCVRY_BACKUP_OFFSET") or '0', 0)
    version_offset = int(d.getVar("VERSION_OFFSET") or '0', 0)
    checksum_offset = int(d.getVar("CHECKSUM_OFFSET") or '0', 0)

    # QSPI data
    qspi_data = io.BytesIO()
    qspi_data.write(b'\xFF' * qspi_size)

    # Image Selector - Primary, Backup, Image A and Image B
    imgsel_file = d.getVar("DEPLOY_DIR_IMAGE")+"/imgsel-"+d.getVar("MACHINE")+".bin"
    try:
        with open(imgsel_file, "rb") as il:
            imgsel = il.read(-1)
    except OSError as err:
        bb.fatal("Unable to open imgsel file: " + str(err))

    qspi_data.seek(image_selector_offset)
    qspi_data.write(imgsel)
    qspi_data.seek(image_selector_backup_offset)
    qspi_data.write(imgsel)
    qspi_data.seek(image_a_imgsel_offset)
    qspi_data.write(imgsel)
    qspi_data.seek(image_b_imgsel_offset)
    qspi_data.write(imgsel)

    # Persistent Registers - Primary and Backup
    p_reg = [idn_reg, version_reg, length_reg, persistent_reg, \
            image_a_offset, image_b_offset, image_rcvry_offset]
    checksum = 0xffffffff - (0xffffffff & sum(p_reg))
    p_reg.insert(3, checksum)

    qspi_data.seek(persistent_reg_offset)
    for value in p_reg:
        qspi_data.write(value.to_bytes(4, byteorder="little"))

    qspi_data.seek(persistent_reg_backup_offset)
    for value in p_reg:
        qspi_data.write(value.to_bytes(4, byteorder="little"))

    # Image A and B - boot.bin
    try:
        with open(d.getVar("DEPLOY_DIR_IMAGE")+"/boot.bin", "rb") as bo:
            bootbin = bo.read(-1)
    except OSError as err:
        bb.fatal("Unable to open boot.bin file: " + str(err))

    qspi_data.seek(image_a_offset)
    qspi_data.write(bootbin)
    qspi_data.seek(image_b_offset)
    qspi_data.write(bootbin)

    # Recovery Image & Recovery Image Backup
    imgrcry_file = d.getVar("DEPLOY_DIR_IMAGE")+"/imgrcry-"+d.getVar("MACHINE")+".bin"
    try:
        with open(imgrcry_file, "rb") as iy:
            imgrcry = iy.read(-1)
    except OSError as err:
        bb.fatal("Unable to open imgrcry file: " + str(err))

    qspi_data.seek(image_rcvry_offset)
    qspi_data.write(imgrcry)
    qspi_data.seek(image_rcvry_backup_offset)
    qspi_data.write(imgrcry)

    # Version string and checksum
    version = d.getVar("QSPI_IMAGE_VERSION")
    date = time.strftime("%m%d%H%M")
    machine = d.getVar("MACHINE")[:3]
    image_name = d.getVar("QSPI_IMAGE_NAME")

    qspi_version = f"{image_name}-{machine}-v{version}-{date}\x00"
    qspi_data.seek(version_offset)
    qspi_data.write(qspi_version.encode())

    qspi_sha = hashlib.sha256(qspi_data.getbuffer())
    qspi_data.seek(checksum_offset)
    qspi_data.write(qspi_sha.digest())

    # Write the QSPI data to file
    with open(d.getVar("B") + "/" + d.getVar("IMAGE_NAME") + ".bin", "wb") as sq:
        sq.write(qspi_data.getbuffer())

do_compile[depends] += "virtual/boot-bin:do_deploy virtual/imgsel:do_deploy virtual/imgrcry:do_deploy"

python amd_spi_image_do_compile() {
    generate_spi_image(d)
}

EXPORT_FUNCTIONS do_compile
