hw: avoid overhead for cache maintainance

When running on x86, and riscv never enter the kernel for cache maintainance,
but use the dummy implementation of the generic base library instead.

On ARMv8 it is not necessary to enter privileged mode for cache cleaning, and
unification of instruction/data cache, but only for invalidating cache lines
at all levels, which is necessary for the use cases, where this function it
needed (coherency of DMA memory).

Fix genodelabs/genode#4339
This commit is contained in:
Stefan Kalkowski 2022-12-19 11:17:43 +01:00 committed by Christian Helmuth
parent 66fd027b96
commit 08c56e61e1
12 changed files with 170 additions and 63 deletions

View File

@ -0,0 +1,3 @@
vpath cache.cc $(REP_DIR)/src/lib/base/arm
include $(REP_DIR)/lib/mk/base-hw.inc

View File

@ -0,0 +1,3 @@
vpath cache.cc $(REP_DIR)/src/lib/base/arm_64
include $(REP_DIR)/lib/mk/base-hw.inc

View File

@ -0,0 +1 @@
include $(REP_DIR)/lib/mk/base-hw.inc

View File

@ -0,0 +1 @@
include $(REP_DIR)/lib/mk/base-hw.inc

View File

@ -133,7 +133,7 @@ SRC_CORE += $(notdir $(wildcard $(BASE_HW_DIR)/src/core/*.cc)) \
# names of the lib/mk/ files to consider for inclusion in the src archive
LIB_MK_FILES := base-common.inc base-hw-common.mk \
base.inc base-hw.mk \
base.inc base-hw.inc base-hw.mk \
bootstrap-hw.inc bootstrap-hw-$(BOARD).inc bootstrap-hw-$(BOARD).mk \
core-hw.inc core-hw-$(BOARD).inc core-hw-$(BOARD).mk \
startup.inc startup-hw.mk \

View File

@ -57,6 +57,10 @@ struct Genode::Cpu : Arm_cpu
return instruction_cache_line_size;
}
static inline size_t cache_line_size() {
return Genode::min(data_cache_line_size(),
instruction_cache_line_size()); }
};
#endif /* _CORE__SPEC__ARM_V6__CPU_H_ */

View File

@ -79,6 +79,11 @@ struct Genode::Arm_v7_cpu : Arm_cpu
return instruction_cache_line_size;
}
static inline size_t cache_line_size() {
return Genode::min(data_cache_line_size(),
instruction_cache_line_size()); }
};
#endif /* _CORE__SPEC__ARM_V7__CPU_SUPPORT_H_ */

View File

@ -0,0 +1,54 @@
/*
* \brief Cache maintainance utilities
* \author Stefan Kalkowski
* \date 2022-12-16
*/
/*
* Copyright (C) 2022 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__BASE__INTERNAL__CACHE_H_
#define _INCLUDE__BASE__INTERNAL__CACHE_H_
#include <base/internal/page_size.h>
#include <util/misc_math.h>
#include <util/touch.h>
template <typename FN>
static inline void for_each_page(Genode::addr_t addr,
Genode::size_t size,
FN const & fn)
{
using namespace Genode;
while (size) {
addr_t next_page = align_addr(addr+1, get_page_size_log2());
size_t s = min(size, next_page - addr);
touch_read(reinterpret_cast<unsigned char *>(addr));
fn(addr, s);
addr += s;
size -= s;
}
}
template <typename FN>
static inline void for_each_cache_line(Genode::addr_t addr,
Genode::size_t size,
FN const & fn)
{
using namespace Genode;
static size_t cache_line_size = Kernel::cache_line_size();
addr_t start = addr;
addr_t const end = addr + size;
for (; start < end; start += cache_line_size)
fn(start);
}
#endif /* _INCLUDE__BASE__INTERNAL__CACHE_H_ */

View File

