// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved.
//
// StratoVirt is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan
// PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//         http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.

use super::aml_compiler::AmlBuilder;
use util::byte_code::ByteCode;

/// Offset of checksum field in ACPI table.
pub const TABLE_CHECKSUM_OFFSET: u32 = 9;
pub const INTERRUPT_PPIS_COUNT: u32 = 16;
pub const INTERRUPT_SGIS_COUNT: u32 = 16;
/// GTDT irq number for timer.
pub const ACPI_GTDT_ARCH_TIMER_VIRT_IRQ: u32 = 11;
pub const ACPI_GTDT_ARCH_TIMER_S_EL1_IRQ: u32 = 13;
pub const ACPI_GTDT_ARCH_TIMER_NS_EL1_IRQ: u32 = 14;
pub const ACPI_GTDT_ARCH_TIMER_NS_EL2_IRQ: u32 = 10;
pub const ACPI_GTDT_INTERRUPT_MODE_LEVEL: u32 = 0;
pub const ACPI_GTDT_CAP_ALWAYS_ON: u32 = 4;
/// IORT node types, reference: ARM Document number: ARM DEN 0049B, October 2015.
pub const ACPI_IORT_NODE_ITS_GROUP: u8 = 0x00;
pub const ACPI_IORT_NODE_PCI_ROOT_COMPLEX: u8 = 0x02;
/// Root Complex Node in IORT
pub const ROOT_COMPLEX_ENTRY_SIZE: u16 = 36;
pub const ID_MAPPING_ENTRY_SIZE: u16 = 20;
/// Interrupt controller structure types for MADT.
pub const ACPI_MADT_GENERIC_CPU_INTERFACE: u8 = 11;
pub const ACPI_MADT_GENERIC_DISTRIBUTOR: u8 = 12;
pub const ACPI_MADT_GENERIC_REDISTRIBUTOR: u8 = 14;
pub const ACPI_MADT_GENERIC_TRANSLATOR: u8 = 15;

#[repr(C, packed)]
#[derive(Default, Copy, Clone)]
pub struct AcpiGenericAddress {
    space_id: u8,
    bit_width: u8,
    bit_offset: u8,
    access_size: u8,
    address: u64,
}

impl AcpiGenericAddress {
    pub fn new_io_address<T: Into<u64>>(addr: T) -> AcpiGenericAddress {
        AcpiGenericAddress {
            space_id: 1,
            bit_width: 8 * std::mem::size_of::<T>() as u8,
            bit_offset: 0,
            access_size: std::mem::size_of::<T>() as u8,
            address: addr.into(),
        }
    }
}

impl ByteCode for AcpiGenericAddress {}

impl AmlBuilder for AcpiGenericAddress {
    fn aml_bytes(&self) -> Vec<u8> {
        self.as_bytes().to_vec()
    }
}

/// The common ACPI table header.
#[repr(C, packed)]
#[derive(Default, Copy, Clone)]
pub struct AcpiTableHeader {
    /// Signature of this table.
    signature: [u8; 4],
    /// The total length of this table, including this header.
    length: u32,
    /// The revision of this table.
    revision: u8,
    /// The checksum of this table, including this header.
    checksum: u8,
    /// OEM ID.
    oem_id: [u8; 6],
    /// OEM table ID.
    oem_table_id: [u8; 8],
    /// OEM revision of this table.
    oem_revision: u32,
    /// Vendor ID for the ASL Compiler, default zero.
    asl_compiler_id: [u8; 4],
    /// Revision number of the ASL Compiler, default zero.
    asl_compiler_revision: u32,
}

impl ByteCode for AcpiTableHeader {}

impl AmlBuilder for AcpiTableHeader {
    fn aml_bytes(&self) -> Vec<u8> {
        self.as_bytes().to_vec()
    }
}

/// ACPI table.
pub struct AcpiTable {
    entries: Vec<u8>,
}

