extern crate clap;
extern crate env_logger;
extern crate log;
extern crate regex;
extern crate serde;
extern crate serde_derive;
extern crate serde_xml_rs;

use clap::{App, Arg, SubCommand};
use lazy_static::lazy_static;
use log::debug;
use serde_derive::{Deserialize, Serialize};
use serde_json::json;
use std::fs::File;
use std::io::Write;
use std::path::Path;

// Some hardcode implicit config value. Please update the values defined in "init()" when necessary.
lazy_static! {
    static ref DEFAULT_CONFIG: DefaultConfig = DefaultConfig::init();
}

const MISC_FOR_EDMM_PLATFORM: (&str, &str) = ("1", "0xFFFFFFFF");
const MISC_FOR_NON_EDMM_PLATFORM: (&str, &str) = ("0", "0");

struct DefaultConfig {
    // Corresponds to HeapMaxSize in Enclave.xml
    kernel_heap_max_size: &'static str,
    user_space_max_size: &'static str,
    tcs_init_num: u32,
    // Corresponds to TCSMaxNum in Enclave.xml
    tcs_max_num: u32,
    // Extra user region memory for SDK
    extra_user_region_for_sdk: &'static str,
}

impl DefaultConfig {
    fn init() -> Self {
        Self {
            kernel_heap_max_size: "1024MB",
            user_space_max_size: "16GB",
            tcs_init_num: 16,
            tcs_max_num: 4096,
            extra_user_region_for_sdk: "1GB",
        }
    }
}