@ -0,0 +1,41 @@
/*
* \brief Implementation of cache operations for ARM
* \author Stefan Kalkowski
* \date 2022-12-16
*/
/*
* Copyright (C) 2022 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <kernel/interface.h>
#include <base/internal/cache.h>
#include <cpu/cache.h>
using namespace Genode;
void Genode::cache_coherent(addr_t addr, size_t size)
{
for_each_page(addr, size, [] (addr_t addr, size_t size) {
Kernel::cache_coherent_region(addr, size); });
}
void Genode::cache_clean_invalidate_data(addr_t addr, size_t size)
{
for_each_page(addr, size, [] (addr_t addr, size_t size) {
Kernel::cache_clean_invalidate_data_region(addr, size); });
}
void Genode::cache_invalidate_data(addr_t addr, size_t size)
{
for_each_page(addr, size, [] (addr_t addr, size_t size) {
Kernel::cache_invalidate_data_region(addr, size); });
}

View File

@ -0,0 +1,57 @@
/*
* \brief Implementation of the cache maintainance on ARMv8
* \author Stefan Kalkowski
* \date 2022-12-16
*/
/*
* Copyright (C) 2022 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <kernel/interface.h>
#include <base/internal/cache.h>
#include <cpu/cache.h>
#include <cpu/memory_barrier.h>
using namespace Genode;
void Genode::cache_coherent(addr_t addr, size_t size)
{
Genode::memory_barrier();
for_each_page(addr, size, [] (addr_t addr, size_t size) {
for_each_cache_line(addr, size, [&] (addr_t addr) {
asm volatile("dc cvau, %0" :: "r" (addr));
asm volatile("dsb ish");
asm volatile("ic ivau, %0" :: "r" (addr));
asm volatile("dsb ish");
asm volatile("isb");
});
});
}
void Genode::cache_clean_invalidate_data(addr_t addr, size_t size)
{
Genode::memory_barrier();
for_each_page(addr, size, [&] (addr_t addr, size_t size) {
for_each_cache_line(addr, size, [&] (addr_t addr) {
asm volatile("dc civac, %0" :: "r" (addr)); });
});
asm volatile("dsb sy");
asm volatile("isb");
}
void Genode::cache_invalidate_data(addr_t addr, size_t size)
{
for_each_page(addr, size, [] (addr_t addr, size_t size) {
Kernel::cache_invalidate_data_region(addr, size); });
}

View File

@ -1,62 +0,0 @@
/*
* \brief Implementation of the cache operations
* \author Christian Prochaska
* \date 2014-05-13
*/
/*
* Copyright (C) 2014-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#include <kernel/interface.h>
#include <base/internal/page_size.h>
#include <cpu/cache.h>
#include <util/misc_math.h>
#include <util/touch.h>
using namespace Genode;
template <typename FN>
static void for_cachelines(addr_t addr, size_t size, FN const & fn)
{
/**
* The kernel accepts the cache maintainance calls for one designated
* page only. Otherwise, it just ignores the call to limit the time being
* uninteruppptible in the kernel. Therefore, we have to loop if more than
* one page is affected by the given region.
*/
while (size) {
addr_t next_page = align_addr(addr+1, get_page_size_log2());
size_t s = min(size, next_page - addr);
touch_read(reinterpret_cast<unsigned char *>(addr));
fn(addr, s);
addr += s;
size -= s;
}
}
void Genode::cache_coherent(addr_t addr, size_t size)
{
for_cachelines(addr, size, [] (addr_t addr, size_t size) {
Kernel::cache_coherent_region(addr, size); });
}
void Genode::cache_clean_invalidate_data(addr_t addr, size_t size)
{
for_cachelines(addr, size, [] (addr_t addr, size_t size) {
Kernel::cache_clean_invalidate_data_region(addr, size); });
}
void Genode::cache_invalidate_data(addr_t addr, size_t size)
{
for_cachelines(addr, size, [] (addr_t addr, size_t size) {
Kernel::cache_invalidate_data_region(addr, size); });
}