/* * Copyright (c) 2013-2014 Wind River Systems, Inc. * Copyright (c) 2021 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ /** * @file * @brief Cache manipulation * * This module contains functions for manipulation caches. */ #include #include #include #include #include #include /* Not Write-through bit */ #define X86_REG_CR0_NW BIT(29) /* Cache Disable bit */ #define X86_REG_CR0_CD BIT(30) static inline void z_x86_wbinvd(void) { __asm__ volatile("wbinvd;\n\t" : : : "memory"); } void arch_dcache_enable(void) { unsigned long cr0 = 0; /* Enable write-back caching by clearing the NW and CD bits */ __asm__ volatile("mov %%cr0, %0;\n\t" "and %1, %0;\n\t" "mov %0, %%cr0;\n\t" : "=r" (cr0) : "i" (~(X86_REG_CR0_NW | X86_REG_CR0_CD))); } void arch_dcache_disable(void) { unsigned long cr0 = 0; /* Enter the no-fill mode by setting NW=0 and CD=1 */ __asm__ volatile("mov %%cr0, %0;\n\t" "and %1, %0;\n\t" "or %2, %0;\n\t" "mov %0, %%cr0;\n\t" : "=r" (cr0) : "i" (~(X86_REG_CR0_NW)), "i" (X86_REG_CR0_CD)); /* Flush all caches */ z_x86_wbinvd(); } int arch_dcache_flush_all(void) { z_x86_wbinvd(); return 0; } int arch_dcache_invd_all(void) { z_x86_wbinvd(); return 0; } int arch_dcache_flush_and_invd_all(void) { z_x86_wbinvd(); return 0; } /** * No alignment is required for either or , but since * sys_cache_flush() iterates on the cache lines, a cache line alignment for * both is optimal. * * The cache line size is specified via the d-cache-line-size DTS property. */ int arch_dcache_flush_range(void *start_addr, size_t size) { size_t line_size = sys_cache_data_line_size_get(); uintptr_t start = (uintptr_t)start_addr; uintptr_t end = start + size; if (line_size == 0U) { return -ENOTSUP; } end = ROUND_UP(end, line_size); for (; start < end; start += line_size) { __asm__ volatile("clflush %0;\n\t" : "+m"(*(volatile char *)start)); } #if defined(CONFIG_X86_MFENCE_INSTRUCTION_SUPPORTED) __asm__ volatile("mfence;\n\t":::"memory"); #else __asm__ volatile("lock; addl $0,-4(%%esp);\n\t":::"memory", "cc"); #endif return 0; } int arch_dcache_invd_range(void *start_addr, size_t size) { return arch_dcache_flush_range(start_addr, size); } int arch_dcache_flush_and_invd_range(void *start_addr, size_t size) { return arch_dcache_flush_range(start_addr, size); }