fn main() {
    env_logger::init();

    let instance_is_for_edmm_platform = {
        match std::env::var("INSTANCE_IS_FOR_EDMM_PLATFORM") {
            Ok(val) => val == "YES",
            _ => unreachable!(),
        }
    };

    let matches = App::new("gen_internal_conf")
        .version("0.2.0")
        // Input: JSON file which users may change
        .arg(
            Arg::with_name("user_json")
                .long("user_json")
                .value_name("input user json")
                .required(true)
                .validator(|f| match Path::new(&f).exists() {
                    true => Ok(()),
                    false => {
                        let err_message = f + " is not exist";
                        Err(err_message)
                    }
                })
                .takes_value(true),
        )
        .subcommand(
            SubCommand::with_name("gen_conf")
                .about("Generate image config")
                // Input: User's Secure Occlum FS image MAC
                .arg(
                    Arg::with_name("user_fs_mac")
                        .long("user_fs_mac")
                        .value_name("input MAC of user image fs")
                        .required(true)
                        .takes_value(true),
                )
                // Input: InitFS image MAC
                .arg(
                    Arg::with_name("init_fs_mac")
                        .long("init_fs_mac")
                        .value_name("input MAC of init image fs")
                        .required(true)
                        .takes_value(true),
                )
                // Output: JSON file used by libOS and users shouldn't touch
                .arg(
                    Arg::with_name("output_json")
                        .long("output_json")
                        .value_name("output json")
                        .required(true)
                        .validator(|f| match File::create(f) {
                            Ok(_) => Ok(()),
                            Err(e) => Err(e.to_string()),
                        })
                        .takes_value(true),
                )
                // Output: XML file used by Intel SGX SDK
                .arg(
                    Arg::with_name("sdk_xml")
                        .long("sdk_xml")
                        .value_name("output sdk's xml")
                        .required(true)
                        .validator(|f| match File::create(f) {
                            Ok(_e) => Ok(()),
                            Err(e) => Err(e.to_string()),
                        })
                        .takes_value(true),
                ),
        )
        .get_matches();

    let occlum_config_file_path = matches.value_of("user_json").unwrap();
    debug!(
        "Occlum config (json) file name {:?}",
        occlum_config_file_path
    );
    // Read the occlum configuration file
    let occlum_config_file =
        File::open(occlum_config_file_path).expect("The Occlum configuration file does not exist");
    let occlum_config: OcclumConfiguration = serde_json::from_reader(occlum_config_file)
        .expect("It is not a valid Occlum configuration file.");
    debug!("The occlum config is:{:?}", occlum_config);

    // Match subcommand
    if let Some(sub_matches) = matches.subcommand_matches("gen_conf") {
        let occlum_conf_user_fs_mac = sub_matches.value_of("user_fs_mac").unwrap();
        debug!("Occlum config user FS MAC {:?}", occlum_conf_user_fs_mac);

        let occlum_conf_init_fs_mac = sub_matches.value_of("init_fs_mac").unwrap();
        debug!("Occlum config init FS MAC {:?}", occlum_conf_init_fs_mac);

        let occlum_json_file_path = sub_matches.value_of("output_json").unwrap();
        debug!(
            "Genereated Occlum user config (json) file name {:?}",
            occlum_json_file_path
        );

        let enclave_config_file_path = sub_matches.value_of("sdk_xml").unwrap();
        debug!(
            "Enclave config (xml) file name {:?}",
            enclave_config_file_path
        );

        debug!(
            "Build on platform {} EDMM support",
            if instance_is_for_edmm_platform {
                "WITH"
            } else {
                "WITHOUT"
            }
        );
        debug!(
            "user config init num of threads = {:?}",
            occlum_config.resource_limits.init_num_of_threads
        );

        // For init TCS number, try to use the values provided by users. If not provided, use the default value
        let (tcs_init_num, tcs_min_pool, tcs_max_num) = {
            if instance_is_for_edmm_platform {
                let tcs_init_num = if let Some(ref init_num_of_threads) =
                    occlum_config.resource_limits.init_num_of_threads
                {
                    *init_num_of_threads
                } else {
                    // The user doesn't provide a value
                    std::cmp::min(DEFAULT_CONFIG.tcs_init_num,occlum_config.resource_limits.max_num_of_threads )
                };

                // For platforms with EDMM support, use the max value
                let tcs_max_num = std::cmp::max(
                    occlum_config.resource_limits.max_num_of_threads,
                    DEFAULT_CONFIG.tcs_max_num,
                );

                (tcs_init_num, tcs_init_num, tcs_max_num)
            } else {
                // For platforms without EDMM support (including SIM mode), use the "max_num_of_threads" provided by user
                let tcs_max_num = occlum_config.resource_limits.max_num_of_threads;
                (tcs_max_num, tcs_max_num, tcs_max_num)
            }
        };

        debug!(
            "tcs init num: {}, tcs_min_pool: {}, tcs_max_num: {}",
            tcs_init_num, tcs_min_pool, tcs_max_num
        );
        if tcs_init_num > tcs_max_num {
            println!(
                "init_num_of_threads: {:?}, max_num_of_threads: {:?}, wrong configuration",
                occlum_config.resource_limits.init_num_of_threads, occlum_config.resource_limits.max_num_of_threads,
            );
            return;
        }

        // get the kernel stack size
        let stack_max_size =
            parse_memory_size(&occlum_config.resource_limits.kernel_space_stack_size);
        if stack_max_size.is_err() {
            println!(
                "The kernel_space_stack_size \"{}\" is not correct.",
                occlum_config.resource_limits.kernel_space_stack_size
            );
            return;
        }

        let (kernel_heap_init_size, kernel_heap_max_size) = {
            let heap_init_size = {
                let heap_init_size =
                    parse_memory_size(&occlum_config.resource_limits.kernel_space_heap_size);
                if heap_init_size.is_err() {
                    println!(
                        "The kernel_space_heap_size \"{}\" is not correct.",
                        occlum_config.resource_limits.kernel_space_heap_size
                    );
                    return;
                }
                heap_init_size.unwrap()
            };

            let optional_config_heap_max_size = {
                if let Some(ref heap_max_size) =
                    occlum_config.resource_limits.kernel_space_heap_max_size
                {
                    let config_kernel_heap_max_size = parse_memory_size(&heap_max_size);
                    if config_kernel_heap_max_size.is_err() {
                        println!(
                            "The kernel_space_heap_max_size \"{}\" is not correct.",
                            heap_max_size
                        );
                        return;
                    }
                    config_kernel_heap_max_size.ok()
                } else {
                    None
                }
            };

            debug!(
                "optional_config_heap_max_size = {:?}",
                optional_config_heap_max_size
            );

            match optional_config_heap_max_size {
                Some(heap_max_size) => {
                    if instance_is_for_edmm_platform {
                        let heap_max_size = std::cmp::max(
                            heap_max_size,
                            parse_memory_size(DEFAULT_CONFIG.kernel_heap_max_size).unwrap(),
                        );
                        (heap_init_size, heap_max_size)
                    } else {
                        // User specified heap_max but no EDMM support, use user specified as the heap value
                        (heap_max_size, heap_max_size)
                    }
                }
                None => {
                    if instance_is_for_edmm_platform {
                        let heap_max_size = std::cmp::max(
                            heap_init_size,
                            parse_memory_size(DEFAULT_CONFIG.kernel_heap_max_size).unwrap(),
                        );
                        (heap_init_size, heap_max_size)
                    } else {
                        (heap_init_size, heap_init_size)
                    }
                }
            }
        };
        if kernel_heap_init_size > kernel_heap_max_size {
            println!(
                "kernel_space_heap_size: {:?}, kernel_space_heap_max_size: {:?}, wrong configuration",
                occlum_config.resource_limits.kernel_space_heap_size, occlum_config.resource_limits.kernel_space_heap_max_size,
            );
            return;
        }
        debug!(
            "kernel heap init size = {}, kernel heap max size = {}",
            kernel_heap_init_size, kernel_heap_max_size
        );
        assert!(kernel_heap_max_size >= kernel_heap_init_size);

        let (config_user_space_init_size, config_user_space_max_size) = {
            let user_space_init_size = {
                let user_space_init_size =
                    parse_memory_size(&occlum_config.resource_limits.user_space_size);
                if user_space_init_size.is_err() {
                    println!(
                        "The user_space_size \"{}\" is not correct.",
                        occlum_config.resource_limits.user_space_size
                    );
                    return;
                }
                user_space_init_size.unwrap()
            };

            let optional_config_user_space_max_size = {
                if let Some(ref user_space_max_size) =
                    occlum_config.resource_limits.user_space_max_size
                {
                    let config_user_space_max_size = parse_memory_size(&user_space_max_size);
                    if config_user_space_max_size.is_err() {
                        println!(
                            "The kernel_space_heap_max_size \"{}\" is not correct.",
                            user_space_max_size
                        );
                        return;
                    }
                    config_user_space_max_size.ok()
                } else {
                    None
                }
            };
            debug!(
                "optional_config_user_space_max_size = {:?}",
                optional_config_user_space_max_size
            );

            let user_space_max_size = match optional_config_user_space_max_size {
                Some(user_space_max_size) => {
                    if instance_is_for_edmm_platform {
                        std::cmp::max(
                            user_space_max_size,
                            parse_memory_size(DEFAULT_CONFIG.user_space_max_size).unwrap(),
                        )
                    } else {
                        // Without EDMM support, just use user-provided value
                        user_space_max_size
                    }
                }
                None => {
                    if instance_is_for_edmm_platform {
                        std::cmp::max(
                            user_space_init_size,
                            parse_memory_size(DEFAULT_CONFIG.user_space_max_size).unwrap(),
                        )
                    } else {
                        user_space_init_size
                    }
                }
            };
            (user_space_init_size, user_space_max_size)
        };
        if config_user_space_init_size > config_user_space_max_size {
            println!(
                "user_space_size: {:?}, user_space_max_size: {:?}, wrong configuration",
                occlum_config.resource_limits.user_space_size, occlum_config.resource_limits.user_space_max_size,
            );
            return;
        }

        debug!(
            "config user space init size = {},config user space max size = {}",
            config_user_space_init_size, config_user_space_max_size
        );
        assert!(config_user_space_init_size <= config_user_space_max_size);

        // Calculate the actual memory size for different regions
        let (reserved_mem_size, user_region_mem_size) = {
            if instance_is_for_edmm_platform {
                // For platforms with EDMM support, we need extra memory for SDK usage. This might be fixed by SGX SDK in the future.
                let extra_user_region = parse_memory_size(DEFAULT_CONFIG.extra_user_region_for_sdk);
                if extra_user_region.is_err() {
                    println!("The extra_user_region_for_sdk in default config is not correct.");
                    return;
                }
                let user_region_mem_size = if config_user_space_max_size == config_user_space_init_size {
                    // SDK still need user region to track the EMA.
                    config_user_space_max_size
                } else {
                    config_user_space_max_size + extra_user_region.unwrap()
                };

                (
                    config_user_space_init_size as u64,
                    Some(user_region_mem_size as u64),
                )
            } else {
                // For platforms without EDMM support, use the max value for the user space
                let reserved_mem_size = config_user_space_max_size;
                (reserved_mem_size as u64, None)
            }
        };

        debug!(
            "reserved memory size = {:?}, user_region_memory size = {:?}",
            reserved_mem_size, user_region_mem_size
        );

        #[cfg(feature = "ms_buffer")]
        let marshal_buffer_size = if occlum_config.resource_limits.marshal_buffer_size.is_some() {
            let marshal_buffer_size = parse_memory_size(
                occlum_config
                    .resource_limits
                    .marshal_buffer_size
                    .as_ref()
                    .unwrap(),
            );
            if marshal_buffer_size.is_err() {
                println!(
                    "The marshal_buffer_size \"{}\" is not correct.",
                    occlum_config.resource_limits.marshal_buffer_size.unwrap()
                );
                return;
            }
            marshal_buffer_size.unwrap()
        } else {
            0x10_0000
        };

        let kss_tuple = parse_kss_conf(&occlum_config);

        let (misc_select, misc_mask) = if instance_is_for_edmm_platform {
            MISC_FOR_EDMM_PLATFORM
        } else {
            MISC_FOR_NON_EDMM_PLATFORM
        };
        debug!(
            "misc_select = {:?}, misc_mask = {:?}",
            misc_select, misc_mask
        );

        // Generate the enclave configuration
        let sgx_enclave_configuration = EnclaveConfiguration {
            ProdID: occlum_config.metadata.product_id,
            ISVSVN: occlum_config.metadata.version_number,
            StackMaxSize: stack_max_size.unwrap() as u64,
            StackMinSize: stack_max_size.unwrap() as u64, // just use the same size as max size
            HeapInitSize: kernel_heap_init_size as u64,
            HeapMaxSize: kernel_heap_max_size as u64,
            HeapMinSize: kernel_heap_init_size as u64,
            TCSNum: tcs_init_num,
            TCSMinPool: tcs_min_pool,
            TCSMaxNum: tcs_max_num,
            TCSPolicy: 1,
            DisableDebug: match occlum_config.metadata.debuggable {
                true => 0,
                false => 1,
            },
            MiscSelect: misc_select.to_string(),
            MiscMask: misc_mask.to_string(),
            ReservedMemMaxSize: reserved_mem_size,
            ReservedMemMinSize: reserved_mem_size,
            ReservedMemInitSize: reserved_mem_size,
            ReservedMemExecutable: 1,
            UserRegionSize: user_region_mem_size,
            #[cfg(feature = "ms_buffer")]
            MarshalBufferSize: marshal_buffer_size as u64,
            EnableKSS: kss_tuple.0,
            ISVEXTPRODID_H: kss_tuple.1,
            ISVEXTPRODID_L: kss_tuple.2,
            ISVFAMILYID_H: kss_tuple.3,
            ISVFAMILYID_L: kss_tuple.4,
            PKRU: occlum_config.metadata.pkru,
            AMX: occlum_config.metadata.amx,
        };
        let enclave_config = serde_xml_rs::to_string(&sgx_enclave_configuration).unwrap();
        debug!("The enclave config:{:?}", enclave_config);

        // Generate app config, including "init" and user app
        let app_config = {
            let app_config = gen_app_config(
                occlum_config.entry_points,
                occlum_config.mount,
                occlum_conf_user_fs_mac.to_string(),
                occlum_conf_init_fs_mac.to_string(),
            );
            if app_config.is_err() {
                println!("Mount configuration invalid: {:?}", app_config);
                return;
            }
            app_config.unwrap()
        };

        let occlum_json_config = InternalOcclumJson {
            resource_limits: InternalResourceLimits {
                user_space_init_size: config_user_space_init_size.to_string() + "B",
                user_space_max_size: config_user_space_max_size.to_string() + "B",
            },
            process: OcclumProcess {
                default_stack_size: occlum_config.process.default_stack_size,
                default_heap_size: occlum_config.process.default_heap_size,
                default_mmap_size: occlum_config.process.default_mmap_size,
            },
            env: occlum_config.env,
            app: app_config,
        };

        let occlum_json_str = serde_json::to_string_pretty(&occlum_json_config).unwrap();
        debug!("The Occlum.json config:\n{:?}", occlum_json_str);

        // Update the output file
        let mut enclave_config_file = File::create(enclave_config_file_path)
            .expect("Could not open the target Enclave configuration file.");
        enclave_config_file
            .write_all(enclave_config.as_bytes())
            .expect("Failed to update the Enclave configuration file.");

        let mut occlum_json = File::create(occlum_json_file_path)
            .expect("Could not open the output Occlum.json file.");
        occlum_json
            .write_all(occlum_json_str.as_bytes())
            .expect("Failed to update the output Occlum.json file.");
    } else {
        unreachable!();
    }
}

