/* * Copyright 2014, General Dynamics C4 Systems * * SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #define LINE_START(a) ROUND_DOWN(a, L1_CACHE_LINE_SIZE_BITS) #define LINE_INDEX(a) (LINE_START(a)>>L1_CACHE_LINE_SIZE_BITS) static void cleanCacheRange_PoC(vptr_t start, vptr_t end, paddr_t pstart) { vptr_t line; word_t index; for (index = LINE_INDEX(start); index < LINE_INDEX(end) + 1; index++) { line = index << L1_CACHE_LINE_SIZE_BITS; cleanByVA(line, pstart + (line - start)); } } void cleanInvalidateCacheRange_RAM(vptr_t start, vptr_t end, paddr_t pstart) { vptr_t line; word_t index; /** GHOSTUPD: "((gs_get_assn cap_get_capSizeBits_'proc \ghost'state = 0 \ \end - \start <= gs_get_assn cap_get_capSizeBits_'proc \ghost'state) \ \start <= \end, id)" */ /* First clean the L1 range */ cleanCacheRange_PoC(start, end, pstart); /* ensure operation completes and visible in L2 */ dsb(); /* Now clean and invalidate the L2 range */ plat_cleanInvalidateL2Range(pstart, pstart + (end - start)); /* Finally clean and invalidate the L1 range. The extra clean is only strictly neccessary * in a multiprocessor environment to prevent a write being lost if another core is * attempting a store at the same time. As the range should already be clean asking * it to clean again should not affect performance */ for (index = LINE_INDEX(start); index < LINE_INDEX(end) + 1; index++) { line = index << L1_CACHE_LINE_SIZE_BITS; cleanInvalByVA(line, pstart + (line - start)); } /* ensure clean and invalidate complete */ dsb(); } void cleanCacheRange_RAM(vptr_t start, vptr_t end, paddr_t pstart) { /** GHOSTUPD: "((gs_get_assn cap_get_capSizeBits_'proc \ghost'state = 0 \ \end - \start <= gs_get_assn cap_get_capSizeBits_'proc \ghost'state) \ \start <= \end \ \pstart <= \pstart + (\end - \start), id)" */ /* clean l1 to l2 */ cleanCacheRange_PoC(start, end, pstart); /* ensure cache operation completes before cleaning l2 */ dsb(); /** GHOSTUPD: "((gs_get_assn cap_get_capSizeBits_'proc \ghost'state = 0 \ \end - \start <= gs_get_assn cap_get_capSizeBits_'proc \ghost'state) \ \start <= \end \ \pstart <= \pstart + (\end - \start), id)" */ /* now clean l2 to RAM */ plat_cleanL2Range(pstart, pstart + (end - start)); } void cleanCacheRange_PoU(vptr_t start, vptr_t end, paddr_t pstart) { vptr_t line; word_t index; /** GHOSTUPD: "((gs_get_assn cap_get_capSizeBits_'proc \ghost'state = 0 \ \end - \start <= gs_get_assn cap_get_capSizeBits_'proc \ghost'state) \ \start <= \end \ \pstart <= \pstart + (\end - \start), id)" */ for (index = LINE_INDEX(start); index < LINE_INDEX(end) + 1; index++) { line = index << L1_CACHE_LINE_SIZE_BITS; cleanByVA_PoU(line, pstart + (line - start)); } } void invalidateCacheRange_RAM(vptr_t start, vptr_t end, paddr_t pstart) { vptr_t line; word_t index; /* If the start and end are not aligned to a cache line boundary * then we need to clean the line first to prevent invalidating * bytes we didn't mean to. Calling the functions in this way is * not the most efficient method, but we assume the user will * rarely be this silly */ if (start != LINE_START(start)) { cleanCacheRange_RAM(start, start, pstart); } if (end + 1 != LINE_START(end + 1)) { line = LINE_START(end); cleanCacheRange_RAM(line, line, pstart + (line - start)); } /** GHOSTUPD: "((gs_get_assn cap_get_capSizeBits_'proc \ghost'state = 0 \ \end - \start <= gs_get_assn cap_get_capSizeBits_'proc \ghost'state) \ \start <= \end \ \pstart <= \pstart + (\end - \start), id)" */ /* Invalidate L2 range. Invalidating the L2 before the L1 is the order * given in the l2c_310 manual, as an L1 line might be allocated from the L2 * before the L2 can be invalidated. */ plat_invalidateL2Range(pstart, pstart + (end - start)); /** GHOSTUPD: "((gs_get_assn cap_get_capSizeBits_'proc \ghost'state = 0 \ \end - \start <= gs_get_assn cap_get_capSizeBits_'proc \ghost'state) \ \start <= \end \ \pstart <= \pstart + (\end - \start), id)" */ /* Now invalidate L1 range */ for (index = LINE_INDEX(start); index < LINE_INDEX(end) + 1; index++) { line = index << L1_CACHE_LINE_SIZE_BITS; invalidateByVA(line, pstart + (line - start)); } /* Ensure invalidate completes */ dsb(); } void invalidateCacheRange_I(vptr_t start, vptr_t end, paddr_t pstart) { vptr_t line; word_t index; for (index = LINE_INDEX(start); index < LINE_INDEX(end) + 1; index++) { line = index << L1_CACHE_LINE_SIZE_BITS; invalidateByVA_I(line, pstart + (line - start)); } } void branchFlushRange(vptr_t start, vptr_t end, paddr_t pstart) { vptr_t line; word_t index; for (index = LINE_INDEX(start); index < LINE_INDEX(end) + 1; index++) { line = index << L1_CACHE_LINE_SIZE_BITS; branchFlush(line, pstart + (line - start)); } } void cleanCaches_PoU(void) { dsb(); clean_D_PoU(); dsb(); invalidate_I_PoU(); dsb(); } void cleanInvalidateL1Caches(void) { dsb(); cleanInvalidate_D_PoC(); dsb(); invalidate_I_PoU(); dsb(); } void arch_clean_invalidate_caches(void) { cleanCaches_PoU(); plat_cleanInvalidateL2Cache(); cleanInvalidateL1Caches(); isb(); } void arch_clean_invalidate_L1_caches(word_t type) { dsb(); if (type & BIT(1)) { cleanInvalidate_L1D(); dsb(); } if (type & BIT(0)) { invalidate_I_PoU(); dsb(); isb(); } }