#[inline(always)]
pub fn prepare_for_execution(slice: &[u8]) {
#![allow(unused_variables)]
#[cfg(target_arch="aarch64")]
{
aarch64::prepare_for_execution()
}
#[cfg(all(unix, any(target_arch="riscv64", target_arch="riscv32")))]
{
riscv::enforce_ordering_dcache_icache(slice, true);
}
}
#[inline(always)]
#[allow(unused_variables)]
pub fn synchronize_icache(slice: &[u8]) {
#[cfg(all(target_arch="aarch64", not(target_os="macos")))]
{
aarch64::synchronize_icache(slice);
}
#[cfg(all(target_arch="aarch64", target_os="macos"))]
{
extern "C" {
pub fn sys_icache_invalidate(
start: *const std::ffi::c_void,
size: usize,
);
}
unsafe {
sys_icache_invalidate(slice.as_ptr() as *const std::ffi::c_void, slice.len());
}
}
}
#[cfg(target_arch="aarch64")]
mod aarch64 {
use std::arch::asm;
fn get_cacheline_sizes() -> (usize, usize) {
let ctr_el0: usize;
unsafe {
asm!(
"mrs {outreg}, ctr_el0",
outreg = lateout(reg) ctr_el0,
options(nomem, nostack, preserves_flags)
);
}
(
4 << ((ctr_el0 >> 16) & 0xF),
4 << (ctr_el0 & 0xF)
)
}
#[inline(always)]
fn wait_for_cache_ops_complete() {
unsafe {
asm!(
"dsb ish",
options(nostack, preserves_flags)
);
}
}
#[inline(always)]
fn flush_dcache_line(addr: usize) {
unsafe {
asm!(
"dc cvau, {address}",
address = in(reg)addr,
options(nostack, preserves_flags)
);
}
}
#[inline(always)]
fn invalidate_icache_line(addr: usize) {
unsafe {
asm!(
"ic ivau, {address}",
address = in(reg)addr,
options(nostack, preserves_flags)
);
}
}
#[inline(always)]
fn invalidate_pipeline() {
unsafe {
asm!(
"isb",
options(nostack, preserves_flags)
);
}
}
#[inline(always)]
pub fn prepare_for_execution() {
invalidate_pipeline();
}
pub fn synchronize_icache(slice: &[u8]) {
if slice.len() == 0 {
return;
}
let start_addr = slice.as_ptr() as usize;
let end_addr = start_addr + slice.len();
let (dcache_line_size, icache_line_size) = get_cacheline_sizes();
let mut addr = start_addr & !(dcache_line_size - 1);
while addr < end_addr {
flush_dcache_line(addr);
addr += dcache_line_size;
}
wait_for_cache_ops_complete();
addr = start_addr & !(icache_line_size - 1);
while addr < end_addr {
invalidate_icache_line(addr);
addr += icache_line_size;
}
wait_for_cache_ops_complete();
}
}
#[cfg(all(unix, any(target_arch="riscv64", target_arch="riscv32")))]
mod riscv {
use std::ffi::{c_void, c_long, c_int};
extern "C" {
#[link_name="__riscv_flush_icache"]
fn riscv_flush_icache(start: *const c_void, end: *const c_void, flags: c_long) -> c_int;
}
pub fn enforce_ordering_dcache_icache(slice: &[u8], local: bool) {
let range = slice.as_ptr_range();
let start = range.start as *const c_void;
let end = range.end as *const c_void;
let mut flags: c_long = 0;
if local {
flags |= 1;
}
let rv;
unsafe {
rv = riscv_flush_icache(start, end, flags);
}
assert!(rv == 0, "riscv_flush_icache failed, returned {rv}");
}
}