# Copyright (c) 2013, Intel Corporation
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#     * Redistributions of source code must retain the above copyright notice,
#       this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright notice,
#       this list of conditions and the following disclaimer in the documentation
#       and/or other materials provided with the distribution.
#     * Neither the name of Intel Corporation nor the names of its contributors
#       may be used to endorse or promote products derived from this software
#       without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

"""CPUID register decoding."""

import bits
from bits.platformbase import CPUID, cpuidfield
import struct

EAX = 0
EBX = 1
ECX = 2
EDX = 3

class LEAF_0(CPUID):
    """Basic CPUID information including vendor and max supported basic leaf."""

    leaf = 0x0
    max_leaf = cpuidfield(EAX, 31, 0, doc="Highest value the CPUID recognizes for returning basic processor information")

    @property
    def vendor(self):
        """Vendor identification string"""
        return struct.pack('III', self.regs.ebx, self.regs.edx, self.regs.ecx)

class LEAF_1(CPUID):
    """Basic CPUID Information

    Contains version type, family, model, and stepping ID; brand index; CLFLUSH
    line size; maximum number of addressable IDs for logical processors in the
    physical package; initial APIC ID; and feature information"""

    leaf = 0x1

    stepping = cpuidfield(EAX, 3, 0, doc="Stepping ID")
    model = cpuidfield(EAX, 7, 4, doc="Model")
    family = cpuidfield(EAX, 11, 8, doc="Family ID")
    processor_type = cpuidfield(EAX, 13, 12, doc="Processor Type")
    ext_model = cpuidfield(EAX, 19, 16, doc="Extended Model ID")
    ext_family = cpuidfield(EAX, 27, 20, doc="Extended Family ID")

    brand_index = cpuidfield(EBX, 7, 0, doc="Brand index")
    CLFLUSH_line_size = cpuidfield(EBX, 15, 8, doc="CLFLUSH instruction cache line size (in 8-byte words)")
    max_logical_processor_ids = cpuidfield(EBX, 23, 16, doc="The maximum number of addressable IDs for logical processors in the physical package.")
    initial_apic_id = cpuidfield(EBX, 31, 24, doc="Initial APIC ID")

    sse3 = cpuidfield(ECX, 0, 0)
    pclmulqdq = cpuidfield(ECX, 1, 1)
    dtes64 = cpuidfield(ECX, 2, 2)
    monitor = cpuidfield(ECX, 3, 3)
    ds_cpl = cpuidfield(ECX, 4, 4)
    vmx = cpuidfield(ECX, 5, 5)
    smx = cpuidfield(ECX, 6, 6)
    est = cpuidfield(ECX, 7, 7)
    tm2 = cpuidfield(ECX, 8, 8)
    ssse3 = cpuidfield(ECX, 9, 9)
    cnxt_id = cpuidfield(ECX, 10, 10)
    fma = cpuidfield(ECX, 12, 12)
    cmpxchg16b = cpuidfield(ECX, 13, 13)
    xtpr = cpuidfield(ECX, 14, 14)
    pdcm = cpuidfield(ECX, 15, 15)
    pcid = cpuidfield(ECX, 17, 17)
    dca = cpuidfield(ECX, 18, 18)
    sse4_1 = cpuidfield(ECX, 19, 19)
    sse4_2 = cpuidfield(ECX, 20, 20)
    x2apic = cpuidfield(ECX, 21, 21)
    movbe = cpuidfield(ECX, 22, 22)
    popcnt = cpuidfield(ECX, 23, 23)
    tsc_deadline = cpuidfield(ECX, 24, 24)
    aes = cpuidfield(ECX, 25, 25)
    xsave = cpuidfield(ECX, 26, 26)
    osxsave = cpuidfield(ECX, 27, 27)
    avx = cpuidfield(ECX, 28, 28)
    f16c = cpuidfield(ECX, 29, 29)
    rdrand = cpuidfield(ECX, 30, 30)

    fpu = cpuidfield(EDX, 0, 0)
    vme = cpuidfield(EDX, 1, 1)
    de = cpuidfield(EDX, 2, 2)
    pse = cpuidfield(EDX, 3, 3)
    tsc = cpuidfield(EDX, 4, 4)
    msr = cpuidfield(EDX, 5, 5)
    pae = cpuidfield(EDX, 6, 6)
    mce = cpuidfield(EDX, 7, 7)
    cx8 = cpuidfield(EDX, 8, 8)
    apic = cpuidfield(EDX, 9, 9)
    sep = cpuidfield(EDX, 11, 11)
    mtrr = cpuidfield(EDX, 12, 12)
    pge = cpuidfield(EDX, 13, 13)
    mca = cpuidfield(EDX, 14, 14)
    cmov = cpuidfield(EDX, 15, 15)
    pat = cpuidfield(EDX, 16, 16)
    pse36 = cpuidfield(EDX, 17, 17)
    psn = cpuidfield(EDX, 18, 18)
    clfsh = cpuidfield(EDX, 19, 19)
    ds = cpuidfield(EDX, 21, 21)
    acpi = cpuidfield(EDX, 22, 22)
    mmx = cpuidfield(EDX, 23, 23)
    fxsr = cpuidfield(EDX, 24, 24)
    sse = cpuidfield(EDX, 25, 25)
    sse2 = cpuidfield(EDX, 26, 26)
    ss = cpuidfield(EDX, 27, 27)
    htt = cpuidfield(EDX, 28, 28)
    tm = cpuidfield(EDX, 29, 29)
    pbe = cpuidfield(EDX, 31, 31)

    @property
    def display_family(self):
        if self.family == 0xf:
            return self.ext_family + self.family
        return self.family

    @property
    def display_model(self):
        if self.family == 0xf or self.family == 0x6:
            return self.ext_model << 4 + self.model
        return self.model