impl AcpiTable {
    /// The construct function of ACPI table.
    ///
    /// # Arguments
    ///
    /// `signature` - The signature of this table.
    /// `revision` - The revision of this table.
    /// `oem_id` - OEM ID.
    /// `oem_table_id` - OEM table ID.
    /// `oem_revision` - OEM revision.
    pub fn new(
        signature: [u8; 4],
        revision: u8,
        oem_id: [u8; 6],
        oem_table_id: [u8; 8],
        oem_revision: u32,
    ) -> AcpiTable {
        AcpiTable {
            entries: AcpiTableHeader {
                signature,
                length: 0,
                revision,
                checksum: 0,
                oem_id,
                oem_table_id,
                oem_revision,
                asl_compiler_id: [0_u8; 4],
                asl_compiler_revision: 0_u32,
            }
            .aml_bytes(),
        }
    }

    /// Get the length of this table.
    pub fn table_len(&self) -> usize {
        self.entries.len()
    }

    /// Append the length of this table, do not support truncation.
    pub fn set_table_len(&mut self, new_size: usize) {
        if new_size < self.entries.len() {
            panic!("New size is smaller than old-size, truncation is not supported.");
        }
        self.entries
            .extend(vec![0_u8; new_size - self.entries.len()].as_slice());
        self.entries[4..=7].copy_from_slice((new_size as u32).as_bytes());
    }

    /// Set the value of one field in table.
    ///
    /// # Arguments
    ///
    /// `byte_index` - The location of field in this table.
    /// `new_value` - The new value that will be set in the field.
    pub fn set_field<T: ByteCode>(&mut self, byte_index: usize, new_value: T) {
        let value_len = std::mem::size_of::<T>();
        if byte_index >= self.entries.len() || byte_index + value_len > self.entries.len() {
            panic!("Set field in table failed: overflow occurs.");
        }
        self.entries[byte_index..(byte_index + value_len)].copy_from_slice(new_value.as_bytes());
    }

    /// Append byte stream to the end of table.
    pub fn append_child(&mut self, bytes: &[u8]) {
        self.entries.extend(bytes);

        let table_len = self.entries.len() as u32;
        self.entries[4..=7].copy_from_slice(table_len.as_bytes());
    }
}

impl AmlBuilder for AcpiTable {
    fn aml_bytes(&self) -> Vec<u8> {
        self.entries.clone()
    }
}

#[repr(C, packed)]
#[derive(Default, Copy, Clone)]
pub struct ProcessorHierarchyNode {
    r#type: u8,
    length: u8,
    reserved: u16,
    flags: u32,
    parent: u32,
    acpi_processor_id: u32,
    num_private_resources: u32,
}

impl ByteCode for ProcessorHierarchyNode {}

impl AmlBuilder for ProcessorHierarchyNode {
    fn aml_bytes(&self) -> Vec<u8> {
        self.as_bytes().to_vec()
    }
}

impl ProcessorHierarchyNode {
    pub fn new(
        flags: u32,
        parent: u32,
        acpi_processor_id: u32,
        num_private_resources: u32,
    ) -> Self {
        Self {
            r#type: 0,
            length: 20 + num_private_resources as u8 * 4,
            reserved: 0,
            flags,
            parent,
            acpi_processor_id,
            num_private_resources,
        }
    }
}

pub fn processor_append_priv_res(pptt: &mut AcpiTable, priv_resources: Vec<u32>) {
    let start = pptt.table_len();
    pptt.set_table_len(start + priv_resources.len() * 4);
    for (i, priv_res) in priv_resources.iter().enumerate() {
        pptt.set_field(start + i * 4, *priv_res);
    }
}

/// The Type of the hardcoded cache info
pub enum CacheType {
    L1D,
    L1I,
    L2,
    L3,
}

struct CacheNode {
    size: u32,
    sets: u32,
    associativity: u8,
    attributes: u8,
    line_size: u16,
}

