mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-23 08:35:19 -05:00
7c5d838d70
ON/OFF in the keys was swapped between the first and second argument of
the prctl. The prctl key is always PR_RISCV_SET_ICACHE_FLUSH_CTX, and
the second argument can be PR_RISCV_CTX_SW_FENCEI_ON or
PR_RISCV_CTX_SW_FENCEI_OFF.
Signed-off-by: Charlie Jenkins <charlie@rivosinc.com>
Fixes: 6a08e4709c
("documentation: Document PR_RISCV_SET_ICACHE_FLUSH_CTX prctl")
Link: https://lore.kernel.org/r/20240628-fix_cmodx_example-v1-1-e6c6523bc163@rivosinc.com
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
98 lines
3.4 KiB
ReStructuredText
98 lines
3.4 KiB
ReStructuredText
.. SPDX-License-Identifier: GPL-2.0
|
|
|
|
==============================================================================
|
|
Concurrent Modification and Execution of Instructions (CMODX) for RISC-V Linux
|
|
==============================================================================
|
|
|
|
CMODX is a programming technique where a program executes instructions that were
|
|
modified by the program itself. Instruction storage and the instruction cache
|
|
(icache) are not guaranteed to be synchronized on RISC-V hardware. Therefore, the
|
|
program must enforce its own synchronization with the unprivileged fence.i
|
|
instruction.
|
|
|
|
However, the default Linux ABI prohibits the use of fence.i in userspace
|
|
applications. At any point the scheduler may migrate a task onto a new hart. If
|
|
migration occurs after the userspace synchronized the icache and instruction
|
|
storage with fence.i, the icache on the new hart will no longer be clean. This
|
|
is due to the behavior of fence.i only affecting the hart that it is called on.
|
|
Thus, the hart that the task has been migrated to may not have synchronized
|
|
instruction storage and icache.
|
|
|
|
There are two ways to solve this problem: use the riscv_flush_icache() syscall,
|
|
or use the ``PR_RISCV_SET_ICACHE_FLUSH_CTX`` prctl() and emit fence.i in
|
|
userspace. The syscall performs a one-off icache flushing operation. The prctl
|
|
changes the Linux ABI to allow userspace to emit icache flushing operations.
|
|
|
|
As an aside, "deferred" icache flushes can sometimes be triggered in the kernel.
|
|
At the time of writing, this only occurs during the riscv_flush_icache() syscall
|
|
and when the kernel uses copy_to_user_page(). These deferred flushes happen only
|
|
when the memory map being used by a hart changes. If the prctl() context caused
|
|
an icache flush, this deferred icache flush will be skipped as it is redundant.
|
|
Therefore, there will be no additional flush when using the riscv_flush_icache()
|
|
syscall inside of the prctl() context.
|
|
|
|
prctl() Interface
|
|
---------------------
|
|
|
|
Call prctl() with ``PR_RISCV_SET_ICACHE_FLUSH_CTX`` as the first argument. The
|
|
remaining arguments will be delegated to the riscv_set_icache_flush_ctx
|
|
function detailed below.
|
|
|
|
.. kernel-doc:: arch/riscv/mm/cacheflush.c
|
|
:identifiers: riscv_set_icache_flush_ctx
|
|
|
|
Example usage:
|
|
|
|
The following files are meant to be compiled and linked with each other. The
|
|
modify_instruction() function replaces an add with 0 with an add with one,
|
|
causing the instruction sequence in get_value() to change from returning a zero
|
|
to returning a one.
|
|
|
|
cmodx.c::
|
|
|
|
#include <stdio.h>
|
|
#include <sys/prctl.h>
|
|
|
|
extern int get_value();
|
|
extern void modify_instruction();
|
|
|
|
int main()
|
|
{
|
|
int value = get_value();
|
|
printf("Value before cmodx: %d\n", value);
|
|
|
|
// Call prctl before first fence.i is called inside modify_instruction
|
|
prctl(PR_RISCV_SET_ICACHE_FLUSH_CTX, PR_RISCV_CTX_SW_FENCEI_ON, PR_RISCV_SCOPE_PER_PROCESS);
|
|
modify_instruction();
|
|
// Call prctl after final fence.i is called in process
|
|
prctl(PR_RISCV_SET_ICACHE_FLUSH_CTX, PR_RISCV_CTX_SW_FENCEI_OFF, PR_RISCV_SCOPE_PER_PROCESS);
|
|
|
|
value = get_value();
|
|
printf("Value after cmodx: %d\n", value);
|
|
return 0;
|
|
}
|
|
|
|
cmodx.S::
|
|
|
|
.option norvc
|
|
|
|
.text
|
|
.global modify_instruction
|
|
modify_instruction:
|
|
lw a0, new_insn
|
|
lui a5,%hi(old_insn)
|
|
sw a0,%lo(old_insn)(a5)
|
|
fence.i
|
|
ret
|
|
|
|
.section modifiable, "awx"
|
|
.global get_value
|
|
get_value:
|
|
li a0, 0
|
|
old_insn:
|
|
addi a0, a0, 0
|
|
ret
|
|
|
|
.data
|
|
new_insn:
|
|
addi a0, a0, 1
|