class LEAF_2(CPUID):
    """TLB, Cache, and Prefetch Information"""

    leaf = 0x2
    times_to_run = cpuidfield(EAX, 7, 0, doc="Number of times CPUID must be executed with EAX = 2 to retrieve a complete description of the processor's TLB, Cache, and Prefetch hardware")

class LEAF_4(CPUID):
    """Deterministic cache parameters

    Returns encoded data that describes a set of deterministic cache parameters
    for the cache level associated in ECX"""

    leaf = 0x4
    cache_type = cpuidfield(EAX, 4, 0, doc="Cache Type Field")
    cache_level = cpuidfield(EAX, 7, 5, doc="Cache Level")
    self_initializing = cpuidfield(EAX, 8, 8, doc="Self Initializing Cache Level")
    fully_associative = cpuidfield(EAX, 9, 9, doc="Fully Associative Cache")
    max_logical_processors_sharing_cache_z = cpuidfield(EAX, 25, 14, doc="Max number of addressable IDs for logical processors sharing this cache (zero based)")
    max_cores_sharing_cache_z = cpuidfield(EAX, 31, 26, doc="Max number of addressable IDs for processor cores in the physical package (zero based)")

    line_size_z = cpuidfield(EBX, 11, 0, doc="System Coherency Line Size (zero-based)")
    partitions_z = cpuidfield(EBX, 21, 12, doc="Physical Line Partitions (zero-based)")
    ways_z = cpuidfield(EBX, 31, 22, doc="Ways of associativity (zero-based)")

    sets_z = cpuidfield(ECX, 31, 0, doc="Sets (zero-based)")

    write_back_invalidate = cpuidfield(EDX, 0, 0, doc="Write-back Invalidate/Invalidate")
    cache_inclusiveness = cpuidfield(EDX, 1, 1, doc="Cache Inclusiveness")
    complex_cache_indexing = cpuidfield(EDX, 2, 2, doc="Complex Cache indexing")

    @property
    def max_logical_processors_sharing_cache(self):
        """Maximum number of addressable IDs for logical processors sharing this cache"""
        return self.max_logical_processors_sharing_cache_z + 1

    @property
    def max_cores_sharing_cache(self):
        """Maximum number of addressable IDs for processor cores in the physical pacakge"""
        return self.max_cores_sharing_cache_z + 1

    @property
    def partitions(self):
        """Number of physical line partitions"""
        return self.partitions_z + 1

    @property
    def line_size(self):
        """System Coherency line size"""
        return self.line_size_z + 1

    @property
    def ways(self):
        """Ways of associativity"""
        return self.ways_z + 1

    @property
    def sets(self):
        """Number of sets"""
        return self.sets_z + 1

    @property
    def cache_size(self):
        """Cache size in bytes"""
        return self.ways * self.partitions * self.line_size * self.sets

