mirror of
https://github.com/RT-Thread/rt-thread.git
synced 2026-10-02 14:23:19 +08:00
[libcpu-riscv]: [surpport SMP]: Add SMP support for qemu-virt64-riscv
1.Add the necessary function declarations for SMP enablement and implement the corresponding functionalities, including rt_hw_secondary_cpu_up, secondary_cpu_entry, rt_hw_local_irq_disable, rt_hw_local_irq_enable, rt_hw_secondary_cpu_idle_exec, rt_hw_spin_lock_init, rt_hw_spin_lock, rt_hw_spin_unlock, rt_hw_ipi_send, rt_hw_interrupt_set_priority, rt_hw_interrupt_get_priority, rt_hw_ipi_init, rt_hw_ipi_handler_install, and rt_hw_ipi_handler. 2.In the two functions (rt_hw_context_switch_to and rt_hw_context_switch) in context_gcc.S, add a call to rt_cpus_lock_status_restore to update the scheduler information. 3.If the MMU is enabled, use the .percpu section and record different hartids by configuring special page tables; if the MMU is not enabled, record them directly in the satp register. Additionally, add dynamic startup based on core configuration.The .percpu section is only used when both ARCH_MM_MMU and RT_USING_SMP are enabled. However, there is a certain amount of space waste since no macro guard is added for it in the link script currently. 4.The physical memory of QEMU started in CI is 128MB, so RT_HW_PAGE_END is modified from the original +256MB to +128MB. Modify the SConscript file under the common64 directory to include common/atomic_riscv.c in the compilation process. Signed-off-by: Mengchen Teng <teng_mengchen@163.com>
This commit is contained in:
@@ -7,6 +7,8 @@ CPPPATH = [cwd]
|
||||
if not GetDepend('ARCH_USING_ASID'):
|
||||
SrcRemove(src, ['asid.c'])
|
||||
|
||||
src.append('../common/atomic_riscv.c')
|
||||
|
||||
group = DefineGroup('CPU', src, depend = [''], CPPPATH = CPPPATH)
|
||||
|
||||
Return('group')
|
||||
|
||||
@@ -69,29 +69,47 @@
|
||||
.endm
|
||||
|
||||
/*
|
||||
* #ifdef RT_USING_SMP
|
||||
* void rt_hw_context_switch_to(rt_ubase_t to, stuct rt_thread *to_thread);
|
||||
* #else
|
||||
* void rt_hw_context_switch_to(rt_ubase_t to);
|
||||
*
|
||||
* a0 --> to SP pointer
|
||||
* #endif
|
||||
* a0 --> to
|
||||
* a1 --> to_thread
|
||||
*/
|
||||
.globl rt_hw_context_switch_to
|
||||
rt_hw_context_switch_to:
|
||||
LOAD sp, (a0)
|
||||
|
||||
#ifdef RT_USING_SMP
|
||||
/* Pass the previous CPU lock status to rt_cpus_lock_status_restore for restoration */
|
||||
mv a0, a1
|
||||
call rt_cpus_lock_status_restore
|
||||
#endif
|
||||
|
||||
call rt_thread_self
|
||||
mv s1, a0
|
||||
|
||||
#ifndef RT_USING_SMP
|
||||
//if enable RT_USING_SMP, it will finished by rt_cpus_lock_status_restore.
|
||||
#ifdef RT_USING_SMART
|
||||
call lwp_aspace_switch
|
||||
call lwp_aspace_switch
|
||||
#endif
|
||||
#endif
|
||||
|
||||
RESTORE_CONTEXT
|
||||
sret
|
||||
|
||||
/*
|
||||
* #ifdef RT_USING_SMP
|
||||
* void rt_hw_context_switch(rt_ubase_t from, rt_ubase_t to, struct rt_thread *to_thread);
|
||||
* #else
|
||||
* void rt_hw_context_switch(rt_ubase_t from, rt_ubase_t to);
|
||||
* #endif
|
||||
*
|
||||
* a0 --> from SP pointer
|
||||
* a1 --> to SP pointer
|
||||
* a2 --> to_thread
|
||||
*
|
||||
* It should only be used on local interrupt disable
|
||||
*/
|
||||
@@ -103,13 +121,22 @@ rt_hw_context_switch:
|
||||
// restore to thread SP
|
||||
LOAD sp, (a1)
|
||||
|
||||
#ifdef RT_USING_SMP
|
||||
/* Pass the previous CPU lock status to rt_cpus_lock_status_restore for restoration */
|
||||
mv a0, a2
|
||||
call rt_cpus_lock_status_restore
|
||||
#endif /*RT_USING_SMP*/
|
||||
|
||||
// restore Address Space
|
||||
call rt_thread_self
|
||||
mv s1, a0
|
||||
|
||||
#ifndef RT_USING_SMP
|
||||
// if enable RT_USING_SMP, it will finished by rt_cpus_lock_status_restore.
|
||||
#ifdef RT_USING_SMART
|
||||
call lwp_aspace_switch
|
||||
call lwp_aspace_switch
|
||||
#endif
|
||||
#endif
|
||||
|
||||
RESTORE_CONTEXT
|
||||
sret
|
||||
|
||||
@@ -18,16 +18,25 @@
|
||||
#include <sbi.h>
|
||||
#include <encoding.h>
|
||||
|
||||
#ifdef ARCH_MM_MMU
|
||||
#include "mmu.h"
|
||||
#endif
|
||||
|
||||
#ifdef RT_USING_SMP
|
||||
#include "tick.h"
|
||||
#include "interrupt.h"
|
||||
#endif /* RT_USING_SMP */
|
||||
|
||||
#ifdef ARCH_RISCV_FPU
|
||||
#define K_SSTATUS_DEFAULT_BASE (SSTATUS_SPP | SSTATUS_SPIE | SSTATUS_SUM | SSTATUS_FS)
|
||||
#define K_SSTATUS_DEFAULT_BASE (SSTATUS_SPP | SSTATUS_SPIE | SSTATUS_SUM | SSTATUS_FS)
|
||||
#else
|
||||
#define K_SSTATUS_DEFAULT_BASE (SSTATUS_SPP | SSTATUS_SPIE | SSTATUS_SUM)
|
||||
#define K_SSTATUS_DEFAULT_BASE (SSTATUS_SPP | SSTATUS_SPIE | SSTATUS_SUM)
|
||||
#endif
|
||||
|
||||
#ifdef ARCH_RISCV_VECTOR
|
||||
#define K_SSTATUS_DEFAULT (K_SSTATUS_DEFAULT_BASE | SSTATUS_VS)
|
||||
#define K_SSTATUS_DEFAULT (K_SSTATUS_DEFAULT_BASE | SSTATUS_VS)
|
||||
#else
|
||||
#define K_SSTATUS_DEFAULT K_SSTATUS_DEFAULT_BASE
|
||||
#define K_SSTATUS_DEFAULT K_SSTATUS_DEFAULT_BASE
|
||||
#endif
|
||||
#ifdef RT_USING_SMART
|
||||
#include <lwp_arch.h>
|
||||
@@ -49,10 +58,13 @@ volatile rt_ubase_t rt_interrupt_to_thread = 0;
|
||||
*/
|
||||
volatile rt_ubase_t rt_thread_switch_interrupt_flag = 0;
|
||||
|
||||
#ifdef ARCH_MM_MMU
|
||||
static rt_ubase_t *percpu_hartid;
|
||||
#endif
|
||||
|
||||
void *_rt_hw_stack_init(rt_ubase_t *sp, rt_ubase_t ra, rt_ubase_t sstatus)
|
||||
{
|
||||
rt_hw_switch_frame_t frame = (rt_hw_switch_frame_t)
|
||||
((rt_ubase_t)sp - sizeof(struct rt_hw_switch_frame));
|
||||
rt_hw_switch_frame_t frame = (rt_hw_switch_frame_t)((rt_ubase_t)sp - sizeof(struct rt_hw_switch_frame));
|
||||
|
||||
rt_memset(frame, 0, sizeof(struct rt_hw_switch_frame));
|
||||
|
||||
@@ -64,7 +76,21 @@ void *_rt_hw_stack_init(rt_ubase_t *sp, rt_ubase_t ra, rt_ubase_t sstatus)
|
||||
|
||||
int rt_hw_cpu_id(void)
|
||||
{
|
||||
#ifndef RT_USING_SMP
|
||||
return 0;
|
||||
#else
|
||||
if (rt_kmem_pvoff() != 0)
|
||||
{
|
||||
return *percpu_hartid;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if not enable MMU or pvoff==0, read hartid from satp register
|
||||
rt_ubase_t hartid;
|
||||
asm volatile("csrr %0, satp" : "=r"(hartid));
|
||||
return hartid & 0xFFFF; // Assuming hartid fits in lower 16 bits
|
||||
}
|
||||
#endif /* RT_USING_SMP */
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,6 +143,18 @@ void rt_hw_context_switch_interrupt(rt_ubase_t from, rt_ubase_t to, rt_thread_t
|
||||
|
||||
return;
|
||||
}
|
||||
#else
|
||||
void rt_hw_context_switch_interrupt(void *context, rt_ubase_t from, rt_ubase_t to, struct rt_thread *to_thread)
|
||||
{
|
||||
/* Perform architecture-specific context switch. This call will
|
||||
* restore the target thread context and should not return when a
|
||||
* switch is performed. The caller (scheduler) invoked this function
|
||||
* in a context where local IRQs are disabled. */
|
||||
rt_uint32_t level;
|
||||
level = rt_hw_local_irq_disable();
|
||||
rt_hw_context_switch((rt_ubase_t)from, (rt_ubase_t)to, to_thread);
|
||||
rt_hw_local_irq_enable(level);
|
||||
}
|
||||
#endif /* end of RT_USING_SMP */
|
||||
|
||||
/** shutdown CPU */
|
||||
@@ -137,3 +175,80 @@ void rt_hw_set_process_id(int pid)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
#ifdef RT_USING_SMP
|
||||
extern void _start(void);
|
||||
extern int boot_hartid;
|
||||
/* Boot secondary harts using the SBI HSM hart_start call. */
|
||||
void rt_hw_secondary_cpu_up(void)
|
||||
{
|
||||
rt_uint64_t entry_pa;
|
||||
int hart, ret;
|
||||
|
||||
/* translate kernel virtual _start to physical address. */
|
||||
#ifdef ARCH_MM_MMU
|
||||
if (rt_kmem_pvoff() != 0)
|
||||
{
|
||||
entry_pa = (rt_uint64_t)rt_kmem_v2p(&_start);
|
||||
}
|
||||
else
|
||||
{
|
||||
entry_pa = (rt_uint64_t)&_start;
|
||||
}
|
||||
#else
|
||||
entry_pa = (rt_uint64_t)&_start;
|
||||
#endif /* ARCH_MM_MMU */
|
||||
/* Assumes hart IDs are in range [0, RT_CPUS_NR) */
|
||||
RT_ASSERT(boot_hartid < RT_CPUS_NR);
|
||||
for (hart = 0; hart < RT_CPUS_NR; hart++)
|
||||
{
|
||||
if (hart == boot_hartid)
|
||||
continue;
|
||||
|
||||
ret = sbi_hsm_hart_start((unsigned long)hart,
|
||||
(unsigned long)entry_pa,
|
||||
0UL);
|
||||
if (ret)
|
||||
{
|
||||
rt_kprintf("sbi_hsm_hart_start failed for hart %d: %d\n", hart, ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ARCH_MM_MMU
|
||||
void rt_hw_percpu_hartid_init(rt_ubase_t *percpu_ptr, rt_ubase_t hartid)
|
||||
{
|
||||
RT_ASSERT(hartid < RT_CPUS_NR);
|
||||
rt_ubase_t *percpu_hartid_paddr;
|
||||
rt_size_t percpu_size = (rt_size_t)((rt_ubase_t)&__percpu_end - (rt_ubase_t)&__percpu_start);
|
||||
|
||||
percpu_hartid = percpu_ptr;
|
||||
|
||||
// from virtual address to physical address
|
||||
percpu_ptr = (rt_ubase_t *)((rt_ubase_t)percpu_ptr + (rt_ubase_t)rt_kmem_pvoff());
|
||||
percpu_hartid_paddr = percpu_ptr;
|
||||
|
||||
/* Save to the real area */
|
||||
*(rt_ubase_t *)((void *)percpu_hartid_paddr + hartid * percpu_size) = hartid;
|
||||
}
|
||||
#endif /* ARCH_MM_MMU */
|
||||
|
||||
void secondary_cpu_entry(void)
|
||||
{
|
||||
|
||||
#ifdef RT_USING_SMART
|
||||
/* switch to kernel address space */
|
||||
rt_hw_aspace_switch(&rt_kernel_space);
|
||||
#endif
|
||||
/* The PLIC peripheral interrupts are currently handled by the boot_hart. */
|
||||
/* Enable the Supervisor-Timer bit in SIE */
|
||||
rt_hw_tick_init();
|
||||
|
||||
/* ipi init */
|
||||
rt_hw_ipi_init();
|
||||
|
||||
rt_hw_spin_lock(&_cpus_lock);
|
||||
/* invoke system scheduler start for secondary CPU */
|
||||
rt_system_scheduler_start();
|
||||
}
|
||||
#endif /* RT_USING_SMP */
|
||||
|
||||
@@ -43,6 +43,10 @@ rt_inline void rt_hw_isb(void)
|
||||
__asm__ volatile(OPC_FENCE_I:::"memory");
|
||||
}
|
||||
|
||||
#ifdef ARCH_MM_MMU
|
||||
void rt_hw_percpu_hartid_init(rt_ubase_t *percpu_ptr, rt_ubase_t hartid);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -176,6 +176,8 @@
|
||||
#define PTE_A 0x040 // Accessed
|
||||
#define PTE_D 0x080 // Dirty
|
||||
#define PTE_SOFT 0x300 // Reserved for Software
|
||||
#define PTE_ATTR_RW (PTE_R | PTE_W)
|
||||
#define PTE_ATTR_RWX (PTE_ATTR_RW | PTE_X)
|
||||
|
||||
#define PTE_PPN_SHIFT 10
|
||||
|
||||
|
||||
@@ -60,10 +60,17 @@ _handle_interrupt_and_exception:
|
||||
call handle_trap
|
||||
|
||||
_interrupt_exit:
|
||||
#ifndef RT_USING_SMP
|
||||
la s0, rt_thread_switch_interrupt_flag
|
||||
lw s2, 0(s0)
|
||||
beqz s2, _resume_execution
|
||||
sw zero, 0(s0)
|
||||
#else
|
||||
mv a0, sp
|
||||
call rt_scheduler_do_irq_switch
|
||||
// if failed, jump to __resume_execution
|
||||
j _resume_execution
|
||||
#endif /* RT_USING_SMP */
|
||||
|
||||
_context_switch:
|
||||
la t0, rt_interrupt_from_thread
|
||||
@@ -88,6 +95,7 @@ _resume_kernel:
|
||||
csrw sscratch, zero
|
||||
sret
|
||||
|
||||
#ifndef RT_USING_SMP
|
||||
.global rt_hw_interrupt_enable
|
||||
rt_hw_interrupt_enable:
|
||||
csrs sstatus, a0 /* restore to old csr */
|
||||
@@ -97,3 +105,18 @@ rt_hw_interrupt_enable:
|
||||
rt_hw_interrupt_disable:
|
||||
csrrci a0, sstatus, 2 /* clear SIE */
|
||||
jr ra
|
||||
#else
|
||||
.global rt_hw_local_irq_disable
|
||||
rt_hw_local_irq_disable:
|
||||
csrrci a0, sstatus, 2
|
||||
jr ra
|
||||
|
||||
.global rt_hw_local_irq_enable
|
||||
rt_hw_local_irq_enable:
|
||||
csrs sstatus, a0
|
||||
jr ra
|
||||
|
||||
.global rt_hw_secondary_cpu_idle_exec
|
||||
rt_hw_secondary_cpu_idle_exec:
|
||||
jr ra
|
||||
#endif /* RT_USING_SMP */
|
||||
+300
-64
File diff suppressed because it is too large
Load Diff
@@ -56,6 +56,14 @@ struct mem_desc
|
||||
#define MMU_MAP_ERROR_NOPAGE -3
|
||||
#define MMU_MAP_ERROR_CONFLICT -4
|
||||
|
||||
#define VPN_MASK 0x1ffUL
|
||||
#define PTE_BITS 10
|
||||
#define VPN_BITS 9
|
||||
|
||||
#if defined(RT_USING_SMP) && defined(ARCH_MM_MMU)
|
||||
extern unsigned int __percpu_end, __percpu_start;
|
||||
#endif /* RT_USING_SMP && ARCH_MM_MMU */
|
||||
|
||||
void *rt_hw_mmu_tbl_get(void);
|
||||
int rt_hw_mmu_map_init(rt_aspace_t aspace, void *v_address, rt_ubase_t size,
|
||||
rt_ubase_t *vtable, rt_ubase_t pv_off);
|
||||
@@ -72,4 +80,5 @@ int rt_hw_mmu_control(struct rt_aspace *aspace, void *vaddr, size_t size,
|
||||
void *rt_hw_mmu_pgtbl_create(void);
|
||||
void rt_hw_mmu_pgtbl_delete(void *pgtbl);
|
||||
|
||||
unsigned long get_free_page(void);
|
||||
#endif
|
||||
|
||||
@@ -32,9 +32,30 @@ _start:
|
||||
1:
|
||||
/* save hartid */
|
||||
la t0, boot_hartid /* global varible rt_boot_hartid */
|
||||
#ifdef RT_USING_SMP
|
||||
lw t2, (t0)
|
||||
li t3, 0xdeadbeef /* Sentinel value indicating uninitialized boot_hartid */
|
||||
li t4, 0xffffffff
|
||||
and t2, t2, t4 /* Extract the lower 32 bits. */
|
||||
bne t2, t3, system_init /* If the current value is 0xdeadbeef, skip the boot_hartid assignment operation. */
|
||||
#endif
|
||||
mv t1, a0 /* get hartid in S-mode frome a0 register */
|
||||
sw t1, (t0) /* store t1 register low 4 bits in memory address which is stored in t0 */
|
||||
|
||||
#ifdef RT_USING_SMP
|
||||
system_init:
|
||||
#endif
|
||||
|
||||
/*
|
||||
* When ARCH_MM_MMU is not enabled or pvoff==0:
|
||||
* Store hartid temporarily in the satp register because:
|
||||
* 1. satp is not used for address translation when MMU is disabled or pvoff==0.
|
||||
* 2. This value will be moved to percpu_hartid once MMU is initialized.
|
||||
* This approach avoids using extra memory or registers during the critical boot phase,
|
||||
* but developers should be aware that satp is overloaded for this purpose until MMU setup.
|
||||
*/
|
||||
csrw satp, a0
|
||||
|
||||
/* clear Interrupt Registers */
|
||||
csrw sie, 0
|
||||
csrw sip, 0
|
||||
@@ -51,7 +72,10 @@ _start:
|
||||
li x7, 0
|
||||
li x8, 0
|
||||
li x9, 0
|
||||
#ifndef RT_USING_SMP
|
||||
/* In the SMP architecture, a0 will be used again later */
|
||||
li x10,0
|
||||
#endif
|
||||
li x11,0
|
||||
li x12,0
|
||||
li x13,0
|
||||
@@ -85,17 +109,42 @@ _start:
|
||||
la gp, __global_pointer$
|
||||
.option pop
|
||||
|
||||
#ifndef RT_USING_SMP
|
||||
/* removed SMP support here */
|
||||
la sp, __stack_start__
|
||||
li t0, __STACKSIZE__
|
||||
add sp, sp, t0
|
||||
#else
|
||||
/* Initialize the sp pointer according to different hartids. */
|
||||
mv t0, a0
|
||||
/* calculate stack offset: hartid * __STACKSIZE__ */
|
||||
li t1, __STACKSIZE__
|
||||
mul t0, t0, t1 /* t0 = hartid * __STACKSIZE__ */
|
||||
|
||||
/* set stack pointer */
|
||||
la sp, __stack_start__
|
||||
add sp, sp, t0 /* sp = __stack_start__ + hartid * __STACKSIZE__ */
|
||||
add sp, sp, t1 /* sp += __STACKSIZE__ (point to stack top) */
|
||||
|
||||
mv t0, a0
|
||||
lw t1, boot_hartid
|
||||
mv tp, a0
|
||||
bne t0, t1, early_secondary_cpu_entry
|
||||
#endif /* RT_USING_SMP */
|
||||
|
||||
/**
|
||||
* sscratch is always zero on kernel mode
|
||||
*/
|
||||
csrw sscratch, zero
|
||||
call init_bss
|
||||
early_secondary_cpu_entry:
|
||||
#ifdef ARCH_MM_MMU
|
||||
// Manually manage pages in the early stage
|
||||
la a0, .early_page_array
|
||||
call set_free_page
|
||||
|
||||
la a0, .early_tbl_page
|
||||
mv a1, tp
|
||||
call rt_hw_mem_setup_early
|
||||
call rt_kmem_pvoff
|
||||
/* a0 := pvoff */
|
||||
@@ -106,13 +155,37 @@ _start:
|
||||
sub x1, x1, a0
|
||||
ret
|
||||
_after_pc_relocation:
|
||||
#if defined(RT_USING_SMP)
|
||||
/* If the MMU is enabled, save the hartid in percpu_hartid.
|
||||
* -> .percpu_hartid (hartid_0)
|
||||
* ...... align(2MB)
|
||||
* -> (hartid_1)
|
||||
* ......
|
||||
*/
|
||||
la a0, .percpu_hartid
|
||||
mv a1, tp
|
||||
call rt_hw_percpu_hartid_init
|
||||
#endif
|
||||
/* relocate gp */
|
||||
sub gp, gp, a0
|
||||
|
||||
#ifndef RT_USING_SMP
|
||||
/* relocate context: sp */
|
||||
la sp, __stack_start__
|
||||
li t0, __STACKSIZE__
|
||||
add sp, sp, t0
|
||||
#else
|
||||
/* Initialize the sp pointer according to different hartids. */
|
||||
mv t0, tp
|
||||
/* calculate stack offset: hartid * __STACKSIZE__ */
|
||||
li t1, __STACKSIZE__
|
||||
mul t0, t0, t1 /* t0 = hartid * __STACKSIZE__ */
|
||||
|
||||
/* set stack pointer */
|
||||
la sp, __stack_start__
|
||||
add sp, sp, t0 /* sp = __stack_start__ + hartid * __STACKSIZE__ */
|
||||
add sp, sp, t1 /* sp += __STACKSIZE__ (point to stack top) */
|
||||
#endif /* RT_USING_SMP */
|
||||
|
||||
/* reset s0-fp */
|
||||
mv s0, zero
|
||||
@@ -121,7 +194,12 @@ _after_pc_relocation:
|
||||
la t0, trap_entry
|
||||
csrw stvec, t0
|
||||
1:
|
||||
#ifdef RT_USING_SMP
|
||||
mv t0, tp
|
||||
lw t1, boot_hartid
|
||||
bne t0, t1, secondary_cpu_entry
|
||||
#endif
|
||||
#endif /* ARCH_MM_MMU */
|
||||
call sbi_init
|
||||
call primary_cpu_entry
|
||||
|
||||
@@ -131,3 +209,31 @@ _never_return_here:
|
||||
.global _start_link_addr
|
||||
_start_link_addr:
|
||||
.dword __text_start
|
||||
|
||||
#ifdef ARCH_MM_MMU
|
||||
#ifdef RT_USING_SMP
|
||||
/*
|
||||
* CPU stack builtin
|
||||
*/
|
||||
.section ".percpu"
|
||||
.percpu_hartid:
|
||||
.space 16
|
||||
#endif
|
||||
|
||||
.section ".bss"
|
||||
|
||||
.equ page_size, 4096
|
||||
.balign page_size
|
||||
.early_tbl_page:
|
||||
.space 1 * page_size
|
||||
#if defined(RT_USING_SMP) && RT_CPUS_NR > 1
|
||||
.space (RT_CPUS_NR - 1) * page_size
|
||||
#endif
|
||||
|
||||
.early_page_array:
|
||||
.space (8 + 8) * page_size
|
||||
#ifdef RT_USING_SMP
|
||||
.space RT_CPUS_NR * 5 * page_size
|
||||
#endif
|
||||
|
||||
#endif /* ARCH_MM_MMU */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,6 +17,12 @@
|
||||
|
||||
struct rt_irq_desc irq_desc[MAX_HANDLERS];
|
||||
|
||||
#ifdef RT_USING_SMP
|
||||
#include "sbi.h"
|
||||
struct rt_irq_desc ipi_desc[RT_MAX_IPI];
|
||||
uint8_t ipi_vectors[RT_CPUS_NR] = { 0 };
|
||||
#endif /* RT_USING_SMP */
|
||||
|
||||
static rt_isr_handler_t rt_hw_interrupt_handle(rt_uint32_t vector, void *param)
|
||||
{
|
||||
rt_kprintf("UN-handled interrupt %d occurred!!!\n", vector);
|
||||
@@ -53,11 +59,11 @@ void rt_hw_interrupt_umask(int vector)
|
||||
* @param old_handler the old interrupt service routine
|
||||
*/
|
||||
rt_isr_handler_t rt_hw_interrupt_install(int vector, rt_isr_handler_t handler,
|
||||
void *param, const char *name)
|
||||
void *param, const char *name)
|
||||
{
|
||||
rt_isr_handler_t old_handler = RT_NULL;
|
||||
|
||||
if(vector < MAX_HANDLERS)
|
||||
if (vector < MAX_HANDLERS)
|
||||
{
|
||||
old_handler = irq_desc[vector].handler;
|
||||
if (handler != RT_NULL)
|
||||
@@ -92,3 +98,141 @@ void rt_hw_interrupt_init()
|
||||
|
||||
plic_set_threshold(0);
|
||||
}
|
||||
|
||||
#ifdef RT_USING_SMP
|
||||
void rt_hw_interrupt_set_priority(int vector, unsigned int priority)
|
||||
{
|
||||
plic_set_priority(vector, priority);
|
||||
}
|
||||
|
||||
unsigned int rt_hw_interrupt_get_priority(int vector)
|
||||
{
|
||||
return (*(uint32_t *)PLIC_PRIORITY(vector));
|
||||
}
|
||||
|
||||
rt_bool_t rt_hw_interrupt_is_disabled(void)
|
||||
{
|
||||
/* Determine the interrupt enable state */
|
||||
rt_ubase_t sstatus;
|
||||
__asm__ volatile("csrr %0, sstatus" : "=r"(sstatus));
|
||||
return (sstatus & SSTATUS_SIE) == 0;
|
||||
}
|
||||
|
||||
void rt_hw_spin_lock_init(rt_hw_spinlock_t *_lock)
|
||||
{
|
||||
_lock->slock = 0;
|
||||
}
|
||||
|
||||
void rt_hw_spin_lock(rt_hw_spinlock_t *lock)
|
||||
{
|
||||
/* Use ticket lock implemented on top of the 32/64-bit atomic AMO ops.
|
||||
* The combined word layout (slock) maps two uint16_t fields:
|
||||
* low 16 bits: owner
|
||||
* high 16 bits: next (ticket allocator)
|
||||
* We atomically increment the "next" field by (1 << 16) and use the
|
||||
* returned old value to compute our ticket. Then wait until owner == ticket.
|
||||
*/
|
||||
rt_atomic_t prev;
|
||||
rt_atomic_t ticket;
|
||||
rt_atomic_t owner;
|
||||
|
||||
/* Allocate a ticket by adding (1 << 16) to slock, prev holds previous value */
|
||||
prev = rt_hw_atomic_add((volatile rt_atomic_t *)&lock->slock, (rt_atomic_t)(1UL << 16));
|
||||
ticket = (prev >> 16) & 0xffffUL;
|
||||
|
||||
/* Wait until owner equals our ticket */
|
||||
for (;;)
|
||||
{
|
||||
owner = rt_hw_atomic_load((volatile rt_atomic_t *)&lock->slock) & 0xffffUL;
|
||||
if (owner == ticket)
|
||||
break;
|
||||
/* TODO: low-power wait for interrupt while spinning */
|
||||
}
|
||||
|
||||
/* Ensure all following memory accesses are ordered after acquiring the lock */
|
||||
__asm__ volatile("fence rw, rw" ::: "memory");
|
||||
}
|
||||
|
||||
void rt_hw_spin_unlock(rt_hw_spinlock_t *lock)
|
||||
{
|
||||
/* Ensure memory operations before unlock are visible before owner increment */
|
||||
__asm__ volatile("fence rw, rw" ::: "memory");
|
||||
|
||||
/* Increment owner (low 16 bits) to hand over lock to next ticket.
|
||||
* Use an atomic load of the combined slock word and compare the low
|
||||
* 16-bit owner field.If owner would overflow (0xffff), clear the owner field
|
||||
* atomically by ANDing with 0xffff0000; otherwise increment owner by 1.
|
||||
*/
|
||||
if ((rt_hw_atomic_load((volatile rt_atomic_t *)&lock->slock) & (rt_atomic_t)0xffffUL) == (rt_atomic_t)0xffffUL)
|
||||
{
|
||||
/* Atomic clear owner (low 16 bits) when it overflows. Keep next ticket field. */
|
||||
rt_hw_atomic_and((volatile rt_atomic_t *)&lock->slock, (rt_atomic_t)0xffff0000UL);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_hw_atomic_add((volatile rt_atomic_t *)&lock->slock, (rt_atomic_t)1);
|
||||
}
|
||||
|
||||
// TODO: IPI interrupt to wake up other harts waiting for the lock
|
||||
|
||||
/* Make the increment visible to other harts */
|
||||
__asm__ volatile("fence rw, rw" ::: "memory");
|
||||
}
|
||||
|
||||
void rt_hw_ipi_send(int ipi_vector, unsigned int cpu_mask)
|
||||
{
|
||||
int cpuid = __builtin_ctz(cpu_mask); // get the bit position of the lowest set bit
|
||||
ipi_vectors[cpuid] |= (uint8_t)ipi_vector;
|
||||
sbi_send_ipi((const unsigned long *)&cpu_mask);
|
||||
}
|
||||
|
||||
void rt_hw_ipi_init(void)
|
||||
{
|
||||
int idx = 0, cpuid = rt_cpu_get_id();
|
||||
ipi_vectors[cpuid] = 0;
|
||||
/* init exceptions table */
|
||||
for (idx = 0; idx < RT_MAX_IPI; idx++)
|
||||
{
|
||||
ipi_desc[idx].handler = RT_NULL;
|
||||
ipi_desc[idx].param = RT_NULL;
|
||||
#ifdef RT_USING_INTERRUPT_INFO
|
||||
rt_snprintf(ipi_desc[idx].name, RT_NAME_MAX - 1, "default");
|
||||
ipi_desc[idx].counter = 0;
|
||||
#endif
|
||||
}
|
||||
set_csr(sie, SIP_SSIP);
|
||||
}
|
||||
|
||||
void rt_hw_ipi_handler_install(int ipi_vector, rt_isr_handler_t ipi_isr_handler)
|
||||
{
|
||||
if (ipi_vector < RT_MAX_IPI)
|
||||
{
|
||||
if (ipi_isr_handler != RT_NULL)
|
||||
{
|
||||
ipi_desc[ipi_vector].handler = (rt_isr_handler_t)ipi_isr_handler;
|
||||
ipi_desc[ipi_vector].param = RT_NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rt_hw_ipi_handler(void)
|
||||
{
|
||||
rt_uint32_t ipi_vector;
|
||||
|
||||
ipi_vector = ipi_vectors[rt_cpu_get_id()];
|
||||
while (ipi_vector)
|
||||
{
|
||||
int bitpos = __builtin_ctz(ipi_vector);
|
||||
ipi_vector &= ~(1 << bitpos);
|
||||
if (bitpos < RT_MAX_IPI && ipi_desc[bitpos].handler != RT_NULL)
|
||||
{
|
||||
rt_hw_atomic_and((volatile rt_atomic_t *)&ipi_vectors[rt_cpu_get_id()], ~((rt_atomic_t)(1 << bitpos)));
|
||||
/* call the irq service routine */
|
||||
ipi_desc[bitpos].handler(bitpos, ipi_desc[bitpos].param);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Clear the software interrupt pending bit in CLINT
|
||||
clear_csr(sip, SIP_SSIP);
|
||||
}
|
||||
#endif /* RT_USING_SMP */
|
||||
|
||||
@@ -42,5 +42,12 @@ void rt_hw_interrupt_init(void);
|
||||
void rt_hw_interrupt_mask(int vector);
|
||||
rt_isr_handler_t rt_hw_interrupt_install(int vector, rt_isr_handler_t handler, void *param, const char *name);
|
||||
void handle_trap(rt_ubase_t xcause, rt_ubase_t xtval, rt_ubase_t xepc, struct rt_hw_stack_frame *sp);
|
||||
|
||||
#ifdef RT_USING_SMP
|
||||
void rt_hw_interrupt_set_priority(int vector, unsigned int priority);
|
||||
unsigned int rt_hw_interrupt_get_priority(int vector);
|
||||
void rt_hw_ipi_handler(void);
|
||||
void rt_hw_ipi_handler_install(int ipi_vector, rt_isr_handler_t ipi_isr_handler);
|
||||
void rt_hw_ipi_init(void);
|
||||
void rt_hw_ipi_send(int ipi_vector, unsigned int cpu_mask);
|
||||
#endif /* RT_USING_SMP */
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user