/*
 * Copyright (c) 2020, MediaTek Inc. All rights reserved.
 *
 * This software/firmware and related documentation ("MediaTek Software") are
 * protected under relevant copyright laws.
 * The information contained herein is confidential and proprietary to
 * MediaTek Inc. and/or its licensors.
 * Except as otherwise provided in the applicable licensing terms with
 * MediaTek Inc. and/or its licensors, any reproduction, modification, use or
 * disclosure of MediaTek Software, and information contained herein, in whole
 * or in part, shall be strictly prohibited.
*/

#include <command.h>
#include <common.h>
#include <stdio.h>
#include <debug_impl.h>
#include <utility.h>
#include <dm.h>
#include <led.h>
#include <dm/uclass-internal.h>
#include <iniparser.h>
#include <malloc.h>
#include <upgrade_utility.h>
#include <partition.h>

#define CERTIFICATE_SIZE 0x800000 //8MB
#define CERTIFICATE_IMAGE "certificate.img"
#define FSW_PKG_PARTITION "fsw_pkg"
#define CERT_BUF_ADDRESS 0x30700000
#define FSW_RET_SUCCESS 0
#define FSW_RET_FAIL -1


static char* def_create_scripts[]={
    "gpt clean mmc 0\n \
     gpt create mmc 0 name=certificate,size=0x800000\n \
     gpt create mmc 0 name=persist,size=0x2000000\n \
     gpt create mmc 0 name=config,size=0x2000000\n \
     gpt create mmc 0 name=bootdata_a,size=0x2000000\n \
     gpt create mmc 0 name=bootdata_b,size=0x2000000\n \
     gpt create mmc 0 name=vbmeta_a,size=0x100000\n \
     gpt create mmc 0 name=vbmeta_b,size=0x100000\n \
     gpt create mmc 0 name=tvconfig_a,size=0x1F400000\n \
     gpt create mmc 0 name=tvconfig_b,size=0x1F400000\n \
     gpt create mmc 0 name=misc,size=0x00100000\n \
     mw.b 0x30700000 0 0x100000;partition write mmc 0 misc 0x30700000 0x100000\n \
     gpt create mmc 0 name=boot_a,size=0x3600000\n \
     gpt create mmc 0 name=boot_b,size=0x3600000\n \
     gpt create mmc 0 name=vendor_boot_a,size=0x3000000\n \
     gpt create mmc 0 name=vendor_boot_b,size=0x3000000\n \
     gpt create mmc 0 name=metadata,size=0x1000000\n \
     gpt create mmc 0 name=dtbo_a,size=0x2000000\n \
     gpt create mmc 0 name=dtbo_b,size=0x2000000\n \
     gpt create mmc 0 name=super,size=0x11C600000\n \
     gpt create mmc 0 name=cache,size=0x500000\n \
     gpt create mmc 0 name=oem_a,size=0xA00000\n \
     gpt create mmc 0 name=oem_b,size=0xA00000\n \
     gpt create mmc 0 name=optee_a,size=0x1600000\n \
     gpt create mmc 0 name=optee_b,size=0x1600000\n \
     gpt create mmc 0 name=armfw_a,size=0x100000\n \
     gpt create mmc 0 name=armfw_b,size=0x100000\n \
     gpt create mmc 0 name=persist_cust,size=0x3200000\n \
     gpt create mmc 0 name=demo,size=0x40000000\n \
     gpt create_adaptive mmc 0 userdata 0x1000"
    };

int fsw_check_partitions_exist(void)
{
    char partition_list[][20] = {"mboot_a","mboot_b","uenv","certificate","persist",
                                 "config","bootdata_a","bootdata_b","vbmeta_a","vbmeta_b",
                                 "tvconfig_a","tvconfig_b","misc","boot_a","boot_b",
                                 "vendor_boot_a","vendor_boot_b","metadata","dtbo_a","dtbo_b",
                                 "super","cache","oem_a","oem_b","optee_a","optee_b",
                                 "armfw_a","armfw_b","persist_cust","demo","userdata"};
    char cmd[COMMAND_BUF_SIZE] = {0};
    int snprintf_len = 0;
    int count = sizeof(partition_list) / sizeof(partition_list[0]);

    for(int i = 0; i < count; i++)
    {
        memset(cmd, 0, COMMAND_BUF_SIZE);
        snprintf_len = snprintf(cmd, sizeof(cmd) - 1, "partition read mmc 0 %s 0x30700000 0x200", partition_list[i]);
        if(snprintf_len < 0 || (unsigned)snprintf_len >= (sizeof(cmd) - 1))
        {
            UBOOT_ERROR("The array size is too small(%lu), snprintf fail 'partition read mmc 0 %s 0x30700000 0x200'.\n", sizeof(cmd), partition_list[i]);
            return FSW_RET_FAIL;
        }
        UBOOT_DEBUG("%s:%s:%d command '%s'\n", __FILE__, __func__, __LINE__, cmd);
        if(run_command(cmd, 0) != CMD_RET_SUCCESS)
        {
            UBOOT_ERROR("%s:%s:%d command '%s' fail\n", __FILE__, __func__, __LINE__, cmd);
            return FSW_RET_FAIL;
        }
    }

    return FSW_RET_SUCCESS;

}

