arm_dynarmic_cp15: Update CP15
This commit is contained in:
		@@ -90,8 +90,6 @@ public:
 | 
			
		||||
 | 
			
		||||
    ARM_Dynarmic_32& parent;
 | 
			
		||||
    std::size_t num_interpreted_instructions{};
 | 
			
		||||
    u64 tpidrro_el0{};
 | 
			
		||||
    u64 tpidr_el0{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable& page_table,
 | 
			
		||||
@@ -100,7 +98,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
 | 
			
		||||
    config.callbacks = cb.get();
 | 
			
		||||
    // TODO(bunnei): Implement page table for 32-bit
 | 
			
		||||
    // config.page_table = &page_table.pointers;
 | 
			
		||||
    config.coprocessors[15] = std::make_shared<DynarmicCP15>((u32*)&CP15_regs[0]);
 | 
			
		||||
    config.coprocessors[15] = cp15;
 | 
			
		||||
    config.define_unpredictable_behaviour = true;
 | 
			
		||||
    return std::make_unique<Dynarmic::A32::Jit>(config);
 | 
			
		||||
}
 | 
			
		||||
@@ -118,8 +116,8 @@ void ARM_Dynarmic_32::Step() {
 | 
			
		||||
 | 
			
		||||
ARM_Dynarmic_32::ARM_Dynarmic_32(System& system, ExclusiveMonitor& exclusive_monitor,
 | 
			
		||||
                                 std::size_t core_index)
 | 
			
		||||
    : ARM_Interface{system},
 | 
			
		||||
      cb(std::make_unique<DynarmicCallbacks32>(*this)), core_index{core_index},
 | 
			
		||||
    : ARM_Interface{system}, cb(std::make_unique<DynarmicCallbacks32>(*this)),
 | 
			
		||||
      cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index},
 | 
			
		||||
      exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
 | 
			
		||||
 | 
			
		||||
ARM_Dynarmic_32::~ARM_Dynarmic_32() = default;
 | 
			
		||||