class LEAF_5(CPUID):
    """MONITOR/MWAIT Leaf

    Returns information about features available to MONITOR/MWAIT instructions"""

    leaf = 0x5

    smallest_monitor_line_size = cpuidfield(EAX, 15, 0, doc="Smallest monitor-line size in bytes")

    largest_monitor_line_size = cpuidfield(EBX, 15, 0, doc="Largest monitor-line size in bytes")

    monitor_mwait_supported = cpuidfield(ECX, 0, 0, doc="Enumeration of MONITOR/MWAIT extensions supported")
    interrupt_break_event_supported = cpuidfield(ECX, 1, 1, doc="Supports treating interrupts as break-events for MWAIT, even when interrupts disabled")

    c0 = cpuidfield(EDX, 3, 0, doc="Number of C0 sub C-states supported using MWAIT")
    c1 = cpuidfield(EDX, 7, 4, doc="Number of C1 sub C-states supported using MWAIT")
    c2 = cpuidfield(EDX, 11, 8, doc="Number of C2 sub C-states supported using MWAIT")
    c3 = cpuidfield(EDX, 15, 12, doc="Number of C3 sub C-states supported using MWAIT")
    c4 = cpuidfield(EDX, 19, 16, doc="Number of C4 sub C-states supported using MWAIT")

class LEAF_6(CPUID):
    """Thermal and Power Management leaf

    Returns information about the maximum input values for sub-leaves that contain extended feature flags."""

    leaf = 0x6

    digital_temperature_sensor_supported = cpuidfield(EAX, 0, 0, doc = "Digital temperature sensor is supported if set")
    turbo_boost_available = cpuidfield(EAX, 1, 1, doc = "Intel Turbo Boost technology available")
    arat_supported = cpuidfield(EAX, 2, 2, doc = "APIC-Timer-always-running feature is supported if set")
    pln_supported = cpuidfield(EAX, 4, 4, doc = "Power limit notification controls are supported if set")
    ecmd_supported = cpuidfield(EAX, 5, 5, doc = "Clock modulation duty cycle extension is supported if set")
    package_thermal_management_supported = cpuidfield(EAX, 6, 6, doc = "Package thermal management is supported if set")

    num_interrupt_thresholds = cpuidfield(EBX, 3, 0, doc="Number of interrupt thresholds in digital thermal sensor")

    hardware_coordination_feedback_capability = cpuidfield(ECX, 0, 0, doc="Hardware coordination feedback capability")
    performance_energy_bias = cpuidfield(ECX, 3, 3, doc="Performance-energy bias preference support")

class LEAF_7(CPUID):
    """Structured Extended Feature Flags Enumeration Leaf

    Returns information about the maximum input value for sub-leaves that contain
    extended feature flags"""

    leaf = 0x7

    max_input_values = cpuidfield(EAX, 31, 0, doc="Reports the maximum input value for supported leaf 7 sub-leaves")

    fsgsbase = cpuidfield(EBX, 0, 0, doc="Supports RDFSBASE/RDGSBASE/WRFSBASE/WRGSBASE if 1")
    ia32_tsc_adjust_msr = cpuidfield(EBX, 1, 1, doc="IA32_TSC_ADJUST MSR is supported if 1")
    bmi1 = cpuidfield(EBX, 3, 3)
    hle = cpuidfield(EBX, 4, 4)
    avx2 = cpuidfield(EBX, 5, 5)
    smep = cpuidfield(EBX, 7, 7, doc="Supports Supervisor Mode Execution Protection if 1")
    bmi2 = cpuidfield(EBX, 8, 8)
    erms = cpuidfield(EBX, 9, 9, doc="Supports Enhanced REP MOVSB/STOSB if 1")
    invpcid = cpuidfield(EBX, 10, 10, doc="Supports INVPCID instruction for system software that manages process-context identifiers if 1")
    rtm = cpuidfield(EBX, 11, 11)
    qm = cpuidfield(EBX, 12, 12, doc="Supports Quality of Service Monitoring capability if 1")
    deprecate_fpu = cpuidfield(EBX, 13, 13, doc="Deprecates FPS CS and FPU DS values if 1")
    rdseed = cpuidfield(EBX, 18, 18)
    adx = cpuidfield(EBX, 19, 19)
    smap = cpuidfield(EBX, 20, 20)