int fsw_create_partition(void)
{
    char *script = NULL;
    char *next_line = NULL;
    size_t def_create_scripts_size = 0;
    char *tmp_script_buf = NULL;
    int ret = 0;

    UBOOT_TRACE("IN\n");

    for (int i = 0; i < sizeof(def_create_scripts)/sizeof(char*); i++) {
        def_create_scripts_size += strlen(def_create_scripts[i]);
    }
    printf("\n def_create_scripts_size = %ld \n",def_create_scripts_size);
    script = malloc(def_create_scripts_size+2);  //+2 because need to add '\n' & '%' in the end of script
    if (script == NULL)
    {
        UBOOT_ERROR("Malloc mem fail \n");
        return FSW_RET_FAIL;
    }
    memset(script,0,def_create_scripts_size);

    /*script = read_storage_file_to_memory("tvconfig", "fsw_set_partition", &size);
	if( script== NULL)
	{
	    //Use default scripts
	    memcpy(script,*def_create_scripts,REE_SCRIPT_SIZE);
	}*/

    memcpy(script,*def_create_scripts,def_create_scripts_size);
    tmp_script_buf = script;
    tmp_script_buf[def_create_scripts_size] = '\n'; // make sure there is an ending symbol in the end of script
    tmp_script_buf[def_create_scripts_size+1] = '%'; // make sure there is an ending symbol in the end of script

    UBOOT_DEBUG("\n scripts=%s \n",script);

    while((next_line = get_script_next_line(&tmp_script_buf)) != NULL && ((*next_line) != '\0'))
    {
        UBOOT_DEBUG("\n>> %s \n", next_line);
        if(check_skip_cmd(next_line) == 0)
        {
            ret = run_command(next_line, 0);
            if(ret != 0)
            {
                UBOOT_ERROR("Error: '%s' fail\n", next_line);
                return FSW_RET_FAIL;
            }
        }
    }

    if(script != NULL)
    {
        free(script);
        script = NULL;
    }

    UBOOT_INFO("\n########## Execute script succeed ##########\n\n");
    UBOOT_TRACE("OK\n");

    return FSW_RET_SUCCESS;
}

int fsw_set_emmc_register(void)
{
    int ret = FSW_RET_SUCCESS;

    //Set eMMC HW reset enable.
    if(run_command("mmc rst-function 0 1",0) != 0)
    {
        UBOOT_ERROR("\n set ECSD[162](HW reset function) to 0x01 failed. \n");
        ret = FSW_RET_FAIL;
    }
    //Set eMMC to x8 bit width boot mode.
    if(run_command("mmc bootbus 0 2 0 0",0) != 0)
    {
        UBOOT_ERROR("\n set ECSD[177](eMMC x8 bit width boot mode) failed. \n");
        ret = FSW_RET_FAIL;
    }
    //set eMMC boot partition to boot2 for eMMC FSW boot up
    if(run_command("mmc partconf 0 0 2 0",0) != 0)
    {
        UBOOT_ERROR("\n set ECSD[179](set eMMC boot partition to boot2 for eMMC FSW boot up) failed. \n");
        ret = FSW_RET_FAIL;
    }

    return ret;
}

int fsw_format_certifcate_partition(void)
{
    //Flow:get certificate.img from fsw_pkg partition and write to eMMC.
    int ret = 0;
    loff_t size;
    const char *str_interface = "mmc";
    const char *str_device = "0";
    const char *str_partition_name = "certificate";
    const char *str_address = "0x30700000";
    const char *str_size = "0x800000";
    unsigned char* cert_buf = NULL;

    //Step 1, malloc memory to store certificate.img.
    cert_buf = read_storage_file_to_memory(FSW_PKG_PARTITION, CERTIFICATE_IMAGE, &size);
    if(cert_buf != NULL){
        printf("Size of certificate is: 0x%llx \n",(unsigned long long)size);
        //Step 2, write data to certificate partition.
        memcpy((void *)CERT_BUF_ADDRESS, cert_buf, size);
        ret = do_partition_write(str_interface,str_device,str_partition_name,str_address,str_size,NULL);
        if(ret == CMD_RET_SUCCESS){
            printf("USB boot format certificate partition success.\n");
        }
        else{
            printf("USB boot format certificate partition failed, ret=%d\n",ret);
        }
        free(cert_buf);
        cert_buf = NULL;
    }
    return ret;
}

int fsw_check_certificate_ext4(void)
{
    //Partition number of certificate is 4.
    if(run_command("ext4ls mmc 0:4",0) != 0)
    {
        UBOOT_ERROR("\n Certificate partition NOT formated to ext4, need to format it. \n");
        return FSW_RET_FAIL;
    }
    return FSW_RET_SUCCESS;
}

int do_fsw_create_partition(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{

    int ret = 0 ;

    ret = fsw_check_partitions_exist();
    if(ret != FSW_RET_SUCCESS)
    {
        ret = fsw_create_partition();
    }
    ret = fsw_check_certificate_ext4();
    if(ret == FSW_RET_FAIL)
    {
        ret = fsw_format_certifcate_partition();
    }
    ret = fsw_set_emmc_register();

    return ret;
}

U_BOOT_CMD(
    fsw_create_partition, 1, 0, do_fsw_create_partition,
    "Use for fsw mode create partition \n",
    "command: fsw_create_partition\n"
);