const CACHE_NODES: [CacheNode; CacheType::L3 as usize + 1] = [
    // L1 data cache
    CacheNode {
        size: 65536,
        sets: 256,
        associativity: 4,
        attributes: 2,
        line_size: 64,
    },
    // L1 instruction cache
    CacheNode {
        size: 65536,
        sets: 256,
        associativity: 4,
        attributes: 4,
        line_size: 64,
    },
    // L2 unified cache
    CacheNode {
        size: 524228,
        sets: 1024,
        associativity: 8,
        attributes: 10,
        line_size: 64,
    },
    // L3 unified cache
    CacheNode {
        size: 33554432,
        sets: 2048,
        associativity: 15,
        attributes: 10,
        line_size: 128,
    },
];

#[repr(C, packed)]
#[derive(Default, Copy, Clone)]
pub struct CacheHierarchyNode {
    r#type: u8,
    length: u8,
    reserved: u16,
    flags: u32,
    next_level: u32,
    size: u32,
    number_sets: u32,
    associativity: u8,
    attributes: u8,
    line_size: u16,
}

impl ByteCode for CacheHierarchyNode {}

impl AmlBuilder for CacheHierarchyNode {
    fn aml_bytes(&self) -> Vec<u8> {
        self.as_bytes().to_vec()
    }
}

impl CacheHierarchyNode {
    pub fn new(next_level: u32, cache_type: CacheType) -> Self {
        let cache_node = &CACHE_NODES[cache_type as usize];
        Self {
            r#type: 1,
            length: 24,
            reserved: 0,
            flags: 127,
            next_level,
            size: cache_node.size,
            number_sets: cache_node.sets,
            associativity: cache_node.associativity,
            attributes: cache_node.attributes,
            line_size: cache_node.line_size,
        }
    }
}

/// ACPI RSDP structure.
#[repr(C, packed)]
#[derive(Default, Copy, Clone)]
pub struct AcpiRsdp {
    /// The signature of RSDP, which is "RSD PTR ".
    signature: [u8; 8],
    /// The checksum of the first 20 bytes of RSDP.
    checksum: u8,
    /// OEM ID.
    oem_id: [u8; 6],
    /// The revision of this structure, only revision 2 is supported.
    revision: u8,
    /// 32-bit address of RSDT table.
    rsdt_tlb_addr: u32,
    /// The length of this table.
    length: u32,
    /// 64-bit address of XSDT table.
    xsdt_tlb_addr: u64,
    /// Extended checksum of this RSDP structure.
    extended_checksum: u8,
    /// Reserved field.
    reserved: [u8; 3],
}

impl AcpiRsdp {
    pub fn new(oem_id: [u8; 6]) -> AcpiRsdp {
        AcpiRsdp {
            signature: *b"RSD PTR ",
            checksum: 0,
            oem_id,
            revision: 2,
            rsdt_tlb_addr: 0_u32,
            length: std::mem::size_of::<AcpiRsdp>() as u32,
            xsdt_tlb_addr: 0_u64,
            extended_checksum: 0,
            reserved: [0_u8; 3],
        }
    }
}

impl ByteCode for AcpiRsdp {}

impl AmlBuilder for AcpiRsdp {
    fn aml_bytes(&self) -> Vec<u8> {
        self.as_bytes().to_vec()
    }
}

/// ACPI SRAT processor affinity structure.
#[repr(C, packed)]
#[derive(Default, Copy, Clone)]
pub struct AcpiSratProcessorAffinity {
    /// Type ID.
    pub type_id: u8,
    /// The length of this structure.
    pub length: u8,
    /// Bit `\[`7:0`\]` of the proximity domain to which the processor belongs.
    pub proximity_lo: u8,
    /// The processor local APIC ID.
    pub local_apic_id: u8,
    /// The processor affinity flags.
    pub flags: u32,
    /// The processor local SAPIC EID.
    pub local_sapic_eid: u8,
    /// Bit `\[`31:8`\]` of the proximity domain to which the processor belongs.
    pub proximity_hi: [u8; 3],
    /// The clock domain to which the processor belongs.
    pub clock_domain: u32,
}

impl ByteCode for AcpiSratProcessorAffinity {}

