| /* SPDX-License-Identifier: GPL-2.0 */ |
| // Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd. |
| |
| #include <linux/linkage.h> |
| #include <abi/entry.h> |
| #include <abi/pgtable-bits.h> |
| #include <asm/errno.h> |
| #include <asm/setup.h> |
| #include <asm/unistd.h> |
| #include <asm/asm-offsets.h> |
| #include <linux/threads.h> |
| #include <asm/setup.h> |
| #include <asm/page.h> |
| #include <asm/thread_info.h> |
| |
| #define PTE_INDX_MSK 0xffc |
| #define PTE_INDX_SHIFT 10 |
| #define _PGDIR_SHIFT 22 |
| |
| .macro zero_fp |
| #ifdef CONFIG_STACKTRACE |
| movi r8, 0 |
| #endif |
| .endm |
| |
| .macro context_tracking |
| #ifdef CONFIG_CONTEXT_TRACKING |
| mfcr a0, epsr |
| btsti a0, 31 |
| bt 1f |
| jbsr context_tracking_user_exit |
| ldw a0, (sp, LSAVE_A0) |
| ldw a1, (sp, LSAVE_A1) |
| ldw a2, (sp, LSAVE_A2) |
| ldw a3, (sp, LSAVE_A3) |
| #if defined(__CSKYABIV1__) |
| ldw r6, (sp, LSAVE_A4) |
| ldw r7, (sp, LSAVE_A5) |
| #endif |
| 1: |
| #endif |
| .endm |
| |
| .macro tlbop_begin name, val0, val1, val2 |
| ENTRY(csky_\name) |
| mtcr a3, ss2 |
| mtcr r6, ss3 |
| mtcr a2, ss4 |
| |
| RD_PGDR r6 |
| RD_MEH a3 |
| #ifdef CONFIG_CPU_HAS_TLBI |
| tlbi.vaas a3 |
| sync.is |
| |
| btsti a3, 31 |
| bf 1f |
| RD_PGDR_K r6 |
| 1: |
| #else |
| bgeni a2, 31 |
| WR_MCIR a2 |
| bgeni a2, 25 |
| WR_MCIR a2 |
| #endif |
| bclri r6, 0 |
| lrw a2, va_pa_offset |
| ld.w a2, (a2, 0) |
| subu r6, a2 |
| bseti r6, 31 |
| |
| mov a2, a3 |
| lsri a2, _PGDIR_SHIFT |
| lsli a2, 2 |
| addu r6, a2 |
| ldw r6, (r6) |
| |
| lrw a2, va_pa_offset |
| ld.w a2, (a2, 0) |
| subu r6, a2 |
| bseti r6, 31 |
| |
| lsri a3, PTE_INDX_SHIFT |
| lrw a2, PTE_INDX_MSK |
| and a3, a2 |
| addu r6, a3 |
| ldw a3, (r6) |
| |
| movi a2, (_PAGE_PRESENT | \val0) |
| and a3, a2 |
| cmpne a3, a2 |
| bt \name |
| |
| /* First read/write the page, just update the flags */ |
| ldw a3, (r6) |
| bgeni a2, PAGE_VALID_BIT |
| bseti a2, PAGE_ACCESSED_BIT |
| bseti a2, \val1 |
| bseti a2, \val2 |
| or a3, a2 |
| stw a3, (r6) |
| |
| /* Some cpu tlb-hardrefill bypass the cache */ |
| #ifdef CONFIG_CPU_NEED_TLBSYNC |
| movi a2, 0x22 |
| bseti a2, 6 |
| mtcr r6, cr22 |
| mtcr a2, cr17 |
| sync |
| #endif |
| |
| mfcr a3, ss2 |
| mfcr r6, ss3 |
| mfcr a2, ss4 |
| rte |
| \name: |
| mfcr a3, ss2 |
| mfcr r6, ss3 |
| mfcr a2, ss4 |
| SAVE_ALL 0 |
| .endm |
| .macro tlbop_end is_write |
| zero_fp |
| context_tracking |
| RD_MEH a2 |
| psrset ee, ie |
| mov a0, sp |
| movi a1, \is_write |
| jbsr do_page_fault |
| jmpi ret_from_exception |
| .endm |
| |
| .text |
| |
| tlbop_begin tlbinvalidl, _PAGE_READ, PAGE_VALID_BIT, PAGE_ACCESSED_BIT |
| tlbop_end 0 |
| |
| tlbop_begin tlbinvalids, _PAGE_WRITE, PAGE_DIRTY_BIT, PAGE_MODIFIED_BIT |
| tlbop_end 1 |
| |
| tlbop_begin tlbmodified, _PAGE_WRITE, PAGE_DIRTY_BIT, PAGE_MODIFIED_BIT |
| #ifndef CONFIG_CPU_HAS_LDSTEX |
| jbsr csky_cmpxchg_fixup |
| #endif |
| tlbop_end 1 |
| |
| ENTRY(csky_systemcall) |
| SAVE_ALL TRAP0_SIZE |
| zero_fp |
| context_tracking |
| psrset ee, ie |
| |
| lrw r9, __NR_syscalls |
| cmphs syscallid, r9 /* Check nr of syscall */ |
| bt 1f |
| |
| lrw r9, sys_call_table |
| ixw r9, syscallid |
| ldw syscallid, (r9) |
| cmpnei syscallid, 0 |
| bf ret_from_exception |
| |
| mov r9, sp |
| bmaski r10, THREAD_SHIFT |
| andn r9, r10 |
| ldw r10, (r9, TINFO_FLAGS) |
| lrw r9, _TIF_SYSCALL_WORK |
| and r10, r9 |
| cmpnei r10, 0 |
| bt csky_syscall_trace |
| #if defined(__CSKYABIV2__) |
| subi sp, 8 |
| stw r5, (sp, 0x4) |
| stw r4, (sp, 0x0) |
| jsr syscallid /* Do system call */ |
| addi sp, 8 |
| #else |
| jsr syscallid |
| #endif |
| stw a0, (sp, LSAVE_A0) /* Save return value */ |
| 1: |
| #ifdef CONFIG_DEBUG_RSEQ |
| mov a0, sp |
| jbsr rseq_syscall |
| #endif |
| jmpi ret_from_exception |
| |
| csky_syscall_trace: |
| mov a0, sp /* sp = pt_regs pointer */ |
| jbsr syscall_trace_enter |
| cmpnei a0, 0 |
| bt 1f |
| /* Prepare args before do system call */ |
| ldw a0, (sp, LSAVE_A0) |
| ldw a1, (sp, LSAVE_A1) |
| ldw a2, (sp, LSAVE_A2) |
| ldw a3, (sp, LSAVE_A3) |
| #if defined(__CSKYABIV2__) |
| subi sp, 8 |
| ldw r9, (sp, LSAVE_A4) |
| stw r9, (sp, 0x0) |
| ldw r9, (sp, LSAVE_A5) |
| stw r9, (sp, 0x4) |
| jsr syscallid /* Do system call */ |
| addi sp, 8 |
| #else |
| ldw r6, (sp, LSAVE_A4) |
| ldw r7, (sp, LSAVE_A5) |
| jsr syscallid /* Do system call */ |
| #endif |
| stw a0, (sp, LSAVE_A0) /* Save return value */ |
| |
| 1: |
| #ifdef CONFIG_DEBUG_RSEQ |
| mov a0, sp |
| jbsr rseq_syscall |
| #endif |
| mov a0, sp /* right now, sp --> pt_regs */ |
| jbsr syscall_trace_exit |
| br ret_from_exception |
| |
| ENTRY(ret_from_kernel_thread) |
| jbsr schedule_tail |
| mov a0, r10 |
| jsr r9 |
| jbsr ret_from_exception |
| |
| ENTRY(ret_from_fork) |
| jbsr schedule_tail |
| mov r9, sp |
| bmaski r10, THREAD_SHIFT |
| andn r9, r10 |
| ldw r10, (r9, TINFO_FLAGS) |
| lrw r9, _TIF_SYSCALL_WORK |
| and r10, r9 |
| cmpnei r10, 0 |
| bf ret_from_exception |
| mov a0, sp /* sp = pt_regs pointer */ |
| jbsr syscall_trace_exit |
| |
| ret_from_exception: |
| psrclr ie |
| ld r9, (sp, LSAVE_PSR) |
| btsti r9, 31 |
| |
| bt 1f |
| /* |
| * Load address of current->thread_info, Then get address of task_struct |
| * Get task_needreshed in task_struct |
| */ |
| mov r9, sp |
| bmaski r10, THREAD_SHIFT |
| andn r9, r10 |
| |
| ldw r10, (r9, TINFO_FLAGS) |
| lrw r9, _TIF_WORK_MASK |
| and r10, r9 |
| cmpnei r10, 0 |
| bt exit_work |
| #ifdef CONFIG_CONTEXT_TRACKING |
| jbsr context_tracking_user_enter |
| #endif |
| 1: |
| #ifdef CONFIG_PREEMPTION |
| mov r9, sp |
| bmaski r10, THREAD_SHIFT |
| andn r9, r10 |
| |
| ldw r10, (r9, TINFO_PREEMPT) |
| cmpnei r10, 0 |
| bt 2f |
| jbsr preempt_schedule_irq /* irq en/disable is done inside */ |
| 2: |
| #endif |
| |
| #ifdef CONFIG_TRACE_IRQFLAGS |
| ld r10, (sp, LSAVE_PSR) |
| btsti r10, 6 |
| bf 2f |
| jbsr trace_hardirqs_on |
| 2: |
| #endif |
| RESTORE_ALL |
| |
| exit_work: |
| lrw r9, ret_from_exception |
| mov lr, r9 |
| |
| btsti r10, TIF_NEED_RESCHED |
| bt work_resched |
| |
| psrset ie |
| mov a0, sp |
| mov a1, r10 |
| jmpi do_notify_resume |
| |
| work_resched: |
| jmpi schedule |
| |
| ENTRY(csky_trap) |
| SAVE_ALL 0 |
| zero_fp |
| context_tracking |
| psrset ee |
| mov a0, sp /* Push Stack pointer arg */ |
| jbsr trap_c /* Call C-level trap handler */ |
| jmpi ret_from_exception |
| |
| /* |
| * Prototype from libc for abiv1: |
| * register unsigned int __result asm("a0"); |
| * asm( "trap 3" :"=r"(__result)::); |
| */ |
| ENTRY(csky_get_tls) |
| USPTOKSP |
| |
| /* increase epc for continue */ |
| mfcr a0, epc |
| addi a0, TRAP0_SIZE |
| mtcr a0, epc |
| |
| /* get current task thread_info with kernel 8K stack */ |
| bmaski a0, THREAD_SHIFT |
| not a0 |
| subi sp, 1 |
| and a0, sp |
| addi sp, 1 |
| |
| /* get tls */ |
| ldw a0, (a0, TINFO_TP_VALUE) |
| |
| KSPTOUSP |
| rte |
| |
| ENTRY(csky_irq) |
| SAVE_ALL 0 |
| zero_fp |
| context_tracking |
| psrset ee |
| |
| #ifdef CONFIG_TRACE_IRQFLAGS |
| jbsr trace_hardirqs_off |
| #endif |
| |
| |
| mov a0, sp |
| jbsr csky_do_IRQ |
| |
| jmpi ret_from_exception |
| |
| /* |
| * a0 = prev task_struct * |
| * a1 = next task_struct * |
| * a0 = return next |
| */ |
| ENTRY(__switch_to) |
| lrw a3, TASK_THREAD |
| addu a3, a0 |
| |
| SAVE_SWITCH_STACK |
| |
| stw sp, (a3, THREAD_KSP) |
| |
| /* Set up next process to run */ |
| lrw a3, TASK_THREAD |
| addu a3, a1 |
| |
| ldw sp, (a3, THREAD_KSP) /* Set next kernel sp */ |
| |
| #if defined(__CSKYABIV2__) |
| addi a3, a1, TASK_THREAD_INFO |
| ldw tls, (a3, TINFO_TP_VALUE) |
| #endif |
| |
| RESTORE_SWITCH_STACK |
| |
| rts |
| ENDPROC(__switch_to) |