class LEAF_9(CPUID):
    """Direct Cache Access Information leaf

    Returns information about Direct Cache Access capabilities"""

    leaf = 0x9

    platform_dca_cap = cpuidfield(EAX, 31, 0, doc="Value of bits of IA32_PLATFORM_DCA_CAP MSR (address 1F8H)")

class LEAF_A(CPUID):
    """Architectural Performance Monitoring Leaf

    Returns information about support for architectural performance monitoring capabilities"""

    leaf = 0xA

    architectural_performance_monitor_version_id = cpuidfield(EAX, 7, 0, doc="Version ID of architectural performance monitoring")
    gp_performance_monitor_counters = cpuidfield(EAX, 15, 8, doc="Number of general-purpose performance monitoring counter per logical processor")
    gp_performance_counter_width = cpuidfield(EAX, 23, 16, doc="Bit width of general-purpose, performance monitoring counter")
    ebx_bit_vector_length = cpuidfield(EAX, 31, 24, doc="Length of EBX bit vector to enumerate architectural performance monitoring events")

    core_cycle_event = cpuidfield(EBX, 0, 0, doc="Core cycle event not available if 1")
    instruction_retired_event = cpuidfield(EBX, 1, 1, doc="Instruction retired event not available if 1")
    reference_cycles_event = cpuidfield(EBX, 2, 2, doc="Reference cycles event not available if 1")
    llc_ref_event = cpuidfield(EBX, 3, 3, doc="Last-level cache reference event not available if 1")
    llc_misses_event = cpuidfield(EBX, 4, 4, doc= "Last-level cache misses event not available if 1")
    branch_instruction_retired_event = cpuidfield(EBX, 5, 5, doc="Branch instruction retired event not available if 1")
    branch_mispredict_retired_event = cpuidfield(EBX, 6, 6, doc="Branch mispredict retired event not available if 1")

    ff_performance_counters = cpuidfield(EDX, 4, 0, doc="Number of fixed-function performance counters")
    ff_performance_counter_width = cpuidfield(EDX, 12, 5, doc="Bit width of fixed-function performance counters")

class LEAF_B(CPUID):
    """Extended Topology Enumeration Leaf

    Returns information about extended topology enumeration data"""

    leaf = 0xB

    num_bit_shift = cpuidfield(EAX, 4, 0, doc="Number of bits to shift right on x2APID ID to get a unique topology ID of the next level type")

    logical_proccessors_at_level = cpuidfield(EBX, 15, 0, doc="Number of logical processors at this level type.")

    level_number = cpuidfield(ECX, 7, 0, doc="Level number")
    level_type = cpuidfield(ECX, 15, 8, doc="Level type")

    x2apic_id = cpuidfield(EDX, 31, 0, doc="x2APIC ID of the current logical processor")

