Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions arch/riscv/include/asm/cacheflush.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,25 @@ do { \
} while (0)

#ifdef CONFIG_64BIT
extern u64 new_vmalloc[NR_CPUS / sizeof(u64) + 1];
extern u64 new_valid_map_cpus[NR_CPUS / sizeof(u64) + 1];
extern char _end[];
static inline void mark_new_valid_map(void)
{
int i;

/*
* We don't care if concurrently a cpu resets this value since
* the only place this can happen is in handle_exception() where
* an sfence.vma is emitted.
*/
for (i = 0; i < ARRAY_SIZE(new_valid_map_cpus); ++i)
new_valid_map_cpus[i] = -1ULL;
}
#define flush_cache_vmap flush_cache_vmap
static inline void flush_cache_vmap(unsigned long start, unsigned long end)
{
if (is_vmalloc_or_module_addr((void *)start)) {
int i;

/*
* We don't care if concurrently a cpu resets this value since
* the only place this can happen is in handle_exception() where
* an sfence.vma is emitted.
*/
for (i = 0; i < ARRAY_SIZE(new_vmalloc); ++i)
new_vmalloc[i] = -1ULL;
}
if (is_vmalloc_or_module_addr((void *)start))
mark_new_valid_map();
}
#define flush_cache_vmap_early(start, end) local_flush_tlb_kernel_range(start, end)
#endif
Expand Down
7 changes: 5 additions & 2 deletions arch/riscv/include/asm/kfence.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <linux/kfence.h>
#include <linux/pfn.h>
#include <asm-generic/pgalloc.h>
#include <asm/cacheflush.h>
#include <asm/pgtable.h>

static inline bool arch_kfence_init_pool(void)
Expand All @@ -17,10 +18,12 @@ static inline bool kfence_protect_page(unsigned long addr, bool protect)
{
pte_t *pte = virt_to_kpte(addr);

if (protect)
if (protect) {
set_pte(pte, __pte(pte_val(ptep_get(pte)) & ~_PAGE_PRESENT));
else
} else {
set_pte(pte, __pte(pte_val(ptep_get(pte)) | _PAGE_PRESENT));
mark_new_valid_map();
}

preempt_disable();
local_flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
Expand Down
44 changes: 23 additions & 21 deletions arch/riscv/kernel/entry.S
Original file line number Diff line number Diff line change
Expand Up @@ -20,44 +20,44 @@

.section .irqentry.text, "ax"

.macro new_vmalloc_check
.macro new_valid_map_cpus_check
REG_S a0, TASK_TI_A0(tp)
csrr a0, CSR_CAUSE
/* Exclude IRQs */
blt a0, zero, .Lnew_vmalloc_restore_context_a0
blt a0, zero, .Lnew_valid_map_cpus_restore_context_a0

REG_S a1, TASK_TI_A1(tp)
/* Only check new_vmalloc if we are in page/protection fault */
/* Only check new_valid_map_cpus if we are in page/protection fault */
li a1, EXC_LOAD_PAGE_FAULT
beq a0, a1, .Lnew_vmalloc_kernel_address
beq a0, a1, .Lnew_valid_map_cpus_kernel_address
li a1, EXC_STORE_PAGE_FAULT
beq a0, a1, .Lnew_vmalloc_kernel_address
beq a0, a1, .Lnew_valid_map_cpus_kernel_address
li a1, EXC_INST_PAGE_FAULT
bne a0, a1, .Lnew_vmalloc_restore_context_a1
bne a0, a1, .Lnew_valid_map_cpus_restore_context_a1

.Lnew_vmalloc_kernel_address:
.Lnew_valid_map_cpus_kernel_address:
/* Is it a kernel address? */
csrr a0, CSR_TVAL
bge a0, zero, .Lnew_vmalloc_restore_context_a1
bge a0, zero, .Lnew_valid_map_cpus_restore_context_a1

/* Check if a new vmalloc mapping appeared that could explain the trap */
REG_S a2, TASK_TI_A2(tp)
/*
* Computes:
* a0 = &new_vmalloc[BIT_WORD(cpu)]
* a0 = &new_valid_map_cpus[BIT_WORD(cpu)]
* a1 = BIT_MASK(cpu)
*/
lw a2, TASK_TI_CPU(tp)
/*
* Compute the new_vmalloc element position:
* Compute the new_valid_map_cpus element position:
* (cpu / 64) * 8 = (cpu >> 6) << 3
*/
srli a1, a2, 6
slli a1, a1, 3
la a0, new_vmalloc
la a0, new_valid_map_cpus
add a0, a0, a1
/*
* Compute the bit position in the new_vmalloc element:
* Compute the bit position in the new_valid_map_cpus element:
* bit_pos = cpu % 64 = cpu - (cpu / 64) * 64 = cpu - (cpu >> 6) << 6
* = cpu - ((cpu >> 6) << 3) << 3
*/
Expand All @@ -67,12 +67,12 @@
li a2, 1
sll a1, a2, a1

/* Check the value of new_vmalloc for this cpu */
/* Check the value of new_valid_map_cpus for this cpu */
REG_L a2, 0(a0)
and a2, a2, a1
beq a2, zero, .Lnew_vmalloc_restore_context
beq a2, zero, .Lnew_valid_map_cpus_restore_context

/* Atomically reset the current cpu bit in new_vmalloc */
/* Atomically reset the current cpu bit in new_valid_map_cpus */
amoxor.d a0, a1, (a0)

/* Only emit a sfence.vma if the uarch caches invalid entries */
Expand All @@ -84,11 +84,11 @@
csrw CSR_SCRATCH, x0
sret

.Lnew_vmalloc_restore_context:
.Lnew_valid_map_cpus_restore_context:
REG_L a2, TASK_TI_A2(tp)
.Lnew_vmalloc_restore_context_a1:
.Lnew_valid_map_cpus_restore_context_a1:
REG_L a1, TASK_TI_A1(tp)
.Lnew_vmalloc_restore_context_a0:
.Lnew_valid_map_cpus_restore_context_a0:
REG_L a0, TASK_TI_A0(tp)
.endm

Expand Down Expand Up @@ -136,15 +136,17 @@ SYM_CODE_START(handle_exception)

#ifdef CONFIG_64BIT
/*
* The RISC-V kernel does not eagerly emit a sfence.vma after each
* new vmalloc mapping, which may result in exceptions:
* The RISC-V kernel does not flush TLBs on all CPUS after each new
* vmalloc mapping or kfence_unprotect(), which may result in
* exceptions:
*
* - if the uarch caches invalid entries, the new mapping would not be
* observed by the page table walker and an invalidation is needed.
* - if the uarch does not cache invalid entries, a reordered access
* could "miss" the new mapping and traps: in that case, we only need
* to retry the access, no sfence.vma is required.
*/
new_vmalloc_check
new_valid_map_cpus_check
#endif

REG_S sp, TASK_TI_KERNEL_SP(tp)
Expand Down
2 changes: 1 addition & 1 deletion arch/riscv/mm/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@

#include "../kernel/head.h"

u64 new_vmalloc[NR_CPUS / sizeof(u64) + 1];
u64 new_valid_map_cpus[NR_CPUS / sizeof(u64) + 1];

struct kernel_mapping kernel_map __ro_after_init;
EXPORT_SYMBOL(kernel_map);
Expand Down
Loading