@@ -155,19 +153,19 @@ void ARM_Dynarmic_32::SetPSTATE(u32 cpsr) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
u64 ARM_Dynarmic_32::GetTlsAddress() const {
 | 
			
		||||
    return CP15_regs[static_cast<std::size_t>(CP15Register::CP15_THREAD_URO)];
 | 
			
		||||
    return cp15->uro;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ARM_Dynarmic_32::SetTlsAddress(VAddr address) {
 | 
			
		||||
    CP15_regs[static_cast<std::size_t>(CP15Register::CP15_THREAD_URO)] = static_cast<u32>(address);
 | 
			
		||||
    cp15->uro = static_cast<u32>(address);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
u64 ARM_Dynarmic_32::GetTPIDR_EL0() const {
 | 
			
		||||
    return cb->tpidr_el0;
 | 
			
		||||
    return cp15->uprw;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ARM_Dynarmic_32::SetTPIDR_EL0(u64 value) {
 | 
			
		||||
    cb->tpidr_el0 = value;
 | 
			
		||||
    cp15->uprw = value;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@ class Memory;
 | 
			
		||||
namespace Core {
 | 
			
		||||
 | 
			
		||||
class DynarmicCallbacks32;
 | 
			
		||||
class DynarmicCP15;
 | 
			
		||||
class DynarmicExclusiveMonitor;
 | 
			
		||||
class System;
 | 
			
		||||
 | 
			
		||||
@@ -66,12 +67,14 @@ private:
 | 
			
		||||
        std::unordered_map<JitCacheKey, std::shared_ptr<Dynarmic::A32::Jit>, Common::PairHash>;
 | 
			
		||||
 | 
			
		||||
    friend class DynarmicCallbacks32;
 | 
			
		||||
    friend class DynarmicCP15;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<DynarmicCallbacks32> cb;
 | 
			
		||||
    JitCacheType jit_cache;
 | 
			
		||||
    std::shared_ptr<Dynarmic::A32::Jit> jit;
 | 
			
		||||
    std::shared_ptr<DynarmicCP15> cp15;
 | 
			
		||||
    std::size_t core_index;
 | 
			
		||||
    DynarmicExclusiveMonitor& exclusive_monitor;
 | 
			
		||||
    std::array<u32, 84> CP15_regs{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace Core
 | 
			
		||||
 
 | 
			
		||||
@@ -2,79 +2,119 @@
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "core/arm/dynarmic/arm_dynarmic_32.h"
 | 
			
		||||
#include "core/arm/dynarmic/arm_dynarmic_cp15.h"
 | 
			
		||||
 | 
			
		||||
using Callback = Dynarmic::A32::Coprocessor::Callback;
 | 
			
		||||
using CallbackOrAccessOneWord = Dynarmic::A32::Coprocessor::CallbackOrAccessOneWord;
 | 
			
		||||
using CallbackOrAccessTwoWords = Dynarmic::A32::Coprocessor::CallbackOrAccessTwoWords;
 | 
			
		||||
 | 
			
		||||
template <>
 | 
			
		||||
struct fmt::formatter<Dynarmic::A32::CoprocReg> {
 | 
			
		||||
    constexpr auto parse(format_parse_context& ctx) {
 | 
			
		||||
        return ctx.begin();
 | 
			
		||||
    }
 | 
			
		||||
    template <typename FormatContext>
 | 
			
		||||
    auto format(const Dynarmic::A32::CoprocReg& reg, FormatContext& ctx) {
 | 
			
		||||
        return format_to(ctx.out(), "cp{}", static_cast<size_t>(reg));
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
namespace Core {
 | 
			
		||||
 | 
			
		||||
static u32 dummy_value;
 | 
			
		||||
 | 
			
		||||
std::optional<Callback> DynarmicCP15::CompileInternalOperation(bool two, unsigned opc1,
 | 
			
		||||
                                                               CoprocReg CRd, CoprocReg CRn,
 | 
			
		||||
                                                               CoprocReg CRm, unsigned opc2) {
 | 
			
		||||
    LOG_CRITICAL(Core_ARM, "CP15: cdp{} p15, {}, {}, {}, {}, {}", two ? "2" : "", opc1, CRd, CRn,
 | 
			
		||||
                 CRm, opc2);
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn,
 | 
			
		||||
                                                         CoprocReg CRm, unsigned opc2) {
 | 
			
		||||
    // TODO(merry): Privileged CP15 registers
 | 
			
		||||
 | 
			
		||||
    if (!two && CRn == CoprocReg::C7 && opc1 == 0 && CRm == CoprocReg::C5 && opc2 == 4) {
 | 
			
		||||
        // CP15_FLUSH_PREFETCH_BUFFER
 | 
			
		||||
        // This is a dummy write, we ignore the value written here.
 | 
			
		||||
        return &CP15[static_cast<std::size_t>(CP15Register::CP15_FLUSH_PREFETCH_BUFFER)];
 | 
			
		||||
        return &dummy_value;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!two && CRn == CoprocReg::C7 && opc1 == 0 && CRm == CoprocReg::C10) {
 | 
			
		||||
        switch (opc2) {
 | 
			
		||||
        case 4:
 | 
			
		||||
            // CP15_DATA_SYNC_BARRIER
 | 
			
		||||
            // This is a dummy write, we ignore the value written here.
 | 
			
		||||
            return &CP15[static_cast<std::size_t>(CP15Register::CP15_DATA_SYNC_BARRIER)];
 | 
			
		||||
            return &dummy_value;
 | 
			
		||||
        case 5:
 | 
			
		||||
            // CP15_DATA_MEMORY_BARRIER
 | 
			
		||||
            // This is a dummy write, we ignore the value written here.
 | 
			
		||||
            return &CP15[static_cast<std::size_t>(CP15Register::CP15_DATA_MEMORY_BARRIER)];
 | 
			
		||||
        default:
 | 
			
		||||
            return {};
 | 
			
		||||
            return &dummy_value;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!two && CRn == CoprocReg::C13 && opc1 == 0 && CRm == CoprocReg::C0 && opc2 == 2) {
 | 
			
		||||
        return &CP15[static_cast<std::size_t>(CP15Register::CP15_THREAD_UPRW)];
 | 
			
		||||
        // CP15_THREAD_UPRW
 | 
			
		||||
        return &uprw;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LOG_CRITICAL(Core_ARM, "CP15: mcr{} p15, {}, <Rt>, {}, {}, {}", two ? "2" : "", opc1, CRn, CRm,
 | 
			
		||||
                 opc2);
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CallbackOrAccessTwoWords DynarmicCP15::CompileSendTwoWords(bool two, unsigned opc, CoprocReg CRm) {
 | 
			
		||||
    LOG_CRITICAL(Core_ARM, "CP15: mcrr{} p15, {}, <Rt>, <Rt2>, {}", two ? "2" : "", opc, CRm);
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CallbackOrAccessOneWord DynarmicCP15::CompileGetOneWord(bool two, unsigned opc1, CoprocReg CRn,
 | 
			
		||||
                                                        CoprocReg CRm, unsigned opc2) {
 | 
			
		||||
    // TODO(merry): Privileged CP15 registers
 | 
			
		||||
 | 
			
		||||
    if (!two && CRn == CoprocReg::C13 && opc1 == 0 && CRm == CoprocReg::C0) {
 | 
			
		||||
        switch (opc2) {
 | 
			
		||||
        case 2:
 | 
			
		||||
            return &CP15[static_cast<std::size_t>(CP15Register::CP15_THREAD_UPRW)];
 | 
			
		||||
            // CP15_THREAD_UPRW
 | 
			
		||||
            return &uprw;
 | 
			
		||||
        case 3:
 | 
			
		||||
            return &CP15[static_cast<std::size_t>(CP15Register::CP15_THREAD_URO)];
 | 
			
		||||
        default:
 | 
			
		||||
            return {};
 | 
			
		||||
            // CP15_THREAD_URO
 | 
			
		||||
            return &uro;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    LOG_CRITICAL(Core_ARM, "CP15: mrc{} p15, {}, <Rt>, {}, {}, {}", two ? "2" : "", opc1, CRn, CRm,
 | 
			
		||||
                 opc2);
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CallbackOrAccessTwoWords DynarmicCP15::CompileGetTwoWords(bool two, unsigned opc, CoprocReg CRm) {
 | 
			
		||||
    LOG_CRITICAL(Core_ARM, "CP15: mrrc{} p15, {}, <Rt>, <Rt2>, {}", two ? "2" : "", opc, CRm);
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<Callback> DynarmicCP15::CompileLoadWords(bool two, bool long_transfer, CoprocReg CRd,
 | 
			
		||||
                                                       std::optional<u8> option) {
 | 
			
		||||
    if (option) {
 | 
			
		||||
        LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...], {}", two ? "2" : "",
 | 
			
		||||
                     long_transfer ? "l" : "", CRd, *option);
 | 
			
		||||
    } else {
 | 
			
		||||
        LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "",
 | 
			
		||||
                     long_transfer ? "l" : "", CRd);
 | 
			
		||||
    }
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd,
 | 
			
		||||
                                                        std::optional<u8> option) {
 | 
			
		||||
    if (option) {
 | 
			
		||||
        LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...], {}", two ? "2" : "",
 | 
			
		||||
                     long_transfer ? "l" : "", CRd, *option);
 | 
			
		||||
    } else {
 | 
			
		||||
        LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "",
 | 
			
		||||
                     long_transfer ? "l" : "", CRd);
 | 
			
		||||
    }
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace Core
 | 
			
		||||
 
 | 
			
		||||
@@ -10,128 +10,15 @@
 | 
			
		||||
#include <dynarmic/A32/coprocessor.h>
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
 | 
			
		||||
enum class CP15Register {
 | 
			
		||||
    // c0 - Information registers
 | 
			
		||||
    CP15_MAIN_ID,
 | 
			
		||||
    CP15_CACHE_TYPE,
 | 
			
		||||
    CP15_TCM_STATUS,
 | 
			
		||||
    CP15_TLB_TYPE,
 | 
			
		||||
    CP15_CPU_ID,
 | 
			
		||||
    CP15_PROCESSOR_FEATURE_0,
 | 
			
		||||
    CP15_PROCESSOR_FEATURE_1,
 | 
			
		||||
    CP15_DEBUG_FEATURE_0,
 | 
			
		||||
    CP15_AUXILIARY_FEATURE_0,
 | 
			
		||||
    CP15_MEMORY_MODEL_FEATURE_0,
 | 
			
		||||
    CP15_MEMORY_MODEL_FEATURE_1,
 | 
			
		||||
    CP15_MEMORY_MODEL_FEATURE_2,
 | 
			
		||||
    CP15_MEMORY_MODEL_FEATURE_3,
 | 
			
		||||
    CP15_ISA_FEATURE_0,
 | 
			
		||||
    CP15_ISA_FEATURE_1,
 | 
			
		||||
    CP15_ISA_FEATURE_2,
 | 
			
		||||
    CP15_ISA_FEATURE_3,
 | 
			
		||||
    CP15_ISA_FEATURE_4,
 | 
			
		||||
namespace Core {
 | 
			
		||||
 | 
			
		||||
    // c1 - Control registers
 | 
			
		||||
    CP15_CONTROL,
 | 
			
		||||
    CP15_AUXILIARY_CONTROL,
 | 
			
		||||
    CP15_COPROCESSOR_ACCESS_CONTROL,
 | 
			
		||||
 | 
			
		||||
    // c2 - Translation table registers
 | 
			
		||||
    CP15_TRANSLATION_BASE_TABLE_0,
 | 
			
		||||
    CP15_TRANSLATION_BASE_TABLE_1,
 | 
			
		||||
    CP15_TRANSLATION_BASE_CONTROL,
 | 
			
		||||
    CP15_DOMAIN_ACCESS_CONTROL,
 | 
			
		||||
    CP15_RESERVED,
 | 
			
		||||
 | 
			
		||||
    // c5 - Fault status registers
 | 
			
		||||
    CP15_FAULT_STATUS,
 | 
			
		||||
    CP15_INSTR_FAULT_STATUS,
 | 
			
		||||
    CP15_COMBINED_DATA_FSR = CP15_FAULT_STATUS,
 | 
			
		||||
    CP15_INST_FSR,
 | 
			
		||||
 | 
			
		||||
    // c6 - Fault Address registers
 | 
			
		||||
    CP15_FAULT_ADDRESS,
 | 
			
		||||
    CP15_COMBINED_DATA_FAR = CP15_FAULT_ADDRESS,
 | 
			
		||||
    CP15_WFAR,
 | 
			
		||||
    CP15_IFAR,
 | 
			
		||||
 | 
			
		||||
    // c7 - Cache operation registers
 | 
			
		||||
    CP15_WAIT_FOR_INTERRUPT,
 | 
			
		||||
    CP15_PHYS_ADDRESS,
 | 
			
		||||
    CP15_INVALIDATE_INSTR_CACHE,
 | 
			
		||||
    CP15_INVALIDATE_INSTR_CACHE_USING_MVA,
 | 
			
		||||
    CP15_INVALIDATE_INSTR_CACHE_USING_INDEX,
 | 
			
		||||
    CP15_FLUSH_PREFETCH_BUFFER,
 | 
			
		||||
    CP15_FLUSH_BRANCH_TARGET_CACHE,
 | 
			
		||||
    CP15_FLUSH_BRANCH_TARGET_CACHE_ENTRY,
 | 
			
		||||
    CP15_INVALIDATE_DATA_CACHE,
 | 
			
		||||
    CP15_INVALIDATE_DATA_CACHE_LINE_USING_MVA,
 | 
			
		||||
    CP15_INVALIDATE_DATA_CACHE_LINE_USING_INDEX,
 | 
			
		||||
    CP15_INVALIDATE_DATA_AND_INSTR_CACHE,
 | 
			
		||||
    CP15_CLEAN_DATA_CACHE,
 | 
			
		||||
    CP15_CLEAN_DATA_CACHE_LINE_USING_MVA,
 | 
			
		||||
    CP15_CLEAN_DATA_CACHE_LINE_USING_INDEX,
 | 
			
		||||
    CP15_DATA_SYNC_BARRIER,
 | 
			
		||||
    CP15_DATA_MEMORY_BARRIER,
 | 
			
		||||
    CP15_CLEAN_AND_INVALIDATE_DATA_CACHE,
 | 
			
		||||
    CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_MVA,
 | 
			
		||||
    CP15_CLEAN_AND_INVALIDATE_DATA_CACHE_LINE_USING_INDEX,
 | 
			
		||||
 | 
			
		||||
    // c8 - TLB operations
 | 
			
		||||
    CP15_INVALIDATE_ITLB,
 | 
			
		||||
    CP15_INVALIDATE_ITLB_SINGLE_ENTRY,
 | 
			
		||||
    CP15_INVALIDATE_ITLB_ENTRY_ON_ASID_MATCH,
 | 
			
		||||
    CP15_INVALIDATE_ITLB_ENTRY_ON_MVA,
 | 
			
		||||
    CP15_INVALIDATE_DTLB,
 | 
			
		||||
    CP15_INVALIDATE_DTLB_SINGLE_ENTRY,
 | 
			
		||||
    CP15_INVALIDATE_DTLB_ENTRY_ON_ASID_MATCH,
 | 
			
		||||
    CP15_INVALIDATE_DTLB_ENTRY_ON_MVA,
 | 
			
		||||
    CP15_INVALIDATE_UTLB,
 | 
			
		||||
    CP15_INVALIDATE_UTLB_SINGLE_ENTRY,
 | 
			
		||||
    CP15_INVALIDATE_UTLB_ENTRY_ON_ASID_MATCH,
 | 
			
		||||
    CP15_INVALIDATE_UTLB_ENTRY_ON_MVA,
 | 
			
		||||
 | 
			
		||||
    // c9 - Data cache lockdown register
 | 
			
		||||
    CP15_DATA_CACHE_LOCKDOWN,
 | 
			
		||||
 | 
			
		||||
    // c10 - TLB/Memory map registers
 | 
			
		||||
    CP15_TLB_LOCKDOWN,
 | 
			
		||||
    CP15_PRIMARY_REGION_REMAP,
 | 
			
		||||
    CP15_NORMAL_REGION_REMAP,
 | 
			
		||||
 | 
			
		||||
    // c13 - Thread related registers
 | 
			
		||||
    CP15_PID,
 | 
			
		||||
    CP15_CONTEXT_ID,
 | 
			
		||||
    CP15_THREAD_UPRW, // Thread ID register - User/Privileged Read/Write
 | 
			
		||||
    CP15_THREAD_URO,  // Thread ID register - User Read Only (Privileged R/W)
 | 
			
		||||
    CP15_THREAD_PRW,  // Thread ID register - Privileged R/W only.
 | 
			
		||||
 | 
			
		||||
    // c15 - Performance and TLB lockdown registers
 | 
			
		||||
    CP15_PERFORMANCE_MONITOR_CONTROL,
 | 
			
		||||
    CP15_CYCLE_COUNTER,
 | 
			
		||||
    CP15_COUNT_0,
 | 
			
		||||
    CP15_COUNT_1,
 | 
			
		||||
    CP15_READ_MAIN_TLB_LOCKDOWN_ENTRY,
 | 
			
		||||
    CP15_WRITE_MAIN_TLB_LOCKDOWN_ENTRY,
 | 
			
		||||
    CP15_MAIN_TLB_LOCKDOWN_VIRT_ADDRESS,
 | 
			
		||||
    CP15_MAIN_TLB_LOCKDOWN_PHYS_ADDRESS,
 | 
			
		||||
    CP15_MAIN_TLB_LOCKDOWN_ATTRIBUTE,
 | 
			
		||||
    CP15_TLB_DEBUG_CONTROL,
 | 
			
		||||
 | 
			
		||||
    // Skyeye defined
 | 
			
		||||
    CP15_TLB_FAULT_ADDR,
 | 
			
		||||
    CP15_TLB_FAULT_STATUS,
 | 
			
		||||
 | 
			
		||||
    // Not an actual register.
 | 
			
		||||
    // All registers should be defined above this.
 | 
			
		||||
    CP15_REGISTER_COUNT,
 | 
			
		||||
};
 | 
			
		||||
class ARM_Dynarmic_32;
 | 
			
		||||
 | 
			
		||||
class DynarmicCP15 final : public Dynarmic::A32::Coprocessor {
 | 
			
		||||
public:
 | 
			
		||||
    using CoprocReg = Dynarmic::A32::CoprocReg;
 | 
			
		||||
 | 
			
		||||
    explicit DynarmicCP15(u32* cp15) : CP15(cp15){};
 | 
			
		||||
    explicit DynarmicCP15(ARM_Dynarmic_32& parent) : parent(parent) {}
 | 
			
		||||
 | 
			
		||||
    std::optional<Callback> CompileInternalOperation(bool two, unsigned opc1, CoprocReg CRd,
 | 
			
		||||
                                                     CoprocReg CRn, CoprocReg CRm,
 | 
			
		||||
@@ -147,6 +34,9 @@ public:
 | 
			
		||||
    std::optional<Callback> CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd,
 | 
			
		||||
                                              std::optional<u8> option) override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    u32* CP15{};
 | 
			
		||||
    ARM_Dynarmic_32& parent;
 | 
			
		||||
    u32 uprw;
 | 
			
		||||
    u32 uro;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace Core
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user