fn parse_memory_size(mem_str: &str) -> Result<usize, &str> {
    const UNIT2FACTOR: [(&str, usize); 5] = [
        ("KB", 1024),
        ("MB", 1024 * 1024),
        ("GB", 1024 * 1024 * 1024),
        ("TB", 1024 * 1024 * 1024 * 1024),
        ("B", 1),
    ];

    // Extract the unit part of the memory size
    let mem_str = mem_str.trim();
    let (mem_unit, unit_factor) = UNIT2FACTOR
        .iter()
        .position(|(mem_unit, _)| mem_str.ends_with(mem_unit))
        .ok_or("No unit")
        .map(|unit_i| &UNIT2FACTOR[unit_i])?;

    // Extract the value part of the memory size
    let mem_val = match mem_str[0..mem_str.len() - mem_unit.len()]
        .trim()
        .parse::<usize>()
    {
        Err(_) => {
            return Err("No number");
        }
        Ok(mem_val) => mem_val,
    };

    Ok(mem_val * unit_factor)
}

fn get_u64_id_high_and_low(id: &OcclumMetaID) -> (u64, u64) {
    let id_high = u64::from_str_radix(id.high.trim_start_matches("0x"), 16)
        .expect("64 bit hex string ID required, such as 0x1234567812345678");
    let id_low = u64::from_str_radix(id.low.trim_start_matches("0x"), 16)
        .expect("64 bit hex string ID required, such as 0x1234567812345678");

    (id_high, id_low)
}

