/* * Copyright (c) Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ namespace { ALWAYS_INLINE int FloorLog2(int v) { return BITSIZEOF(u32) - (hw::CountLeadingZeros(static_cast(v)) + 1); } ALWAYS_INLINE int CeilLog2(int v) { const int log = FloorLog2(v); return ((1 << log) == v) ? log : log + 1; } void FlushDataCacheTo(int loc) { for (int level = 0; level < loc; ++level) { /* Set the selection register. */ { util::BitPack32 csselr = {}; csselr.Set(0); csselr.Set(level); HW_CPU_SET_CSSELR_EL1(csselr); } /* Ensure that reordering doesn't occur around this operation. */ hw::InstructionSynchronizationBarrier(); /* Get ccsidr. */ util::BitPack32 ccsidr; HW_CPU_GET_CCSIDR_EL1(ccsidr); /* Get cache size id info. */ const int num_sets = ccsidr.Get() + 1; const int num_ways = ccsidr.Get() + 1; const int line_size = ccsidr.Get() + 4; const int way_shift = 32 - FloorLog2(num_ways); const int set_shift = line_size; for (int way = 0; way <= num_ways; way++) { for (int set = 0; set <= num_sets; set++) { const u64 value = (static_cast(way) << way_shift) | (static_cast(set) << set_shift) | (static_cast(level) << 1); __asm__ __volatile__("dc cisw, %[value]" :: [value]"r"(value) : "memory"); } } } } void FlushDataCacheFrom(int loc) { for (int level = loc - 1; level >= 0; --level) { /* Set the selection register. */ { util::BitPack32 csselr = {}; csselr.Set(0); csselr.Set(level); HW_CPU_SET_CSSELR_EL1(csselr); } /* Ensure that reordering doesn't occur around this operation. */ hw::InstructionSynchronizationBarrier(); /* Get ccsidr. */ util::BitPack32 ccsidr; HW_CPU_GET_CCSIDR_EL1(ccsidr); /* Get cache size id info. */ const int num_sets = ccsidr.Get() + 1; const int num_ways = ccsidr.Get() + 1; const int line_size = ccsidr.Get() + 4; const int way_shift = 32 - FloorLog2(num_ways); const int set_shift = line_size; for (int way = 0; way <= num_ways; way++) { for (int set = 0; set <= num_sets; set++) { const u64 value = (static_cast(way) << way_shift) | (static_cast(set) << set_shift) | (static_cast(level) << 1); __asm__ __volatile__("dc cisw, %[value]" :: [value]"r"(value) : "memory"); } } } } void InvalidateDataCacheTo(int loc) { for (int level = 0; level < loc; ++level) { /* Set the selection register. */ { util::BitPack32 csselr = {}; csselr.Set(0); csselr.Set(level); HW_CPU_SET_CSSELR_EL1(csselr); } /* Ensure that reordering doesn't occur around this operation. */ hw::InstructionSynchronizationBarrier(); /* Get ccsidr. */ util::BitPack32 ccsidr; HW_CPU_GET_CCSIDR_EL1(ccsidr); /* Get cache size id info. */ const int num_sets = ccsidr.Get() + 1; const int num_ways = ccsidr.Get() + 1; const int line_size = ccsidr.Get() + 4; const int way_shift = 32 - FloorLog2(num_ways); const int set_shift = line_size; for (int way = 0; way <= num_ways; way++) { for (int set = 0; set <= num_sets; set++) { const u64 value = (static_cast(way) << way_shift) | (static_cast(set) << set_shift) | (static_cast(level) << 1); __asm__ __volatile__("dc isw, %[value]" :: [value]"r"(value) : "memory"); } } } } } void FlushEntireDataCache() { util::BitPack32 clidr; HW_CPU_GET_CLIDR_EL1(clidr); FlushDataCacheTo(clidr.Get()); } void FlushEntireDataCacheLocal() { util::BitPack32 clidr; HW_CPU_GET_CLIDR_EL1(clidr); FlushDataCacheFrom(clidr.Get()); } void InvalidateEntireDataCache() { util::BitPack32 clidr; HW_CPU_GET_CLIDR_EL1(clidr); InvalidateDataCacheTo(clidr.Get()); } void EnsureMappingConsistency() { ::ams::hw::DataSynchronizationBarrierInnerShareable(); ::ams::hw::InvalidateEntireTlb(); ::ams::hw::DataSynchronizationBarrierInnerShareable(); ::ams::hw::InstructionSynchronizationBarrier(); } void EnsureMappingConsistency(uintptr_t address) { ::ams::hw::DataSynchronizationBarrierInnerShareable(); ::ams::hw::InvalidateTlb(address); ::ams::hw::DataSynchronizationBarrierInnerShareable(); ::ams::hw::InstructionSynchronizationBarrier(); } void EnsureInstructionConsistency() { ::ams::hw::DataSynchronizationBarrierInnerShareable(); ::ams::hw::InvalidateEntireInstructionCache(); ::ams::hw::DataSynchronizationBarrierInnerShareable(); ::ams::hw::InstructionSynchronizationBarrier(); }