Files
android/libwvdrmengine/level3/include/clear_cache_function.h
Robert Shih cbfc3c7193 wvoec3: add membarriers to complete cache flush
Merge of http://go/wvgerrit/104543

Bug: 159465432
Test: oemcrypto_test
Change-Id: Idcf345088c1d40836b5e100d2f758147986a4b04
2021-02-28 05:57:40 +00:00

121 lines
3.7 KiB
C++

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <linux/membarrier.h>
#include <sys/syscall.h>
#include <sys/utsname.h>
#if __mips__
# include <asm/cachectl.h>
#endif
#include "log.h"
namespace wvoec3 {
bool supports_membarier_syscall() {
// Check kernel version supports membarrier(2); version 4.16 is required.
static constexpr int kRequiredMajor = 4;
static constexpr int kRequiredMinor = 16;
struct utsname uts;
int major = 0, minor = 0;
if (uname(&uts) != 0 || strcmp(uts.sysname, "Linux") != 0 ||
sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
LOGE("Failed to get kernel version");
return false;
}
if (major < kRequiredMajor ||
(major == kRequiredMajor && minor < kRequiredMinor)) {
LOGE("Kernel version %d, %d < required %d, %d", major, minor,
kRequiredMajor, kRequiredMinor);
return false;
}
return true;
}
#if defined(__NR_membarrier)
// membarrier function is used to complete flush of CPU instruction pipelines.
// Reference:
// * membarrier(2)
// * art::jit::JitMemoryRegion::CommitCode
int membarrier_function(int command) {
static bool supports_membarier = supports_membarier_syscall();
if (supports_membarier) {
return syscall(__NR_membarrier, command, 0);
}
errno = ENOSYS;
return -1;
}
#else // __NR_membarrier
# pragma message \
"(info): __NR_membarrier unavailable; membarrier_function will be no-op."
int membarrier_function(int) {
errno = ENOSYS;
return -1;
}
#endif // __NR_membarrier
// The Cache Flush function is very processor dependent, and is used by the
// Level3 code using platform-specific flags.
// Linked in separately from the rest of the build, since this code isn't
// obfuscated and relies on system flags to function properly.
void clear_cache_function(void *page, size_t len) {
// Note on cross platform support. If __has_builtin is not defined as a
// preprocessor function, we cannot use
// "#if defined(__has_builtin) && __has_builtin(..)".
// So, instead, we will define USED_BUILTIN_CLEAR_CACHE if both conditions
// are true, and use "#ifndef USED_BUILTIN_CLEAR_CACHE" instead of #else.
#ifdef __has_builtin
#if __has_builtin(__builtin___clear_cache)
#pragma message "(info): clear_cache_function is using __builtin___clear_cache."
#define USED_BUILTIN_CLEAR_CACHE
char *begin = static_cast<char *>(page);
char *end = begin + len;
__builtin___clear_cache(begin, end);
#endif
#endif
#ifndef USED_BUILTIN_CLEAR_CACHE
#if __arm__
#pragma message "(info): clear_cache_function is using arm asm."
// ARM Cache Flush System Call:
char *begin = static_cast<char *>(page);
char *end = begin + len;
const int syscall = 0xf0002;
__asm __volatile(
"push {r0, r1, r2, r7}\n"
"mov r0, %0\n"
"mov r1, %1\n"
"mov r7, %2\n"
"mov r2, #0x0\n"
"svc 0x00000000\n"
"pop {r0, r1, r2, r7}\n"
:
: "r"(begin), "r"(end), "r"(syscall)
: "r0", "r1", "r7");
#elif __mips__
#pragma message "(info): clear_cache_function is using mips asm."
int result = syscall(__NR_cacheflush, page, len, ICACHE);
if (result) {
fprintf(stderr, "cacheflush failed!: errno=%d %s\n", errno,
strerror(errno));
exit(-1); // TODO(fredgc): figure out more graceful error handling.
}
#else
#pragma message "(info): clear_cache_function is not doing anything."
// TODO(fredgc): silence warning about unused variables.
#endif
#endif
#if defined(__arm__) || defined(__aarch64__)
# pragma message "(info): inserting membarrier_function calls."
membarrier_function(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE);
membarrier_function(MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE);
#endif // defined(__arm__) || defined(__aarch64__)
}
} // namespace wvoec3