// Return a tuple (EnableKSS, ISVEXTPRODID_H, ISVEXTPRODID_L, ISVFAMILYID_H, ISVFAMILYID_L)
fn parse_kss_conf(occlum_config: &OcclumConfiguration) -> (u32, u64, u64, u64, u64) {
    match occlum_config.metadata.enable_kss {
        true => {
            let ext_prod_id = get_u64_id_high_and_low(&occlum_config.metadata.ext_prod_id);
            let family_id = get_u64_id_high_and_low(&occlum_config.metadata.family_id);

            (1, ext_prod_id.0, ext_prod_id.1, family_id.0, family_id.1)
        }
        false => (0, 0, 0, 0, 0),
    }
}

fn gen_app_config(
    entry_points: serde_json::Value,
    mount_conf: Vec<OcclumMount>,
    occlum_conf_user_fs_mac: String,
    occlum_conf_init_fs_mac: String,
) -> Result<serde_json::Value, &'static str> {
    let mut app_config: serde_json::Value = json!({
        "app": [
        {
            "stage": "init",
            "entry_points": [
                "/bin"
            ],
            "mount": [
                {
                    "target": "/",
                    "type": "unionfs",
                    "options": {
                        "layers": [
                            {
                                "target": "/",
                                "type": "sefs",
                                "source": "./build/initfs/__ROOT",
                                "options": {
                                    "MAC": ""
                                }
                            },
                            {
                                "target": "/",
                                "type": "sefs",
                                "source": "./run/initfs/__ROOT"
                            }
                        ]
                    }
                },
                {
                    "target": "/proc",
                    "type": "procfs"
                },
                {
                    "target": "/dev",
                    "type": "devfs"
                }
            ]
        },
        {
            "stage": "app",
            "entry_points": [],
            "mount": [
                {
                    "target": "/proc",
                    "type": "procfs"
                },
                {
                    "target": "/dev",
                    "type": "devfs"
                }
            ]
        }]
    });

    // Update init root mount fs MAC
    *app_config
        .pointer_mut("/app/0/mount/0/options/layers/0/options/MAC")
        .unwrap() = serde_json::Value::String(occlum_conf_init_fs_mac);

    // Update app entry points
    *app_config.pointer_mut("/app/1/entry_points").unwrap() = entry_points;

    debug!("User provided root mount config: {:?}", mount_conf);
    let mut root_mount_config = mount_conf;

    //Check the validity of the user provided root mount
    let root_mc = &mut root_mount_config
        .iter_mut()
        .find(|m| m.target == String::from("/") && m.type_ == String::from("unionfs"))
        .ok_or("the root UnionFS is not valid")?;
    if root_mc.options.layers.is_none() {
        return Err("the root UnionFS must be given layers");
    }
    let mut root_image_sefs_mc = root_mc
        .options
        .layers
        .as_mut()
        .unwrap()
        .iter_mut()
        .find(|m| {
            m.target == String::from("/")
                && m.type_ == String::from("sefs")
                && m.options.mac.is_some()
        })
        .ok_or("the image SEFS in layers is not valid")?;
    // Update app root mount fs MAC
    root_image_sefs_mc.options.mac = Some(occlum_conf_user_fs_mac);

    // Combine the user provided mount
    let mut mount_json = serde_json::to_value(root_mount_config).unwrap();
    let mut mount_array = mount_json.as_array_mut().unwrap();
    app_config["app"][1]["mount"]
        .as_array_mut()
        .unwrap()
        .append(&mut mount_array);

    debug!("Occlum.json app config:\n{:?}", app_config);

    Ok(app_config["app"].to_owned())
}

