diff --git a/libcpu/risc-v/SConscript b/libcpu/risc-v/SConscript index 851542e1a3d..273c997e8eb 100644 --- a/libcpu/risc-v/SConscript +++ b/libcpu/risc-v/SConscript @@ -5,7 +5,7 @@ from building import * Import('rtconfig') -common64_arch = ['virt64', 'c906'] +common64_arch = ['virt64', 'c906', 'c908'] cwd = GetCurrentDir() group = [] list = os.listdir(cwd) diff --git a/libcpu/risc-v/t-head/c908/SConscript b/libcpu/risc-v/t-head/c908/SConscript new file mode 100644 index 00000000000..5f750ad65f4 --- /dev/null +++ b/libcpu/risc-v/t-head/c908/SConscript @@ -0,0 +1,13 @@ +# RT-Thread building script for component + +from building import * +cwd = GetCurrentDir() +src = Glob('*.c') + Glob('*.cpp') + Glob('*_gcc.S') +CPPPATH = [cwd] + +if not GetDepend('ARCH_USING_ASID'): + SrcRemove(src, ['asid.c']) + +group = DefineGroup('libcpu', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/libcpu/risc-v/t-head/c908/asm/sbiasm.h b/libcpu/risc-v/t-head/c908/asm/sbiasm.h new file mode 100644 index 00000000000..4639fba68cf --- /dev/null +++ b/libcpu/risc-v/t-head/c908/asm/sbiasm.h @@ -0,0 +1,10 @@ +#ifndef _SBI_ASM_H +#define _SBI_ASM_H + +.macro SBI_CALL which + li a7, \which + ecall + nop +.endm + +#endif /* _SBI_ASM_H */ diff --git a/libcpu/risc-v/t-head/c908/asm/sbidef.h b/libcpu/risc-v/t-head/c908/asm/sbidef.h new file mode 100644 index 00000000000..5bcf58ade7c --- /dev/null +++ b/libcpu/risc-v/t-head/c908/asm/sbidef.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2019-2020, Xim + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +#ifndef _ASM_SBI_DEF_H +#define _ASM_SBI_DEF_H + +#define SBI_SET_TIMER 0 +#define SBI_CONSOLE_PUTCHAR 1 +#define SBI_CONSOLE_GETCHAR 2 +#define SBI_CLEAR_IPI 3 +#define SBI_SEND_IPI 4 +#define SBI_REMOTE_FENCE_I 5 +#define SBI_REMOTE_SFENCE_VMA 6 +#define SBI_REMOTE_SFENCE_VMA_ASID 7 +#define SBI_SHUTDOWN 8 + +#define SBI_CONSOLE_PUTSTR 9 + +#define SBI_SD_WRITE 10 +#define SBI_SD_READ 11 +#define SBI_NET_WRITE 12 +#define SBI_NET_READ 13 + +#endif /* _ASM_SBI_DEF_H */ diff --git a/libcpu/risc-v/t-head/c908/cache.c b/libcpu/risc-v/t-head/c908/cache.c new file mode 100644 index 00000000000..db573fa5f81 --- /dev/null +++ b/libcpu/risc-v/t-head/c908/cache.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-01-29 lizhirui first version + * 2021-11-05 JasonHu add C908 cache inst + * 2022-11-09 WangXiaoyao Support cache coherence operations; + * improve portability and make + * no assumption on undefined behavior + */ + +#include +#include +#include +#include + +#include "opcode.h" +#include "cache.h" + +#define L1_CACHE_BYTES (64) + +/** + * GCC version not support t-head cache flush, so we use fixed code to achieve. + * The following function cannot be optimized. + */ +static void dcache_wb_range(unsigned long start, unsigned long end) __attribute__((optimize("O0"))); +static void dcache_inv_range(unsigned long start, unsigned long end) __attribute__((optimize("O0"))); +static void dcache_wbinv_range(unsigned long start, unsigned long end) __attribute__((optimize("O0"))); +static void icache_inv_range(unsigned long start, unsigned long end) __attribute__((optimize("O0"))); + +#define CACHE_OP_RS1 %0 +#define CACHE_OP_RANGE(instr) \ + { \ + rt_ubase_t i = start & ~(L1_CACHE_BYTES - 1); \ + for (; i < end; i += L1_CACHE_BYTES) \ + { \ + __asm__ volatile(instr ::"r"(i) \ + : "memory"); \ + } \ + } + +static void dcache_wb_range(unsigned long start, unsigned long end) +{ + CACHE_OP_RANGE(OPC_DCACHE_CVA(CACHE_OP_RS1)); +} + +static void dcachel1_wb_range(unsigned long start, unsigned long end) +{ + CACHE_OP_RANGE(OPC_DCACHE_CVAL1(CACHE_OP_RS1)); +} + +static void dcache_inv_range(unsigned long start, unsigned long end) +{ + CACHE_OP_RANGE(OPC_DCACHE_IVA(CACHE_OP_RS1)); +} + +static void dcache_wbinv_range(unsigned long start, unsigned long end) +{ + CACHE_OP_RANGE(OPC_DCACHE_CIVA(CACHE_OP_RS1)); +} + +static void icache_inv_range(unsigned long start, unsigned long end) +{ + CACHE_OP_RANGE(OPC_ICACHE_IVA(CACHE_OP_RS1)); +} + +rt_inline rt_uint32_t rt_cpu_icache_line_size(void) +{ + return L1_CACHE_BYTES; +} + +rt_inline rt_uint32_t rt_cpu_dcache_line_size(void) +{ + return L1_CACHE_BYTES; +} + +void rt_hw_cpu_icache_invalidate_local(void *addr, int size) +{ + icache_inv_range((unsigned long)addr, (unsigned long)((unsigned char *)addr + size)); + rt_hw_cpu_sync_i(); +} + +void rt_hw_cpu_dcache_invalidate_local(void *addr, int size) +{ + dcache_inv_range((unsigned long)addr, (unsigned long)((unsigned char *)addr + size)); + rt_hw_cpu_sync(); +} + +void rt_hw_cpu_dcache_clean_local(void *addr, int size) +{ + dcache_wb_range((unsigned long)addr, (unsigned long)((unsigned char *)addr + size)); + rt_hw_cpu_sync(); +} + +void rt_hw_cpu_dcache_clean_invalidate_local(void *addr, int size) +{ + dcache_wbinv_range((unsigned long)addr, (unsigned long)((unsigned char *)addr + size)); + rt_hw_cpu_sync(); +} + +void rt_hw_cpu_dcachel1_clean_local(void *addr, int size) +{ + __asm__ volatile(OPC_DCACHE_CVAL1(a0):: + : "memory"); +} + +/** + * ===================================================== + * Architecture Independent API + * ===================================================== + */ + +void rt_hw_cpu_icache_ops(int ops, void *addr, int size) +{ + if (ops == RT_HW_CACHE_INVALIDATE) + { + rt_hw_cpu_icache_invalidate(addr, size); + } +} + +void rt_hw_cpu_dcache_ops(int ops, void *addr, int size) +{ + if (ops == RT_HW_CACHE_FLUSH) + { + rt_hw_cpu_dcache_clean(addr, size); + } + else + { + rt_hw_cpu_dcache_invalidate(addr, size); + } +} + +void rt_hw_sync_cache_local(void *addr, int size) +{ + rt_hw_cpu_dcachel1_clean_local(addr, size); + rt_hw_cpu_icache_invalidate_local(addr, size); +} diff --git a/libcpu/risc-v/t-head/c908/cache.h b/libcpu/risc-v/t-head/c908/cache.h new file mode 100644 index 00000000000..09ebce6861f --- /dev/null +++ b/libcpu/risc-v/t-head/c908/cache.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-11-05 JasonHu The first version + * 2022-11-09 WangXiaoyao Distinguish local and broadcast operations + */ + +#ifndef CACHE_H__ +#define CACHE_H__ + +#include "opcode.h" + +#ifndef ALWAYS_INLINE +#define ALWAYS_INLINE inline __attribute__((always_inline)) +#endif + +ALWAYS_INLINE void rt_hw_cpu_sync(void) +{ + asm volatile(OPC_SYNC:: + : "memory"); +} + +ALWAYS_INLINE void rt_hw_cpu_sync_i(void) +{ + asm volatile(OPC_SYNC_I:: + : "memory"); +} + +ALWAYS_INLINE void rt_hw_cpu_sync_s(void) +{ + asm volatile(OPC_SYNC_S:: + : "memory"); +} + +ALWAYS_INLINE void rt_hw_cpu_sync_is(void) +{ + asm volatile(OPC_SYNC_IS:: + : "memory"); +} + +/** + * ======================================== + * Local cpu cache maintainence operations + * ======================================== + */ + +void rt_hw_cpu_dcache_clean_local(void *addr, int size); +void rt_hw_cpu_dcache_invalidate_local(void *addr, int size); +void rt_hw_cpu_dcache_clean_invalidate_local(void *addr, int size); + +void rt_hw_cpu_icache_invalidate_local(void *addr, int size); + +ALWAYS_INLINE void rt_hw_cpu_dcache_clean_all_local(void) +{ + __asm__ volatile(OPC_DCACHE_CALL :: + : "memory"); + rt_hw_cpu_sync(); +} + +ALWAYS_INLINE void rt_hw_cpu_dcache_invalidate_all_local(void) +{ + __asm__ volatile(OPC_DCACHE_IALL :: + : "memory"); + rt_hw_cpu_sync(); +} + +ALWAYS_INLINE void rt_hw_cpu_dcache_clean_invalidate_all_local(void) +{ + __asm__ volatile(OPC_DCACHE_CIALL :: + : "memory"); + rt_hw_cpu_sync(); +} + +ALWAYS_INLINE void rt_hw_cpu_icache_invalidate_all_local(void) +{ + __asm__ volatile(OPC_ICACHE_IALL :: + : "memory"); + rt_hw_cpu_sync_i(); +} + +#define rt_hw_icache_invalidate_all rt_hw_cpu_icache_invalidate_all + +/** + * ======================================== + * Multi-core cache maintainence operations + * ======================================== + */ + +#ifdef RT_USING_SMP +#error "TODO: cache maintainence have not ported to RISC-V SMP yet" + +void rt_hw_cpu_dcache_clean(void *addr, int size); +void rt_hw_cpu_dcache_invalidate(void *addr, int size); +void rt_hw_cpu_dcache_clean_invalidate(void *addr, int size); + +void rt_hw_cpu_dcache_clean_all(void); +void rt_hw_cpu_dcache_invalidate_all(void); +void rt_hw_cpu_dcache_clean_invalidate_all(void); + +void rt_hw_cpu_icache_invalidate(void *addr, int size); +void rt_hw_cpu_icache_invalidate_all(void); + +#else /* !RT_USING_SMP */ + +#define rt_hw_cpu_dcache_clean rt_hw_cpu_dcache_clean_local +#define rt_hw_cpu_dcache_invalidate rt_hw_cpu_dcache_invalidate_local +#define rt_hw_cpu_dcache_clean_and_invalidate rt_hw_cpu_dcache_clean_invalidate_local + +#define rt_hw_cpu_dcache_clean_all rt_hw_cpu_dcache_clean_all_local +#define rt_hw_cpu_dcache_invalidate_all rt_hw_cpu_dcache_invalidate_all_local +#define rt_hw_cpu_dcache_clean_invalidate_all rt_hw_cpu_dcache_clean_invalidate_all_local + +#define rt_hw_cpu_icache_invalidate rt_hw_cpu_icache_invalidate_local +#define rt_hw_cpu_icache_invalidate_all rt_hw_cpu_icache_invalidate_all_local + +#endif /* RT_USING_SMP */ + +/** + * @brief Synchronize cache to Point of Unification + */ +void rt_hw_sync_cache_local(void *addr, int size); + +#endif /* CACHE_H__ */ diff --git a/libcpu/risc-v/t-head/c908/interrupt.c b/libcpu/risc-v/t-head/c908/interrupt.c new file mode 100644 index 00000000000..9e4cf2f1579 --- /dev/null +++ b/libcpu/risc-v/t-head/c908/interrupt.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-10-19 JasonHu first version + */ + +#include +#include + +#include "interrupt.h" +#include "riscv.h" +#include "plic.h" + +extern rt_uint32_t rt_interrupt_nest; +extern rt_uint32_t rt_interrupt_from_thread, rt_interrupt_to_thread; +extern rt_uint32_t rt_thread_switch_interrupt_flag; + +struct rt_irq_desc isr_table[INTERRUPTS_MAX]; + +static void rt_hw_interrupt_handler(int vector, void *param) +{ + rt_kprintf("Unhandled interrupt %d occured!!!\n", vector); +} + +/** + * This function will initialize hardware interrupt + */ +void rt_hw_interrupt_init(void) +{ + /* init interrupt controller */ + plic_init(); + + rt_int32_t idx; + + rt_memset(isr_table, 0x00, sizeof(isr_table)); + for (idx = 0; idx < INTERRUPTS_MAX; idx++) + { + isr_table[idx].handler = rt_hw_interrupt_handler; + } + + /* init interrupt nest, and context in thread sp */ + rt_interrupt_nest = 0; + rt_interrupt_from_thread = 0; + rt_interrupt_to_thread = 0; + rt_thread_switch_interrupt_flag = 0; +} + +/** + * This function will mask a interrupt. + * @param vector the interrupt number + */ +void rt_hw_interrupt_mask(int vector) +{ + if ((vector < 0) || (vector > IRQ_MAX_NR)) + { + return; + } + plic_disable_irq(vector); +} + +/** + + * This function will un-mask a interrupt. + * @param vector the interrupt number + */ +void rt_hw_interrupt_umask(int vector) +{ + if ((vector < 0) || (vector > IRQ_MAX_NR)) + { + return; + } + plic_enable_irq(vector); +} + +/** + * This function will install a interrupt service routine to a interrupt. + * @param vector the interrupt number + * @param handler the interrupt service routine to be installed + * @param param the interrupt service function parameter + * @param name the interrupt name + * @return old handler + */ +rt_isr_handler_t rt_hw_interrupt_install(int vector, rt_isr_handler_t handler, + void *param, const char *name) +{ + rt_isr_handler_t old_handler = RT_NULL; + if ((vector < 0) || (vector > IRQ_MAX_NR)) + { + return old_handler; + } + + old_handler = isr_table[IRQ_OFFSET + vector].handler; + +#ifdef RT_USING_INTERRUPT_INFO + rt_strncpy(isr_table[IRQ_OFFSET + vector].name, name, RT_NAME_MAX); +#endif /* RT_USING_INTERRUPT_INFO */ + isr_table[IRQ_OFFSET + vector].handler = handler; + isr_table[IRQ_OFFSET + vector].param = param; + + return old_handler; +} diff --git a/libcpu/risc-v/t-head/c908/interrupt.h b/libcpu/risc-v/t-head/c908/interrupt.h new file mode 100644 index 00000000000..2ee08248375 --- /dev/null +++ b/libcpu/risc-v/t-head/c908/interrupt.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-10-19 JasonHu first version + */ + +#ifndef __INTERRUPT_H__ +#define __INTERRUPT_H__ + +#include + +#define NR_CPUS 1 + +#define IRQ_OFFSET 16 +#ifndef IRQ_MAX_NR +#define IRQ_MAX_NR 200 +#endif +#define INTERRUPTS_MAX (IRQ_OFFSET + IRQ_MAX_NR) + +enum { + EP_INSTRUCTION_ADDRESS_MISALIGNED = 0, + EP_INSTRUCTION_ACCESS_FAULT, + EP_ILLEGAL_INSTRUCTION, + EP_BREAKPOINT, + EP_LOAD_ADDRESS_MISALIGNED, + EP_LOAD_ACCESS_FAULT, + EP_STORE_ADDRESS_MISALIGNED, + EP_STORE_ACCESS_FAULT, + EP_ENVIRONMENT_CALL_U_MODE, + EP_ENVIRONMENT_CALL_S_MODE, + EP_RESERVED10, + EP_ENVIRONMENT_CALL_M_MODE, + EP_INSTRUCTION_PAGE_FAULT, /* page attr */ + EP_LOAD_PAGE_FAULT, /* read data */ + EP_RESERVED14, + EP_STORE_PAGE_FAULT, /* write data */ +}; + +void rt_hw_interrupt_init(void); +void rt_hw_interrupt_mask(int vector); +void rt_hw_interrupt_umask(int vector); +rt_isr_handler_t rt_hw_interrupt_install(int vector, rt_isr_handler_t handler, void *param, const char *name); + +#endif diff --git a/libcpu/risc-v/t-head/c908/opcode.h b/libcpu/risc-v/t-head/c908/opcode.h new file mode 100644 index 00000000000..90b002dc4eb --- /dev/null +++ b/libcpu/risc-v/t-head/c908/opcode.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-09 WangXiaoyao Add portable asm support + * 2022-03-16 WangXiaoyao Porting to xtheadsync & xtheadcmo ISA extension + */ +#ifndef __OPCODE_H__ +#define __OPCODE_H__ + +/** + * @brief binary opcode pseudo operations + * Used to bypass toolchain restriction on extension ISA + * + * WARNING: Xuantie ISAs are not compatible to each other in opcode. + * It's painful to port this file, and should be really careful. + */ + +/** + * @brief RISC-V instruction formats + */ + +/** + * R type: .insn r opcode6, func3, func7, rd, rs1, rs2 + * + * +-------+-----+-----+-------+----+---------+ + * | func7 | rs2 | rs1 | func3 | rd | opcode6 | + * +-------+-----+-----+-------+----+---------+ + * 31 25 20 15 12 7 0 + */ +#define __OPC_INSN_FORMAT_R(opcode, func3, func7, rd, rs1, rs2) \ + ".insn r "RT_STRINGIFY(opcode)","RT_STRINGIFY(func3)","RT_STRINGIFY(func7)","RT_STRINGIFY(rd)","RT_STRINGIFY(rs1)","RT_STRINGIFY(rs2) + +/** + * @brief Xuantie T-HEAD extension ISA format + * Compatible to Xuantie C908 user manual v03 + */ +#define __OPC_INSN_FORMAT_CACHE(func7, rs2, rs1) \ + __OPC_INSN_FORMAT_R(0x0b, 0x0, func7, x0, rs1, rs2) + +#ifdef _TOOLCHAIN_SUPP_XTHEADE_ISA_ +#define OPC_SYNC "sync" +#define OPC_SYNC_S "sync.s" +#define OPC_SYNC_I "sync.i" +#define OPC_SYNC_IS "sync.is" + +#define OPC_DCACHE_CALL "dcache.call" +#define OPC_DCACHE_IALL "dcache.iall" +#define OPC_DCACHE_CIALL "dcache.ciall" +#define OPC_DCACHE_CVAL1(rs1) "dcache.cval1 "_TOSTR(rs1) + +#define OPC_ICACHE_IALL "icache.iall" + +#define OPC_DCACHE_CVA(rs1) "dcache.cva "RT_STRINGIFY(rs1) +#define OPC_DCACHE_IVA(rs1) "dcache.iva "RT_STRINGIFY(rs1) +#define OPC_DCACHE_CIVA(rs1) "dcache.civa "RT_STRINGIFY(rs1) + +#define OPC_ICACHE_IVA(rs1) "icache.iva "RT_STRINGIFY(rs1) +#else /* !_TOOLCHAIN_NOT_SUPP_THEAD_ISA_ */ + +#define OPC_SYNC ".long 0x0180000B" +#define OPC_SYNC_S ".long 0x0190000B" +#define OPC_SYNC_I ".long 0x01A0000B" +#define OPC_SYNC_IS ".long 0x01B0000B" + +#define OPC_DCACHE_CALL ".long 0x0010000B" +#define OPC_DCACHE_IALL ".long 0x0020000B" +#define OPC_DCACHE_CIALL ".long 0x0030000B" +#define OPC_DCACHE_CVAL1(rs1) __OPC_INSN_FORMAT_CACHE(0x1, x4, rs1) + +#define OPC_ICACHE_IALL ".long 0x0100000B" + +#define OPC_DCACHE_CVA(rs1) __OPC_INSN_FORMAT_CACHE(0x1, x5, rs1) +#define OPC_DCACHE_IVA(rs1) __OPC_INSN_FORMAT_CACHE(0x1, x6, rs1) +#define OPC_DCACHE_CIVA(rs1) __OPC_INSN_FORMAT_CACHE(0x1, x7, rs1) + +#define OPC_ICACHE_IVA(rs1) __OPC_INSN_FORMAT_CACHE(0x1, x16, rs1) +#endif /* _TOOLCHAIN_NOT_SUPP_THEAD_ISA_ */ + +/** + * @brief RISC-V zifencei ISA + */ +#ifdef _TOOLCHAIN_SUPP_ZIFENCEI_ISA_ +#define OPC_FENCE_I "fence.i" +#else /* !_TOOLCHAIN_SUPP_ZIFENCEI_ISA_ */ +#define OPC_FENCE_I ".long 0x0000100F" +#endif /* _TOOLCHAIN_SUPP_ZIFENCEI_ISA_ */ + +#endif /* __OPCODE_H__ */ diff --git a/libcpu/risc-v/t-head/c908/plic.c b/libcpu/risc-v/t-head/c908/plic.c new file mode 100644 index 00000000000..08cb6c18d2f --- /dev/null +++ b/libcpu/risc-v/t-head/c908/plic.c @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-10-19 JasonHu first version + * 2021-11-12 JasonHu fix bug that not intr on f133 + * 2023-04-22 flyingcys add plic register ioremap + */ + +#include + +#include + +#include "plic.h" +#include "interrupt.h" +#include "io.h" +#include "encoding.h" +#include "ioremap.h" + +static void *plic_regs = RT_NULL; +extern struct rt_irq_desc isr_table[]; + +struct plic_handler +{ + rt_bool_t present; + void *hart_base; + void *enable_base; +}; + +rt_inline void plic_toggle(struct plic_handler *handler, int hwirq, int enable); +struct plic_handler plic_handlers[C908_NR_CPUS]; +static void *plic_irq_priority[INTERRUPTS_MAX] = {RT_NULL}; + +rt_inline void plic_irq_toggle(int hwirq, int enable) +{ + int cpu = 0; + void *priority_addr; + + /* set priority of interrupt, interrupt 0 is zero. */ + priority_addr = (void *)((rt_size_t)plic_regs + PRIORITY_BASE + hwirq * PRIORITY_PER_ID); +#ifdef RT_USING_SMART + if (plic_irq_priority[hwirq] == RT_NULL) + { + plic_irq_priority[hwirq] = (void *)rt_ioremap(priority_addr, 0x1000); + } + priority_addr = plic_irq_priority[hwirq]; +#endif + writel(enable, priority_addr); + struct plic_handler *handler = &plic_handlers[cpu]; + + if (handler->present) + { + plic_toggle(handler, hwirq, enable); + } +} + +static void generic_handle_irq(int irq) +{ + rt_isr_handler_t isr; + void *param; + + if (irq < 0 || irq >= IRQ_MAX_NR) + { + LOG_E("bad irq number %d!\n", irq); + return; + } + + if (!irq) // irq = 0 => no irq + { + LOG_W("no irq!\n"); + return; + } + isr = isr_table[IRQ_OFFSET + irq].handler; + param = isr_table[IRQ_OFFSET + irq].param; + if (isr != RT_NULL) + { + isr(irq, param); + } + /* complete irq. */ + plic_complete(irq); +} + +void plic_complete(int irqno) +{ + int cpu = 0; + struct plic_handler *handler = &plic_handlers[cpu]; + + writel(irqno, (void *)((rt_size_t)handler->hart_base + CONTEXT_CLAIM)); +} + +void plic_disable_irq(int irqno) +{ + plic_irq_toggle(irqno, 0); +} + +void plic_enable_irq(int irqno) +{ + plic_irq_toggle(irqno, 1); +} + +/* + * Handling an interrupt is a two-step process: first you claim the interrupt + * by reading the claim register, then you complete the interrupt by writing + * that source ID back to the same claim register. This automatically enables + * and disables the interrupt, so there's nothing else to do. + */ +void plic_handle_irq(void) +{ + int cpu = 0; + unsigned int irq; + + struct plic_handler *handler = &plic_handlers[cpu]; + void *claim = (void *)((rt_size_t)handler->hart_base + CONTEXT_CLAIM); + + if (plic_regs == RT_NULL || !handler->present) + { + LOG_E("plic state not initialized."); + return; + } + + clear_csr(sie, SIE_SEIE); + + while ((irq = readl(claim))) + { + /* ID0 is diabled permantually from spec. */ + if (irq == 0) + { + LOG_E("irq no is zero."); + } + else + { + generic_handle_irq(irq); + } + } + set_csr(sie, SIE_SEIE); +} + +rt_inline void plic_toggle(struct plic_handler *handler, int hwirq, int enable) +{ + uint32_t *reg = (uint32_t *)((rt_size_t)handler->enable_base + (hwirq / 32) * sizeof(uint32_t)); + uint32_t hwirq_mask = 1 << (hwirq % 32); + + if (enable) + { + writel(readl(reg) | hwirq_mask, reg); + } + else + { + writel(readl(reg) & ~hwirq_mask, reg); + } +} + +void plic_init(void) +{ + int nr_irqs; + int nr_context; + int i; + unsigned long hwirq; + int cpu = 0; + + if (plic_regs) + { + LOG_E("plic already initialized!"); + return; + } + + nr_context = C908_NR_CONTEXT; + + plic_regs = (void *)C908_PLIC_PHY_ADDR; + if (!plic_regs) + { + LOG_E("fatal error, plic is reg space is null."); + return; + } + + nr_irqs = C908_PLIC_NR_EXT_IRQS; + + for (i = 0; i < nr_context; i ++) + { + struct plic_handler *handler; + uint32_t threshold = 0; + + cpu = 0; + + /* skip contexts other than supervisor external interrupt */ + if (i == 0) + { + continue; + } + + // we always use CPU0 M-mode target register. + handler = &plic_handlers[cpu]; + if (handler->present) + { + threshold = 0xffffffff; + goto done; + } + + handler->present = RT_TRUE; + handler->hart_base = (void *)((rt_size_t)plic_regs + CONTEXT_BASE + i * CONTEXT_PER_HART); + handler->enable_base = (void *)((rt_size_t)plic_regs + ENABLE_BASE + i * ENABLE_PER_HART); +#ifdef RT_USING_SMART + handler->hart_base = (void *)rt_ioremap(handler->hart_base, 0x1000); + handler->enable_base = (void *)rt_ioremap(handler->enable_base, 0x1000); +#endif +done: + /* priority must be > threshold to trigger an interrupt */ + writel(threshold, (void *)((rt_size_t)handler->hart_base + CONTEXT_THRESHOLD)); + for (hwirq = 1; hwirq <= nr_irqs; hwirq++) + { + plic_toggle(handler, hwirq, 0); + } + } + + /* Enable supervisor external interrupts. */ + set_csr(sie, SIE_SEIE); +} diff --git a/libcpu/risc-v/t-head/c908/plic.h b/libcpu/risc-v/t-head/c908/plic.h new file mode 100644 index 00000000000..94f00eacdb8 --- /dev/null +++ b/libcpu/risc-v/t-head/c908/plic.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-10-19 JasonHu first version + * 2023-04-22 flyingcys add C906_PLIC_PHY_ADDR macro judge + */ + +#ifndef __RISCV64_PLIC_H__ +#define __RISCV64_PLIC_H__ + +#include + +#ifndef C908_PLIC_PHY_ADDR +#define C908_PLIC_PHY_ADDR (0xF00000000UL) +#endif +#define C908_PLIC_NR_EXT_IRQS (IRQ_MAX_NR) +#define C908_NR_CPUS (NR_CPUS) + +/* M and S mode context. */ +#define C908_NR_CONTEXT (2) + +#define MAX_DEVICES 1024 +#define MAX_CONTEXTS 15872 + +/* + * Each interrupt source has a priority register associated with it. + * We always hardwire it to one in Linux. + */ +#define PRIORITY_BASE 0 +#define PRIORITY_PER_ID 4 + +/* + * Each hart context has a vector of interrupt enable bits associated with it. + * There's one bit for each interrupt source. + */ +#define ENABLE_BASE 0x2000 +#define ENABLE_PER_HART 0x80 + +/* + * Each hart context has a set of control registers associated with it. Right + * now there's only two: a source priority threshold over which the hart will + * take an interrupt, and a register to claim interrupts. + */ +#define CONTEXT_BASE 0x200000 +#define CONTEXT_PER_HART 0x1000 +#define CONTEXT_THRESHOLD 0x00 +#define CONTEXT_CLAIM 0x04 + +void plic_init(void); +void plic_enable_irq(int irqno); +void plic_disable_irq(int irqno); +// tell PLIC that we've served this IRQ +void plic_complete(int irq); +void plic_handle_irq(void); + +#endif diff --git a/libcpu/risc-v/t-head/c908/riscv_mmu.h b/libcpu/risc-v/t-head/c908/riscv_mmu.h new file mode 100644 index 00000000000..8fbe241f657 --- /dev/null +++ b/libcpu/risc-v/t-head/c908/riscv_mmu.h @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-01-30 lizhirui first version + * 2021-05-03 lizhirui porting to C906 + * 2023-10-12 Shell Add permission control API + */ + +#ifndef __RISCV_MMU_H__ +#define __RISCV_MMU_H__ + +#include +#include +#include "riscv.h" + +#undef PAGE_SIZE + +/* C-SKY extend */ +#define PTE_SEC (1UL << 59) /* Security */ +#define PTE_SHARE (1UL << 60) /* Shareable */ +#define PTE_BUF (1UL << 61) /* Bufferable */ +#define PTE_CACHE (1UL << 62) /* Cacheable */ +#define PTE_SO (1UL << 63) /* Strong Order */ + +#define PAGE_OFFSET_SHIFT 0 +#define PAGE_OFFSET_BIT 12 +#define PAGE_SIZE __SIZE(PAGE_OFFSET_BIT) +#define PAGE_OFFSET_MASK __MASK(PAGE_OFFSET_BIT) +#define VPN0_SHIFT (PAGE_OFFSET_SHIFT + PAGE_OFFSET_BIT) +#define VPN0_BIT 9 +#define VPN1_SHIFT (VPN0_SHIFT + VPN0_BIT) +#define VPN1_BIT 9 +#define VPN2_SHIFT (VPN1_SHIFT + VPN1_BIT) +#define VPN2_BIT 9 + +#define PPN0_SHIFT (PAGE_OFFSET_SHIFT + PAGE_OFFSET_BIT) +#define PPN0_BIT 9 +#define PPN1_SHIFT (PPN0_SHIFT + PPN0_BIT) +#define PPN1_BIT 9 +#define PPN2_SHIFT (PPN1_SHIFT + PPN1_BIT) +#define PPN2_BIT 26 + +#define L1_PAGE_SIZE __SIZE(PAGE_OFFSET_BIT + VPN0_BIT + VPN1_BIT) +#define L2_PAGE_SIZE __SIZE(PAGE_OFFSET_BIT + VPN0_BIT) +#define L3_PAGE_SIZE __SIZE(PAGE_OFFSET_BIT) + +#define ARCH_ADDRESS_WIDTH_BITS 64 + +#define PHYSICAL_ADDRESS_WIDTH_BITS 56 + +#define PAGE_ATTR_NEXT_LEVEL (0) +#define PAGE_ATTR_RWX (PTE_X | PTE_W | PTE_R) +#define PAGE_ATTR_READONLY (PTE_R) +#define PAGE_ATTR_READEXECUTE (PTE_X | PTE_R) + +#define PAGE_ATTR_USER (PTE_U) +#define PAGE_ATTR_SYSTEM (0) + +#define PAGE_DEFAULT_ATTR_LEAF \ + (PAGE_ATTR_RWX | PAGE_ATTR_USER | PTE_V | PTE_G | PTE_SHARE | PTE_BUF | \ + PTE_CACHE | PTE_A | PTE_D) + +#define PAGE_DEFAULT_ATTR_NEXT \ + (PAGE_ATTR_NEXT_LEVEL | PTE_V | PTE_G | PTE_SHARE | PTE_BUF | PTE_CACHE | PTE_A | PTE_D) + +#define PAGE_IS_LEAF(pte) __MASKVALUE(pte, PAGE_ATTR_RWX) + +#define PTE_USED(pte) __MASKVALUE(pte, PTE_V) +#define PTE_WRAP(attr) (attr | PTE_A | PTE_D) + +/** + * encoding of SATP (Supervisor Address Translation and Protection register) + */ +#define SATP_MODE_OFFSET 60 +#define SATP_MODE_BARE 0 +#define SATP_MODE_SV39 8 +#define SATP_MODE_SV48 9 +#define SATP_MODE_SV57 10 +#define SATP_MODE_SV64 11 + +#define ARCH_VADDR_WIDTH 39 +#define SATP_MODE SATP_MODE_SV39 + +//compatible to rt-smart new version +#define MMU_MAP_K_DEVICE (PTE_BUF | PTE_SO | PTE_A | PTE_D | PTE_G | PTE_W | PTE_R | PTE_V) +#define MMU_MAP_K_RW (PTE_SHARE | PTE_A | PTE_D | PTE_G | PAGE_ATTR_RWX | PTE_V) +#define MMU_MAP_K_RWCB (MMU_MAP_K_RW | PTE_BUF | PTE_CACHE) + +#define MMU_MAP_U_RW (PTE_SHARE | PTE_U | PTE_A | PTE_D | PAGE_ATTR_RWX | PTE_V) +#define MMU_MAP_U_RWCB (MMU_MAP_U_RW | PTE_BUF | PTE_CACHE) +#define MMU_MAP_EARLY \ + PTE_WRAP(PAGE_ATTR_RWX | PTE_G | PTE_V | PTE_CACHE | PTE_SHARE | PTE_BUF) +#define MMU_MAP_TRACE(attr) (attr) + +#define PTE_XWR_MASK 0xe + +#define ARCH_PAGE_SIZE PAGE_SIZE +#define ARCH_PAGE_MASK (ARCH_PAGE_SIZE - 1) +#define ARCH_PAGE_SHIFT PAGE_OFFSET_BIT +#define ARCH_INDEX_WIDTH 9 + +#define ARCH_MAP_FAILED ((void *)0x8000000000000000) + +void mmu_set_pagetable(rt_ubase_t addr); +void mmu_enable_user_page_access(void); +void mmu_disable_user_page_access(void); + +#define RT_HW_MMU_PROT_READ 1 +#define RT_HW_MMU_PROT_WRITE 2 +#define RT_HW_MMU_PROT_EXECUTE 4 +#define RT_HW_MMU_PROT_KERNEL 8 +#define RT_HW_MMU_PROT_USER 16 +#define RT_HW_MMU_PROT_CACHE 32 + +void rt_hw_asid_init(void); +struct rt_aspace; +void rt_hw_asid_switch_pgtbl(struct rt_aspace *aspace, rt_ubase_t pgtbl); + +/** + * @brief Remove permission from attribution + * + * @param attr architecture specified mmu attribution + * @param prot protect that will be removed + * @return size_t returned attribution + */ +rt_inline size_t rt_hw_mmu_attr_rm_perm(size_t attr, rt_base_t prot) +{ + switch (prot) + { + /* remove write permission for user */ + case RT_HW_MMU_PROT_WRITE | RT_HW_MMU_PROT_USER: + attr &= ~PTE_W; + break; + /* remove write permission for kernel */ + case RT_HW_MMU_PROT_WRITE | RT_HW_MMU_PROT_KERNEL: + attr &= ~PTE_W; + break; + default: + RT_ASSERT(0); + } + return attr; +} + +/** + * @brief Add permission from attribution + * + * @param attr architecture specified mmu attribution + * @param prot protect that will be added + * @return size_t returned attribution + */ +rt_inline size_t rt_hw_mmu_attr_add_perm(size_t attr, rt_base_t prot) +{ + switch (prot) + { + /* add write permission for user */ + case RT_HW_MMU_PROT_WRITE | RT_HW_MMU_PROT_USER: + attr |= (PTE_R | PTE_W | PTE_U); + break; + default: + RT_ASSERT(0); + } + return attr; +} + +/** + * @brief Test permission from attribution + * + * @param attr architecture specified mmu attribution + * @param prot protect that will be test + * @return rt_bool_t RT_TRUE if the prot is allowed, otherwise RT_FALSE + */ +rt_inline rt_bool_t rt_hw_mmu_attr_test_perm(size_t attr, rt_base_t prot) +{ + rt_bool_t rc = 0; + switch (prot & ~RT_HW_MMU_PROT_USER) + { + /* test write permission for user */ + case RT_HW_MMU_PROT_WRITE: + rc = ((attr & PTE_W) && (attr & PTE_R)); + break; + case RT_HW_MMU_PROT_READ: + rc = !!(attr & PTE_R); + break; + case RT_HW_MMU_PROT_EXECUTE: + rc = !!(attr & PTE_X); + break; + default: + RT_ASSERT(0); + } + + if (rc && (prot & RT_HW_MMU_PROT_USER)) + { + rc = !!(attr & PTE_U); + } + return rc; +} + +#endif diff --git a/libcpu/risc-v/t-head/c908/symbol_analysis.c b/libcpu/risc-v/t-head/c908/symbol_analysis.c new file mode 100644 index 00000000000..83edacb1c43 --- /dev/null +++ b/libcpu/risc-v/t-head/c908/symbol_analysis.c @@ -0,0 +1,298 @@ +/* + * Copyright lizhirui + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-05-18 lizhirui the first version + * 2021-05-20 lizhirui add os debug support + */ + +#include +#include "symbol_analysis.h" + +#define MEMORY_BASE 0x40000000 +#define MEMORY_SIZE (128 * 0x100000) + +extern rt_size_t _osdebug_start; + +static os_symtab_header *symtab_header = (os_symtab_header *)&_osdebug_start; + +//该函数用于在指定的表中查找某个地址对应的符号的描述结构体指针,返回值的符号遵循规则详见文档 +os_symtab_item *find_symbol_table(rt_size_t symbol_table_addr,rt_size_t symbol_num,rt_size_t address) +{ + rt_size_t left = 0; + rt_size_t right = symbol_num; + os_symtab_item *sym_table = (os_symtab_item *)((rt_size_t)&_osdebug_start + symbol_table_addr); + + while(left < right) + { + rt_size_t mid = (left + right) >> 1; + //rt_kprintf("left = %d,right = %d,mid = %d\n",left,right,mid); + + if(address < sym_table[mid].address) + { + right = mid; + + while((right < symbol_num) && ((right - 1) >= 0) && (sym_table[right].address == sym_table[right - 1].address)) + { + right--; + } + } + else if(address == sym_table[mid].address) + { + left = mid + 1; + break; + } + else + { + left = mid; + + while((left >= 0) && ((left + 1) < symbol_num) && (sym_table[left].address == sym_table[left + 1].address)) + { + left++; + } + + left++; + } + } + + left--; + + if(left == ((rt_size_t)-1)) + { + return RT_NULL; + } + + while((left < symbol_num) && ((left - 1) >= 0) && (sym_table[left].address == sym_table[left - 1].address)) + { + left--; + } + + return &sym_table[left]; +} + +//该函数用于根据给定的符号指针从字符串表中找到对应的符号名指针并返回 +const char *get_symbol_name(os_symtab_item *symbol) +{ + return (const char *)((rt_size_t)&_osdebug_start + symtab_header -> string_table_offset + symbol -> name_offset); +} + +//该函数可以根据给定的符号和地址向中断打印出标准格式的符号信息 +void print_symbol(os_symtab_item *symbol,rt_size_t address) +{ + rt_kprintf("<%s(0x%p)",get_symbol_name(symbol),symbol -> address); + + if(symbol -> size) + { + rt_kprintf(" : 0x%x>",symbol -> size); + } + else + { + rt_kprintf(">"); + } + + if(address > symbol -> address) + { + rt_kprintf(" + 0x%x",address - symbol -> address); + } +} + +//该函数用于打印出一个地址关联的全部符号信息 +void print_symbol_info(rt_size_t address,rt_bool_t function) +{ + os_symtab_item *function_symbol = find_symbol_table(symtab_header -> function_table_offset,symtab_header -> function_table_num,address); + os_symtab_item *object_symbol = find_symbol_table(symtab_header -> object_table_offset,symtab_header -> object_table_num,address); + os_symtab_item *general_symbol = find_symbol_table(symtab_header -> general_symbol_table_offset,symtab_header -> general_symbol_table_num,address); + const char *dot = ""; + rt_bool_t valid = RT_FALSE; + + if(function) + { + while(function_symbol != RT_NULL) + { + if((function_symbol -> address + function_symbol -> size) > address) + { + rt_kprintf(dot); + print_symbol(function_symbol,address); + dot = ","; + valid = RT_TRUE; + } + + if(((rt_size_t)(function_symbol + 1)) >= (((rt_size_t)&_osdebug_start) + symtab_header -> function_table_offset + symtab_header -> function_table_num * sizeof(os_symtab_item))) + { + break; + } + + if(function_symbol[0].address == function_symbol[1].address) + { + function_symbol++; + } + + break; + } + + if(!valid) + { + while(general_symbol != RT_NULL) + { + rt_kprintf(dot); + print_symbol(general_symbol,address); + dot = ","; + valid = RT_TRUE; + + if(((rt_size_t)(general_symbol + 1)) >= (((rt_size_t)&_osdebug_start) + symtab_header -> general_symbol_table_offset + symtab_header -> general_symbol_table_num * sizeof(os_symtab_item))) + { + break; + } + + if(general_symbol[0].address == general_symbol[1].address) + { + general_symbol++; + } + + break; + } + + while(object_symbol != RT_NULL) + { + if((object_symbol -> address + object_symbol -> size) > address) + { + rt_kprintf(dot); + print_symbol(object_symbol,address); + dot = ","; + valid = RT_TRUE; + } + + if(((rt_size_t)(object_symbol + 1)) >= (((rt_size_t)&_osdebug_start) + symtab_header -> object_table_offset + symtab_header -> object_table_num * sizeof(os_symtab_item))) + { + break; + } + + if(object_symbol[0].address == object_symbol[1].address) + { + object_symbol++; + } + + break; + } + } + } + else + { + while(object_symbol != RT_NULL) + { + if((object_symbol -> address + object_symbol -> size) > address) + { + rt_kprintf(dot); + print_symbol(object_symbol,address); + dot = ","; + valid = RT_TRUE; + } + + if(((rt_size_t)(object_symbol + 1)) >= (((rt_size_t)&_osdebug_start) + symtab_header -> object_table_offset + symtab_header -> object_table_num * sizeof(os_symtab_item))) + { + break; + } + + if(object_symbol[0].address == object_symbol[1].address) + { + object_symbol++; + } + + break; + } + + if(!valid) + { + while(general_symbol != RT_NULL) + { + rt_kprintf(dot); + print_symbol(general_symbol,address); + dot = ","; + valid = RT_TRUE; + + if(((rt_size_t)(general_symbol + 1)) >= (((rt_size_t)&_osdebug_start) + symtab_header -> general_symbol_table_offset + symtab_header -> general_symbol_table_num * sizeof(os_symtab_item))) + { + break; + } + + if(general_symbol[0].address == general_symbol[1].address) + { + general_symbol++; + } + + break; + } + + while(function_symbol != RT_NULL) + { + if((function_symbol -> address + function_symbol -> size) > address) + { + rt_kprintf(dot); + print_symbol(function_symbol,address); + dot = ","; + valid = RT_TRUE; + } + + if(((rt_size_t)(function_symbol + 1)) >= (((rt_size_t)&_osdebug_start) + symtab_header -> function_table_offset + symtab_header -> function_table_num * sizeof(os_symtab_item))) + { + break; + } + + if(function_symbol[0].address == function_symbol[1].address) + { + function_symbol++; + } + + break; + } + } + } + + if(dot == "") + { + rt_kprintf(""); + } +} + +//该函数用于在出错时打印出栈跟踪信息 +void print_stacktrace(rt_size_t epc,rt_size_t fp) +{ + rt_kprintf("-----------------------------Dump Stacktrace----------------------------\n\n"); + rt_size_t sp = fp; + rt_size_t i = 0; + + rt_kprintf("address 0x%p(",epc); + print_symbol_info(epc,RT_TRUE); + rt_kprintf(")\n\n"); + + while(1) + { + if((sp >= MEMORY_BASE) && (sp < (MEMORY_BASE + MEMORY_SIZE))) + { + //rt_kprintf("%d: 0x%p\n",i,sp); + rt_size_t *stack = (rt_size_t *)(sp - sizeof(rt_size_t) * 2); + rt_size_t ra = stack[1]; + + if(!ra) + { + break; + } + + rt_kprintf("return to 0x%p(",ra); + print_symbol_info(ra,RT_TRUE); + rt_kprintf(")\n\n"); + //rt_kprintf("ra = 0x%p,fp = 0x%p\n",stack[1],stack[0]); + sp = stack[0]; + i++; + } + else + { + break; + } + } + + rt_kprintf("---------------------------------Dump OK--------------------------------\n"); +} diff --git a/libcpu/risc-v/t-head/c908/symbol_analysis.h b/libcpu/risc-v/t-head/c908/symbol_analysis.h new file mode 100644 index 00000000000..a9db46ffd39 --- /dev/null +++ b/libcpu/risc-v/t-head/c908/symbol_analysis.h @@ -0,0 +1,44 @@ +/* + * Copyright lizhirui + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-05-18 lizhirui the first version + * 2021-05-20 lizhirui add os debug support + */ + +#ifndef __SYMBOL_ANALYSIS_H__ +#define __SYMBOL_ANALYSIS_H__ + + #include + + //osdebug节区头描述结构体 + typedef struct os_symtab_header + { + rt_size_t function_table_offset;//函数表的偏移地址(相对于节区起始地址,下同) + rt_size_t function_table_num;//函数表中的符号数量 + rt_size_t object_table_offset;//对象表的偏移地址 + rt_size_t object_table_num;//对象表中的符号数量 + rt_size_t general_symbol_table_offset;//一般符号(指代类型虽为NONE但带有GLOBAL的符号)表的偏移地址 + rt_size_t general_symbol_table_num;//一般符号表中的符号数量 + rt_size_t string_table_offset;//字符串表的偏移地址 + rt_size_t string_table_size;//字符串表的大小(字节为单位) + }os_symtab_header; + + //符号描述结构体 + typedef struct os_symtab_item + { + rt_size_t name_offset;//符号名称在字符串表中的偏移地址 + rt_size_t address;//该符号所代表的地址 + rt_size_t size;//该符号所代表的大小 + }os_symtab_item; + + os_symtab_item *find_symbol_table(rt_size_t symbol_table_addr,rt_size_t symbol_num,rt_size_t address); + const char *get_symbol_name(os_symtab_item *symbol); + void print_symbol(os_symtab_item *symbol,rt_size_t address); + void print_symbol_info(rt_size_t address,rt_bool_t function); + void print_stacktrace(rt_size_t epc,rt_size_t fp); + +#endif