class LEAF_D(CPUID):
    """Processor Extended State Enumeration Main Leaf and Sub-Leaves.

    Returns information about the bit-vector representation of all processor
    state extensions that are supported in the processor, and storage size
    requirements of the XSAVE/XRSTOR area. Output depends on initial value of ECX."""

    leaf = 0xD

    valid_bits_xcr0_lower = cpuidfield(EAX, 31, 0, doc="Reports the valid bit fields of the lower 32 bits of XCR0. If a bit is 0, the corresponding bit field in XCR0 is reserved")

    legacy_x87 = cpuidfield(EAX, 0, 0, doc="legacy x87")
    sse_128_bit = cpuidfield(EAX, 1, 1, doc="128-bit SSE")
    avx_256_bit = cpuidfield(EAX, 2, 2, doc="256-bit AVX")

    max_size_enabled_xcr0 = cpuidfield(EBX, 31, 0, doc="Maximum size (bytes, from the beginning of the XSAVE/XRSTOR save area) required by enabled features in XCR0. May be different than ECX if some features at the end of the XSAVE save area are not enabled.")

    max_size_supported_xcr0 = cpuidfield(ECX, 31, 0, doc="Maximum size (bytes, from the beginning of the XSAVE/XRSTOR save area) of the XSAVE/XRSTOR save area required by all supported features in the processor, i.e all the valid bit fields in XCR0.")

    valid_bits_xcr0_upper = cpuidfield(EDX, 31, 0, doc="The valid bit fields of the upper 32 bits of XCR0. If a bit is 0, the corresponding bit field in XCR0 is reserved.")

    def __getitem__(self, subleaf):
        if subleaf == 0:
            return self.read(self.apicid, subleaf)
        elif subleaf == 1:
            return LEAF_D_1.read(self.apicid, subleaf)
        return LEAF_D_n.read(self.apicid, subleaf)

class LEAF_D_1(CPUID):
    """Processor Extended State Enumeration Main Leaf and Sub-Leaves.

    Returns information about the bit-vector representation of all processor
    state extensions that are supported in the processor, and storage size
    requirements of the XSAVE/XRSTOR area. Output depends on initial value of ECX."""

    leaf = 0xD

    xsaveopt = cpuidfield(EAX, 0, 0, doc="XSAVEOPT is available")

class LEAF_D_n(CPUID):
    """Processor Extended State Enumeration Main Leaf and Sub-Leaves.

    Returns information about the bit-vector representation of all processor
    state extensions that are supported in the processor, and storage size
    requirements of the XSAVE/XRSTOR area. Output depends on initial value of ECX."""

    leaf = 0xD

    size = cpuidfield(EAX, 31, 0, doc="The size in bytes (from the offset specified in EBX) of the save area for an extended state feature associated with a valid sub-leaf index")

    offset = cpuidfield(EBX, 31, 0, doc="The offset in bytes of this extended state component's save area from the beginning of the XSAVE/XRSTOR area.")

class LEAF_F(CPUID):
    """Quality of Service Resource Type Enumeration Sub-Leaf and L3 Cache QoS Capability Enumeration Sub-leaf. Depends on value of ECX

    Returns Quality of Service (QoS) Enumeration Information."""

    leaf = 0xF

    def __getitem__(self, subleaf):
        if subleaf == 0:
            return self.read(self.apicid, subleaf)
        elif subleaf == 1:
            return LEAF_F_1.read(self.apicid, subleaf)
        return LEAF_F_n.read(self.apicid, subleaf)

    max_range_rmid_z = cpuidfield(EBX, 31, 0, doc="Maximum range (zero-based) of RMID within this physical processor of all types.")

    l3_cache_qos = cpuidfield(EDX, 1, 1, doc="Supports L3 Cache QoS if 1")

    @property
    def max_range_rmid(self):
        """Maximum range of RMID within this physical processor of all types."""
        return self.max_range_rmid_z + 1

class LEAF_F_1(CPUID):
    """Quality of Service Resource Type Enumeration Sub-Leaf and L3 Cache QoS Capability Enumeration Sub-leaf. Depends on value of ECX

    Returns L3 Cache QoS Capability Enumeration Information."""

    leaf = 0xF

    qm_ctr_conversion_factor = cpuidfield(EBX, 31, 0, doc="Conversion factor from reported IA32_QM_CTR value to occupancy metric (bytes).")

    l3_occupancy_monitoring = cpuidfield(EDX, 0, 0, doc="Supports L3 occupancy monitoring if 1")

    max_range_rmid_z = cpuidfield(ECX, 31, 0, doc="Maximum range (zero-based) of RMID of this resource type")

    @property
    def max_range_rmid(self):
        """Maximum range of RMID of this resource type"""
        return self.max_range_rmid_z + 1

class LEAF_F_n(CPUID):
    """Quality of Service Resource Type Enumeration Sub-Leaf and L3 Cache QoS Capability Enumeration Sub-leaf. Depends on value of ECX

    Returns Quality of Service (QoS) Enumeration Information."""

    leaf = 0xF