impl AmlBuilder for AcpiSratProcessorAffinity {
    fn aml_bytes(&self) -> Vec<u8> {
        Vec::from(self.as_bytes())
    }
}

/// ACPI SRAT GICC affinity structure.
#[repr(C, packed)]
#[derive(Default, Copy, Clone)]
pub struct AcpiSratGiccAffinity {
    /// Type ID.
    pub type_id: u8,
    /// The length of this structure.
    pub length: u8,
    /// Represents the proximity domain to which the "range of memory" belongs.
    pub proximity_domain: u32,
    /// The ACPI processor UID of the associated GICC
    pub process_uid: u32,
    /// The GICC affinity flags.
    pub flags: u32,
    /// The clock domain to which the processor belongs.
    pub clock_domain: u32,
}

impl ByteCode for AcpiSratGiccAffinity {}

impl AmlBuilder for AcpiSratGiccAffinity {
    fn aml_bytes(&self) -> Vec<u8> {
        Vec::from(self.as_bytes())
    }
}

/// ACPI SRAT memory affinity structure.
#[repr(C, packed)]
#[derive(Default, Copy, Clone)]
pub struct AcpiSratMemoryAffinity {
    /// Type ID.
    pub type_id: u8,
    /// The length of this structure.
    pub length: u8,
    /// Represents the proximity domain to which the "range of memory" belongs.
    pub proximity_domain: u32,
    /// Reserved field.
    pub reserved1: u16,
    /// The base address of the memory range.
    pub base_addr: u64,
    /// The length of the memory range.
    pub range_length: u64,
    /// Reserved field.
    pub reserved2: u32,
    /// Indicates whether memory is enabled and can be hot plugged.
    pub flags: u32,
    /// Reserved field.
    pub reserved3: u64,
}

impl ByteCode for AcpiSratMemoryAffinity {}

impl AmlBuilder for AcpiSratMemoryAffinity {
    fn aml_bytes(&self) -> Vec<u8> {
        Vec::from(self.as_bytes())
    }
}

/// This module describes ACPI MADT's sub-tables on x86_64 platform.
#[cfg(target_arch = "x86_64")]
pub mod madt_subtable {
    use super::*;
    pub const IOAPIC_BASE_ADDR: u32 = 0xfec0_0000;
    pub const LAPIC_BASE_ADDR: u32 = 0xfee0_0000;

    /// MADT processor Local APIC structure.
    #[repr(C, packed)]
    #[derive(Default, Copy, Clone)]
    pub struct AcpiLocalApic {
        /// Type ID.
        pub type_id: u8,
        /// The length of this structure.
        pub length: u8,
        /// ACPI processor UID.
        pub processor_uid: u8,
        /// The processor's Local APIC ID.
        pub apic_id: u8,
        /// Local APIC flags.
        pub flags: u32,
    }

    impl ByteCode for AcpiLocalApic {}

    impl AmlBuilder for AcpiLocalApic {
        fn aml_bytes(&self) -> Vec<u8> {
            Vec::from(self.as_bytes())
        }
    }

    /// IO APIC structure.
    #[repr(C, packed)]
    #[derive(Default, Copy, Clone)]
    pub struct AcpiIoApic {
        /// Type ID.
        pub type_id: u8,
        /// The length of this structure.
        pub length: u8,
        /// This IO APIC's ID.
        pub io_apic_id: u8,
        /// Reserved field.
        pub reserved: u8,
        /// The 32-bit address of this IO APIC.
        pub io_apic_addr: u32,
        /// The GSI number where this I/O APIC’s interrupt inputs start.
        pub gsi_base: u32,
    }

    impl ByteCode for AcpiIoApic {}

    impl AmlBuilder for AcpiIoApic {
        fn aml_bytes(&self) -> Vec<u8> {
            Vec::from(self.as_bytes())
        }
    }
}

/// This module describes ACPI MADT's sub-tables on aarch64 platform.
#[cfg(target_arch = "aarch64")]
pub mod madt_subtable {
    use super::*;