#[derive(Debug, PartialEq, Deserialize)]
struct OcclumConfiguration {
    resource_limits: OcclumResourceLimits,
    process: OcclumProcess,
    entry_points: serde_json::Value,
    env: serde_json::Value,
    metadata: OcclumMetadata,
    mount: Vec<OcclumMount>,
}

#[derive(Debug, PartialEq, Deserialize)]
struct OcclumResourceLimits {
    #[serde(default)]
    init_num_of_threads: Option<u32>,
    max_num_of_threads: u32,
    kernel_space_heap_size: String,
    #[serde(default)]
    kernel_space_heap_max_size: Option<String>,
    kernel_space_stack_size: String,
    user_space_size: String,
    #[serde(default)]
    user_space_max_size: Option<String>,
    #[cfg(feature = "ms_buffer")]
    marshal_buffer_size: Option<String>,
}

#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
struct OcclumProcess {
    default_stack_size: String,
    default_heap_size: String,
    default_mmap_size: String,
}

#[derive(Debug, PartialEq, Deserialize)]
struct OcclumMetaID {
    high: String,
    low: String,
}

#[derive(Debug, PartialEq, Deserialize)]
struct OcclumMetadata {
    product_id: u32,
    version_number: u32,
    debuggable: bool,
    enable_kss: bool,
    family_id: OcclumMetaID,
    ext_prod_id: OcclumMetaID,
    #[serde(default)]
    pkru: u32,
    #[serde(default)]
    amx: u32,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
struct OcclumMount {
    #[serde(rename = "type")]
    type_: String,
    target: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    source: Option<String>,
    #[serde(default, skip_serializing_if = "is_default")]
    options: OcclumMountOptions,
}

#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
struct OcclumMountOptions {
    #[serde(rename = "MAC")]
    #[serde(default, skip_serializing_if = "Option::is_none")]
    mac: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub layers: Option<Vec<OcclumMount>>,
    #[serde(default, skip_serializing_if = "is_false")]
    pub temporary: bool,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub cache_size: Option<String>,
}

#[inline]
fn is_false(v: &bool) -> bool {
    !(*v)
}

#[inline]
fn is_default(option: &OcclumMountOptions) -> bool {
    let default_option: OcclumMountOptions = Default::default();
    option == &default_option
}

#[allow(non_snake_case)]
#[derive(Debug, PartialEq, Serialize)]
struct EnclaveConfiguration {
    ProdID: u32,
    ISVSVN: u32,
    StackMaxSize: u64,
    StackMinSize: u64,
    HeapInitSize: u64,
    HeapMaxSize: u64,
    HeapMinSize: u64,
    TCSNum: u32,
    TCSMaxNum: u32,
    TCSMinPool: u32,
    TCSPolicy: u32,
    DisableDebug: u32,
    MiscSelect: String,
    MiscMask: String,
    ReservedMemMaxSize: u64,
    ReservedMemMinSize: u64,
    ReservedMemInitSize: u64,
    ReservedMemExecutable: u32,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    UserRegionSize: Option<u64>,
    #[cfg(feature = "ms_buffer")]
    MarshalBufferSize: u64,
    EnableKSS: u32,
    ISVEXTPRODID_H: u64,
    ISVEXTPRODID_L: u64,
    ISVFAMILYID_H: u64,
    ISVFAMILYID_L: u64,
    PKRU: u32,
    AMX: u32,
}

#[derive(Debug, PartialEq, Clone, Serialize)]
struct InternalResourceLimits {
    user_space_init_size: String,
    user_space_max_size: String,
}

#[derive(Debug, PartialEq, Clone, Serialize)]
struct InternalOcclumJson {
    resource_limits: InternalResourceLimits,
    process: OcclumProcess,
    env: serde_json::Value,
    app: serde_json::Value,
}
