Skip to content

Commit fb11de8

Browse files
committed
Reduce RFENCE IPI cache flush scope
This optimizes RFENCE.VMA to use range-based cache invalidation instead of unconditionally flushing all MMU caches. It adds mmu_invalidate_range that selectively invalidates only cache entries within the specified virtual address range, reducing cache flushes by 75-100% for single-page operations. SBI compliance: size==0 and size==-1 trigger full flush per specification.
1 parent bb10925 commit fb11de8

File tree

3 files changed

+77
-12
lines changed

3 files changed

+77
-12
lines changed

main.c

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -437,28 +437,42 @@ static inline sbi_ret_t handle_sbi_ecall_RFENCE(hart_t *hart, int32_t fid)
437437
* completely.
438438
*/
439439
uint64_t hart_mask, hart_mask_base;
440+
uint32_t start_addr, size;
440441
switch (fid) {
441-
case 0:
442+
case SBI_RFENCE__I:
443+
/* Instruction cache flush - ignored in interpreter mode */
442444
return (sbi_ret_t){SBI_SUCCESS, 0};
443-
case 1:
445+
case SBI_RFENCE__VMA:
446+
case SBI_RFENCE__VMA_ASID:
447+
/* RFENCE.VMA and RFENCE.VMA.ASID both use the same parameters:
448+
* a0: hart_mask (low bits)
449+
* a1: hart_mask_base (high bits)
450+
* a2: start_addr
451+
* a3: size
452+
* For VMA_ASID, a4 contains asid (currently ignored)
453+
*/
444454
hart_mask = (uint64_t) hart->x_regs[RV_R_A0];
445455
hart_mask_base = (uint64_t) hart->x_regs[RV_R_A1];
456+
start_addr = hart->x_regs[RV_R_A2];
457+
size = hart->x_regs[RV_R_A3];
458+
446459
if (hart_mask_base == 0xFFFFFFFFFFFFFFFF) {
447-
for (uint32_t i = 0; i < hart->vm->n_hart; i++) {
448-
mmu_invalidate(hart->vm->hart[i]);
449-
}
460+
/* Flush all harts */
461+
for (uint32_t i = 0; i < hart->vm->n_hart; i++)
462+
mmu_invalidate_range(hart->vm->hart[i], start_addr, size);
450463
} else {
464+
/* Flush specified harts based on mask */
451465
for (int i = hart_mask_base; hart_mask; hart_mask >>= 1, i++) {
452-
mmu_invalidate(hart->vm->hart[i]);
466+
if (hart_mask & 1)
467+
mmu_invalidate_range(hart->vm->hart[i], start_addr, size);
453468
}
454469
}
455470
return (sbi_ret_t){SBI_SUCCESS, 0};
456-
case 2:
457-
case 3:
458-
case 4:
459-
case 5:
460-
case 6:
461-
case 7:
471+
case SBI_RFENCE__GVMA_VMID:
472+
case SBI_RFENCE__GVMA:
473+
case SBI_RFENCE__VVMA_ASID:
474+
case SBI_RFENCE__VVMA:
475+
/* Hypervisor-related RFENCE operations - not implemented */
462476
return (sbi_ret_t){SBI_SUCCESS, 0};
463477
default:
464478
return (sbi_ret_t){SBI_ERR_FAILED, 0};

riscv.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,54 @@ void mmu_invalidate(hart_t *vm)
190190
vm->cache_store.n_pages = 0xFFFFFFFF;
191191
}
192192

193+
/* Invalidate MMU caches for a specific virtual address range.
194+
* If size is 0 or -1, invalidate all caches (equivalent to mmu_invalidate()).
195+
* Otherwise, only invalidate cache entries whose VPN falls within
196+
* [start_addr >> PAGE_SHIFT, (start_addr + size - 1) >> PAGE_SHIFT].
197+
*/
198+
void mmu_invalidate_range(hart_t *vm, uint32_t start_addr, uint32_t size)
199+
{
200+
/* SBI spec: size == 0 or size == -1 means flush entire address space */
201+
if (size == 0 || size == (uint32_t) -1) {
202+
mmu_invalidate(vm);
203+
return;
204+
}
205+
206+
/* Calculate VPN range: [start_vpn, end_vpn] inclusive.
207+
* Use 64-bit arithmetic to prevent overflow when (start_addr + size - 1)
208+
* exceeds UINT32_MAX. For example:
209+
* start_addr = 0xFFF00000, size = 0x00200000
210+
* 32-bit: 0xFFF00000 + 0x00200000 - 1 = 0x000FFFFF (wraps)
211+
* 64-bit: 0xFFF00000 + 0x00200000 - 1 = 0x100FFFFF (correct)
212+
* Clamp to RV32 address space maximum before calculating end_vpn.
213+
*/
214+
uint32_t start_vpn = start_addr >> RV_PAGE_SHIFT;
215+
uint64_t end_addr = (uint64_t) start_addr + size - 1;
216+
if (end_addr > UINT32_MAX)
217+
end_addr = UINT32_MAX;
218+
uint32_t end_vpn = (uint32_t) end_addr >> RV_PAGE_SHIFT;
219+
220+
/* Check each cache entry and invalidate if in range.
221+
* Since we only have 4 cache entries total (fetch: 1, load: 2, store: 1),
222+
* simple sequential checks are sufficient.
223+
*/
224+
if (vm->cache_fetch.n_pages >= start_vpn &&
225+
vm->cache_fetch.n_pages <= end_vpn)
226+
vm->cache_fetch.n_pages = 0xFFFFFFFF;
227+
228+
if (vm->cache_load[0].n_pages >= start_vpn &&
229+
vm->cache_load[0].n_pages <= end_vpn)
230+
vm->cache_load[0].n_pages = 0xFFFFFFFF;
231+
232+
if (vm->cache_load[1].n_pages >= start_vpn &&
233+
vm->cache_load[1].n_pages <= end_vpn)
234+
vm->cache_load[1].n_pages = 0xFFFFFFFF;
235+
236+
if (vm->cache_store.n_pages >= start_vpn &&
237+
vm->cache_store.n_pages <= end_vpn)
238+
vm->cache_store.n_pages = 0xFFFFFFFF;
239+
}
240+
193241
/* Pre-verify the root page table to minimize page table access during
194242
* translation time.
195243
*/

riscv.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,3 +190,6 @@ void vm_error_report(const hart_t *vm);
190190

191191
/* Invalidate all MMU translation caches (fetch, load, store) */
192192
void mmu_invalidate(hart_t *vm);
193+
194+
/* Invalidate MMU caches for a specific virtual address range */
195+
void mmu_invalidate_range(hart_t *vm, uint32_t start_addr, uint32_t size);

0 commit comments

Comments
 (0)