    pub const ARCH_GIC_MAINT_IRQ: u32 = 9;

    /// GIC CPU Interface structure.
    #[repr(C, packed)]
    #[derive(Default, Copy, Clone)]
    pub struct AcpiGicCpu {
        /// Type ID.
        pub type_id: u8,
        /// The length of this structure.
        pub length: u8,
        /// Reserved field.
        reserved_1: u16,
        /// CPU interface number.
        pub cpu_interface_num: u32,
        /// ACPI processor UID.
        pub processor_uid: u32,
        /// Flags.
        pub flags: u32,
        /// The version of Arm processor parking protocol.
        pub parking_version: u32,
        /// The GSIV used for performance monitoring interrupts.
        pub perf_interrupt: u32,
        /// The 64-bit address of the processor’s parking protocol mailbox.
        pub parked_addr: u64,
        /// CPU can access this CPU interface via this 64-bit address.
        pub base_addr: u64,
        /// Address of the GIC virtual CPU interface registers.
        pub gicv_addr: u64,
        /// Address of the GIC virtual interface control block registers.
        pub gich_addr: u64,
        /// GSIV for Virtual GIC maintenance interrupt.
        pub vgic_interrupt: u32,
        /// If GIC's version is above 3, this field is 64-bit address of redistributor.
        pub gicr_addr: u64,
        /// MPIDR.
        pub mpidr: u64,
        /// Reserved field.
        reserved_2: u32,
    }

    impl ByteCode for AcpiGicCpu {}

    impl AmlBuilder for AcpiGicCpu {
        fn aml_bytes(&self) -> Vec<u8> {
            Vec::from(self.as_bytes())
        }
    }

    /// GIC distributor structure.
    #[repr(C, packed)]
    #[derive(Default, Copy, Clone)]
    pub struct AcpiGicDistributor {
        /// Type ID.
        pub type_id: u8,
        /// The length of this structure.
        pub length: u8,
        /// Reserved field.
        reserved_1: u16,
        /// This distributor's hardware ID.
        pub gic_id: u32,
        /// The 64-bit address of this distributor.
        pub base_addr: u64,
        /// System vector base, must be zero.
        pub sys_vector_base: u32,
        /// GIC version.
        pub gic_version: u8,
        /// Reserved field.
        reserved_2: [u8; 3],
    }

    impl ByteCode for AcpiGicDistributor {}

    impl AmlBuilder for AcpiGicDistributor {
        fn aml_bytes(&self) -> Vec<u8> {
            Vec::from(self.as_bytes())
        }
    }

    /// GIC Redistributor structure.
    #[repr(C, packed)]
    #[derive(Default, Copy, Clone)]
    pub struct AcpiGicRedistributor {
        /// Type ID.
        pub type_id: u8,
        /// The length of this structure.
        pub length: u8,
        /// Reserved field.
        reserved_1: u16,
        /// The 64-bit address of this redistributor.
        pub base_addr: u64,
        /// Length of the GIC redistributor discovery page range.
        pub range_length: u32,
    }

    impl ByteCode for AcpiGicRedistributor {}

    impl AmlBuilder for AcpiGicRedistributor {
        fn aml_bytes(&self) -> Vec<u8> {
            Vec::from(self.as_bytes())
        }
    }

    /// GIC Interrupt Translation Service (ITS) Structure.
    #[repr(C, packed)]
    #[derive(Default, Copy, Clone)]
    pub struct AcpiGicIts {
        /// Type ID.
        pub type_id: u8,
        /// The length of this structure.
        pub length: u8,
        /// Reserved field.
        reserved_1: u16,
        /// ITS ID, must be unique.
        pub its_id: u32,
        /// The 64-bit address of this ITS.
        pub base_addr: u64,
        /// Reserved field.
        reserved_2: u32,
    }

    impl ByteCode for AcpiGicIts {}

    impl AmlBuilder for AcpiGicIts {
        fn aml_bytes(&self) -> Vec<u8> {
            Vec::from(self.as_bytes())
        }
    }
}