class LEAF_80000000(CPUID):
    """Extended Function CPUID Information"""

    leaf = 0x80000000

    max_extended_leaf = cpuidfield(EAX, 31, 0, doc="Highest extended function input value understood by CPUID")

class LEAF_80000001(CPUID):
    """Extended Function CPUID Information"""

    leaf = 0x80000001

    ext_signature_feature_bits = cpuidfield(EAX, 31, 0, doc="Extended processor signature and feature bits")

    lahf_sahf_64 = cpuidfield(ECX, 0, 0, doc="LAHF/SAHF available in 64-bit mode")
    lzcnt = cpuidfield(ECX, 5, 5)

    syscall_sysret_64 = cpuidfield(EDX, 11, 11, doc="SYSCALL/SYSRET available in 64-bit mode")
    execute_disable = cpuidfield(EDX, 20, 20, doc="Execute Disable Bit available")
    gbyte_pages = cpuidfield(EDX, 26, 26, doc="GByte pages are available if 1")
    rdtscp_ia32_tsc_aux = cpuidfield(EDX, 27, 27, doc="RDTSCP and IA32_TSC_AUX are available if 1")
    intel_64 = cpuidfield(EDX, 29, 29, doc="Intel(R) 64 Architecture available if 1")

class LEAF_80000002(CPUID):
    """Extended Function CPUID Information

    Processor Brand String"""

    leaf = 0x80000002

    @property
    def brandstring(self):
        """Processor Brand String"""
        return struct.pack('IIII', self.regs.eax, self.regs.ebx, self.regs.ecx, self.regs.edx).rstrip("\x00")

class LEAF_80000003(CPUID):
    """Extended Function CPUID Information

    Processor Brand String Continued"""

    leaf = 0x80000003

    @property
    def brandstring(self):
        """Processor Brand String"""
        return struct.pack('IIII', self.regs.eax, self.regs.ebx, self.regs.ecx, self.regs.edx).rstrip("\x00")

class LEAF_80000004(CPUID):
    """Extended Function CPUID Information

    Processor Brand String Continued"""

    leaf = 0x80000004

    @property
    def brandstring(self):
        """Processor Brand String"""
        return struct.pack('IIII', self.regs.eax, self.regs.ebx, self.regs.ecx, self.regs.edx).rstrip("\x00")

class LEAF_80000006(CPUID):
    """Extended Function CPUID Information"""

    leaf = 0x80000006

    cache_line_size = cpuidfield(ECX, 7, 0, doc="Cache Line size in bytes")

    l2_associativity = cpuidfield(ECX, 15, 12, doc="L2 Associativity field")
    cache_size_k = cpuidfield(ECX, 31, 16, doc="Cache size in 1K units")

class LEAF_80000007(CPUID):
    """Misc Feature Flags"""

    leaf = 0x80000007

    invariant_tsc = cpuidfield(EDX, 8, 8, doc="Invariant TSC available if 1")

class LEAF_80000008(CPUID):
    """Returns linear/physical address size"""

    leaf = 0x80000008
    physical_address_bits = cpuidfield(EAX, 7, 0, doc="# Physical Address bits")
    linear_address_bits = cpuidfield(EAX, 15, 8, doc="# Linear Address bits")

def generate_cpuids(apicid):
    max_leaf = LEAF_0.read(apicid).max_leaf
    max_extended_leaf = LEAF_80000000.read(apicid).max_extended_leaf

    leafs = [
        LEAF_0,
        LEAF_1,
        LEAF_2,
        LEAF_4,
        LEAF_5,
        LEAF_6,
        LEAF_7,
        LEAF_9,
        LEAF_A,
        LEAF_B,
        LEAF_D,
        LEAF_F,
        LEAF_80000000,
        LEAF_80000001,
        LEAF_80000002,
        LEAF_80000003,
        LEAF_80000004,
        LEAF_80000006,
        LEAF_80000007,
        LEAF_80000008,
        ]
    return [l for l in leafs if l.leaf <= max_leaf or (l.leaf >= 0x8000000 and l.leaf <= max_extended_leaf)]
