diff options
Diffstat (limited to 'arch/h8300/kernel')
-rw-r--r-- | arch/h8300/kernel/.gitignore | 2 | ||||
-rw-r--r-- | arch/h8300/kernel/Makefile | 22 | ||||
-rw-r--r-- | arch/h8300/kernel/asm-offsets.c | 71 | ||||
-rw-r--r-- | arch/h8300/kernel/entry.S | 434 | ||||
-rw-r--r-- | arch/h8300/kernel/h8300_ksyms.c | 37 | ||||
-rw-r--r-- | arch/h8300/kernel/head_ram.S | 61 | ||||
-rw-r--r-- | arch/h8300/kernel/head_rom.S | 111 | ||||
-rw-r--r-- | arch/h8300/kernel/irq.c | 98 | ||||
-rw-r--r-- | arch/h8300/kernel/kgdb.c | 135 | ||||
-rw-r--r-- | arch/h8300/kernel/module.c | 71 | ||||
-rw-r--r-- | arch/h8300/kernel/process.c | 176 | ||||
-rw-r--r-- | arch/h8300/kernel/ptrace.c | 200 | ||||
-rw-r--r-- | arch/h8300/kernel/ptrace_h.c | 256 | ||||
-rw-r--r-- | arch/h8300/kernel/ptrace_s.c | 44 | ||||
-rw-r--r-- | arch/h8300/kernel/setup.c | 218 | ||||
-rw-r--r-- | arch/h8300/kernel/signal.c | 287 | ||||
-rw-r--r-- | arch/h8300/kernel/sim-console.c | 31 | ||||
-rw-r--r-- | arch/h8300/kernel/syscalls.c | 15 | ||||
-rw-r--r-- | arch/h8300/kernel/traps.c | 160 | ||||
-rw-r--r-- | arch/h8300/kernel/vmlinux.lds.S | 69 |
20 files changed, 2498 insertions, 0 deletions
diff --git a/arch/h8300/kernel/.gitignore b/arch/h8300/kernel/.gitignore new file mode 100644 index 000000000..bbb90f92d --- /dev/null +++ b/arch/h8300/kernel/.gitignore @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +vmlinux.lds diff --git a/arch/h8300/kernel/Makefile b/arch/h8300/kernel/Makefile new file mode 100644 index 000000000..307aa5157 --- /dev/null +++ b/arch/h8300/kernel/Makefile @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the linux kernel. +# + +extra-y := vmlinux.lds + +obj-y := process.o traps.o ptrace.o \ + signal.o setup.o syscalls.o \ + irq.o entry.o + +obj-$(CONFIG_ROMKERNEL) += head_rom.o +obj-$(CONFIG_RAMKERNEL) += head_ram.o + +obj-$(CONFIG_MODULES) += module.o h8300_ksyms.o +obj-$(CONFIG_H8300H_SIM) += sim-console.o +obj-$(CONFIG_H8S_SIM) += sim-console.o + +obj-$(CONFIG_CPU_H8300H) += ptrace_h.o +obj-$(CONFIG_CPU_H8S) += ptrace_s.o + +obj-$(CONFIG_KGDB) += kgdb.o diff --git a/arch/h8300/kernel/asm-offsets.c b/arch/h8300/kernel/asm-offsets.c new file mode 100644 index 000000000..d4b53af65 --- /dev/null +++ b/arch/h8300/kernel/asm-offsets.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This program is used to generate definitions needed by + * assembly language modules. + * + * We use the technique used in the OSF Mach kernel code: + * generate asm statements containing #defines, + * compile this file to assembler, and then extract the + * #defines from the assembly-language output. + */ + +#include <linux/stddef.h> +#include <linux/sched.h> +#include <linux/kernel_stat.h> +#include <linux/ptrace.h> +#include <linux/hardirq.h> +#include <linux/kbuild.h> +#include <asm/irq.h> +#include <asm/ptrace.h> + +int main(void) +{ + /* offsets into the task struct */ + OFFSET(TASK_STATE, task_struct, state); + OFFSET(TASK_FLAGS, task_struct, flags); + OFFSET(TASK_PTRACE, task_struct, ptrace); + OFFSET(TASK_BLOCKED, task_struct, blocked); + OFFSET(TASK_THREAD, task_struct, thread); + OFFSET(TASK_THREAD_INFO, task_struct, stack); + OFFSET(TASK_MM, task_struct, mm); + OFFSET(TASK_ACTIVE_MM, task_struct, active_mm); + + /* offsets into the irq_cpustat_t struct */ + DEFINE(CPUSTAT_SOFTIRQ_PENDING, offsetof(irq_cpustat_t, + __softirq_pending)); + + /* offsets into the thread struct */ + OFFSET(THREAD_KSP, thread_struct, ksp); + OFFSET(THREAD_USP, thread_struct, usp); + OFFSET(THREAD_CCR, thread_struct, ccr); + + /* offsets into the pt_regs struct */ + DEFINE(LER0, offsetof(struct pt_regs, er0) - sizeof(long)); + DEFINE(LER1, offsetof(struct pt_regs, er1) - sizeof(long)); + DEFINE(LER2, offsetof(struct pt_regs, er2) - sizeof(long)); + DEFINE(LER3, offsetof(struct pt_regs, er3) - sizeof(long)); + DEFINE(LER4, offsetof(struct pt_regs, er4) - sizeof(long)); + DEFINE(LER5, offsetof(struct pt_regs, er5) - sizeof(long)); + DEFINE(LER6, offsetof(struct pt_regs, er6) - sizeof(long)); + DEFINE(LORIG, offsetof(struct pt_regs, orig_er0) - sizeof(long)); + DEFINE(LSP, offsetof(struct pt_regs, sp) - sizeof(long)); + DEFINE(LCCR, offsetof(struct pt_regs, ccr) - sizeof(long)); + DEFINE(LVEC, offsetof(struct pt_regs, vector) - sizeof(long)); +#if defined(CONFIG_CPU_H8S) + DEFINE(LEXR, offsetof(struct pt_regs, exr) - sizeof(long)); +#endif + DEFINE(LRET, offsetof(struct pt_regs, pc) - sizeof(long)); + + DEFINE(PT_PTRACED, PT_PTRACED); + + /* offsets in thread_info structure */ + OFFSET(TI_TASK, thread_info, task); + OFFSET(TI_FLAGS, thread_info, flags); + OFFSET(TI_CPU, thread_info, cpu); + OFFSET(TI_PRE, thread_info, preempt_count); +#ifdef CONFIG_PREEMPTION + DEFINE(TI_PRE_COUNT, offsetof(struct thread_info, preempt_count)); +#endif + + return 0; +} diff --git a/arch/h8300/kernel/entry.S b/arch/h8300/kernel/entry.S new file mode 100644 index 000000000..c6e289b5f --- /dev/null +++ b/arch/h8300/kernel/entry.S @@ -0,0 +1,434 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * + * linux/arch/h8300/kernel/entry.S + * + * Yoshinori Sato <ysato@users.sourceforge.jp> + * David McCullough <davidm@snapgear.com> + * + */ + +/* + * entry.S + * include exception/interrupt gateway + * system call entry + */ + +#include <linux/sys.h> +#include <asm/unistd.h> +#include <asm/setup.h> +#include <asm/segment.h> +#include <asm/linkage.h> +#include <asm/asm-offsets.h> +#include <asm/thread_info.h> +#include <asm/errno.h> + +#if defined(CONFIG_CPU_H8300H) +#define USERRET 8 +INTERRUPTS = 64 + .h8300h + .macro SHLL2 reg + shll.l \reg + shll.l \reg + .endm + .macro SHLR2 reg + shlr.l \reg + shlr.l \reg + .endm + .macro SAVEREGS + mov.l er0,@-sp + mov.l er1,@-sp + mov.l er2,@-sp + mov.l er3,@-sp + .endm + .macro RESTOREREGS + mov.l @sp+,er3 + mov.l @sp+,er2 + .endm + .macro SAVEEXR + .endm + .macro RESTOREEXR + .endm +#endif +#if defined(CONFIG_CPU_H8S) +#define USERRET 10 +#define USEREXR 8 +INTERRUPTS = 128 + .h8300s + .macro SHLL2 reg + shll.l #2,\reg + .endm + .macro SHLR2 reg + shlr.l #2,\reg + .endm + .macro SAVEREGS + stm.l er0-er3,@-sp + .endm + .macro RESTOREREGS + ldm.l @sp+,er2-er3 + .endm + .macro SAVEEXR + mov.w @(USEREXR:16,er0),r1 + mov.w r1,@(LEXR-LER3:16,sp) /* copy EXR */ + .endm + .macro RESTOREEXR + mov.w @(LEXR-LER1:16,sp),r1 /* restore EXR */ + mov.b r1l,r1h + mov.w r1,@(USEREXR:16,er0) + .endm +#endif + + +/* CPU context save/restore macros. */ + + .macro SAVE_ALL + mov.l er0,@-sp + stc ccr,r0l /* check kernel mode */ + btst #4,r0l + bne 5f + + /* user mode */ + mov.l sp,@_sw_usp + mov.l @sp,er0 /* restore saved er0 */ + orc #0x10,ccr /* switch kernel stack */ + mov.l @_sw_ksp,sp + sub.l #(LRET-LORIG),sp /* allocate LORIG - LRET */ + SAVEREGS + mov.l @_sw_usp,er0 + mov.l @(USERRET:16,er0),er1 /* copy the RET addr */ + mov.l er1,@(LRET-LER3:16,sp) + SAVEEXR + + mov.l @(LORIG-LER3:16,sp),er0 + mov.l er0,@(LER0-LER3:16,sp) /* copy ER0 */ + mov.w e1,r1 /* e1 highbyte = ccr */ + and #0xef,r1h /* mask mode? flag */ + bra 6f +5: + /* kernel mode */ + mov.l @sp,er0 /* restore saved er0 */ + subs #2,sp /* set dummy ccr */ + subs #4,sp /* set dummp sp */ + SAVEREGS + mov.w @(LRET-LER3:16,sp),r1 /* copy old ccr */ +6: + mov.b r1h,r1l + mov.b #0,r1h + mov.w r1,@(LCCR-LER3:16,sp) /* set ccr */ + mov.l @_sw_usp,er2 + mov.l er2,@(LSP-LER3:16,sp) /* set usp */ + mov.l er6,@-sp /* syscall arg #6 */ + mov.l er5,@-sp /* syscall arg #5 */ + mov.l er4,@-sp /* syscall arg #4 */ + .endm /* r1 = ccr */ + + .macro RESTORE_ALL + mov.l @sp+,er4 + mov.l @sp+,er5 + mov.l @sp+,er6 + RESTOREREGS + mov.w @(LCCR-LER1:16,sp),r0 /* check kernel mode */ + btst #4,r0l + bne 7f + + orc #0xc0,ccr + mov.l @(LSP-LER1:16,sp),er0 + mov.l @(LER0-LER1:16,sp),er1 /* restore ER0 */ + mov.l er1,@er0 + RESTOREEXR + mov.w @(LCCR-LER1:16,sp),r1 /* restore the RET addr */ + mov.b r1l,r1h + mov.b @(LRET+1-LER1:16,sp),r1l + mov.w r1,e1 + mov.w @(LRET+2-LER1:16,sp),r1 + mov.l er1,@(USERRET:16,er0) + + mov.l @sp+,er1 + add.l #(LRET-LER1),sp /* remove LORIG - LRET */ + mov.l sp,@_sw_ksp + andc #0xef,ccr /* switch to user mode */ + mov.l er0,sp + bra 8f +7: + mov.l @sp+,er1 + add.l #10,sp +8: + mov.l @sp+,er0 + adds #4,sp /* remove the sw created LVEC */ + rte + .endm + +.globl _system_call +.globl ret_from_exception +.globl ret_from_fork +.globl ret_from_kernel_thread +.globl ret_from_interrupt +.globl _interrupt_redirect_table +.globl _sw_ksp,_sw_usp +.globl _resume +.globl _interrupt_entry +.globl _trace_break +.globl _nmi + +#if defined(CONFIG_ROMKERNEL) + .section .int_redirect,"ax" +_interrupt_redirect_table: +#if defined(CONFIG_CPU_H8300H) + .rept 7 + .long 0 + .endr +#endif +#if defined(CONFIG_CPU_H8S) + .rept 5 + .long 0 + .endr + jmp @_trace_break + .long 0 +#endif + + jsr @_interrupt_entry /* NMI */ + jmp @_system_call /* TRAPA #0 (System call) */ + .long 0 +#if defined(CONFIG_KGDB) + jmp @_kgdb_trap +#else + .long 0 +#endif + jmp @_trace_break /* TRAPA #3 (breakpoint) */ + .rept INTERRUPTS-12 + jsr @_interrupt_entry + .endr +#endif +#if defined(CONFIG_RAMKERNEL) +.globl _interrupt_redirect_table + .section .bss +_interrupt_redirect_table: + .space 4 +#endif + + .section .text + .align 2 +_interrupt_entry: + SAVE_ALL +/* r1l is saved ccr */ + mov.l sp,er0 + add.l #LVEC,er0 + btst #4,r1l + bne 1f + /* user LVEC */ + mov.l @_sw_usp,er0 + adds #4,er0 +1: + mov.l @er0,er0 /* LVEC address */ +#if defined(CONFIG_ROMKERNEL) + sub.l #_interrupt_redirect_table,er0 +#endif +#if defined(CONFIG_RAMKERNEL) + mov.l @_interrupt_redirect_table,er1 + sub.l er1,er0 +#endif + SHLR2 er0 + dec.l #1,er0 + mov.l sp,er1 + subs #4,er1 /* adjust ret_pc */ +#if defined(CONFIG_CPU_H8S) + orc #7,exr +#endif + jsr @do_IRQ + jmp @ret_from_interrupt + +_system_call: + subs #4,sp /* dummy LVEC */ + SAVE_ALL + /* er0: syscall nr */ + andc #0xbf,ccr + mov.l er0,er4 + + /* save top of frame */ + mov.l sp,er0 + jsr @set_esp0 + andc #0x3f,ccr + mov.l sp,er2 + and.w #0xe000,r2 + mov.l @(TI_FLAGS:16,er2),er2 + and.w #_TIF_WORK_SYSCALL_MASK,r2 + beq 1f + mov.l sp,er0 + jsr @do_syscall_trace_enter +1: + cmp.l #__NR_syscalls,er4 + bcc badsys + SHLL2 er4 + mov.l #_sys_call_table,er0 + add.l er4,er0 + mov.l @er0,er4 + beq ret_from_exception:16 + mov.l @(LER1:16,sp),er0 + mov.l @(LER2:16,sp),er1 + mov.l @(LER3:16,sp),er2 + jsr @er4 + mov.l er0,@(LER0:16,sp) /* save the return value */ + mov.l sp,er2 + and.w #0xe000,r2 + mov.l @(TI_FLAGS:16,er2),er2 + and.w #_TIF_WORK_SYSCALL_MASK,r2 + beq 2f + mov.l sp,er0 + jsr @do_syscall_trace_leave +2: + orc #0xc0,ccr + bra resume_userspace + +badsys: + mov.l #-ENOSYS,er0 + mov.l er0,@(LER0:16,sp) + bra resume_userspace + +#if !defined(CONFIG_PREEMPTION) +#define resume_kernel restore_all +#endif + +ret_from_exception: +#if defined(CONFIG_PREEMPTION) + orc #0xc0,ccr +#endif +ret_from_interrupt: + mov.b @(LCCR+1:16,sp),r0l + btst #4,r0l + bne resume_kernel:16 /* return from kernel */ +resume_userspace: + andc #0xbf,ccr + mov.l sp,er4 + and.w #0xe000,r4 /* er4 <- current thread info */ + mov.l @(TI_FLAGS:16,er4),er1 + and.l #_TIF_WORK_MASK,er1 + beq restore_all:8 +work_pending: + btst #TIF_NEED_RESCHED,r1l + bne work_resched:8 + /* work notifysig */ + mov.l sp,er0 + subs #4,er0 /* er0: pt_regs */ + jsr @do_notify_resume + bra resume_userspace:8 +work_resched: + mov.l sp,er0 + jsr @set_esp0 + jsr @schedule + bra resume_userspace:8 +restore_all: + RESTORE_ALL /* Does RTE */ + +#if defined(CONFIG_PREEMPTION) +resume_kernel: + mov.l @(TI_PRE_COUNT:16,er4),er0 + bne restore_all:8 +need_resched: + mov.l @(TI_FLAGS:16,er4),er0 + btst #TIF_NEED_RESCHED,r0l + beq restore_all:8 + mov.b @(LCCR+1:16,sp),r0l /* Interrupt Enabled? */ + bmi restore_all:8 + mov.l sp,er0 + jsr @set_esp0 + jsr @preempt_schedule_irq + bra need_resched:8 +#endif + +ret_from_fork: + mov.l er2,er0 + jsr @schedule_tail + jmp @ret_from_exception + +ret_from_kernel_thread: + mov.l er2,er0 + jsr @schedule_tail + mov.l @(LER4:16,sp),er0 + mov.l @(LER5:16,sp),er1 + jsr @er1 + jmp @ret_from_exception + +_resume: + /* + * Beware - when entering resume, offset of tss is in d1, + * prev (the current task) is in a0, next (the new task) + * is in a1 and d2.b is non-zero if the mm structure is + * shared between the tasks, so don't change these + * registers until their contents are no longer needed. + */ + + /* save sr */ + sub.w r3,r3 + stc ccr,r3l + mov.w r3,@(THREAD_CCR+2:16,er0) + + /* disable interrupts */ + orc #0xc0,ccr + mov.l @_sw_usp,er3 + mov.l er3,@(THREAD_USP:16,er0) + mov.l sp,@(THREAD_KSP:16,er0) + + /* Skip address space switching if they are the same. */ + /* FIXME: what did we hack out of here, this does nothing! */ + + mov.l @(THREAD_USP:16,er1),er0 + mov.l er0,@_sw_usp + mov.l @(THREAD_KSP:16,er1),sp + + /* restore status register */ + mov.w @(THREAD_CCR+2:16,er1),r3 + + ldc r3l,ccr + rts + +_trace_break: + subs #4,sp + SAVE_ALL + sub.l er1,er1 + dec.l #1,er1 + mov.l er1,@(LORIG,sp) + mov.l sp,er0 + jsr @set_esp0 + mov.l @_sw_usp,er0 + mov.l @er0,er1 + mov.w @(-2:16,er1),r2 + cmp.w #0x5730,r2 + beq 1f + subs #2,er1 + mov.l er1,@er0 +1: + and.w #0xff,e1 + mov.l er1,er0 + jsr @trace_trap + jmp @ret_from_exception + +_nmi: + subs #4, sp + mov.l er0, @-sp + mov.l @_interrupt_redirect_table, er0 + add.l #8*4, er0 + mov.l er0, @(4,sp) + mov.l @sp+, er0 + jmp @_interrupt_entry + +#if defined(CONFIG_KGDB) +_kgdb_trap: + subs #4,sp + SAVE_ALL + mov.l sp,er0 + add.l #LRET,er0 + mov.l er0,@(LSP,sp) + jsr @set_esp0 + mov.l sp,er0 + subs #4,er0 + jsr @h8300_kgdb_trap + jmp @ret_from_exception +#endif + + .section .bss +_sw_ksp: + .space 4 +_sw_usp: + .space 4 + + .end diff --git a/arch/h8300/kernel/h8300_ksyms.c b/arch/h8300/kernel/h8300_ksyms.c new file mode 100644 index 000000000..1c6f902e8 --- /dev/null +++ b/arch/h8300/kernel/h8300_ksyms.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/module.h> +#include <linux/linkage.h> + +/* + * libgcc functions - functions that are used internally by the + * compiler... (prototypes are not correct though, but that + * doesn't really matter since they're not versioned). + */ +asmlinkage long __ucmpdi2(long long, long long); +asmlinkage long long __ashldi3(long long, int); +asmlinkage long long __ashrdi3(long long, int); +asmlinkage long long __lshrdi3(long long, int); +asmlinkage long __divsi3(long, long); +asmlinkage long __modsi3(long, long); +asmlinkage unsigned long __umodsi3(unsigned long, unsigned long); +asmlinkage long long __muldi3(long long, long long); +asmlinkage long __mulsi3(long, long); +asmlinkage long __udivsi3(long, long); +asmlinkage void *memcpy(void *, const void *, size_t); +asmlinkage void *memset(void *, int, size_t); +asmlinkage long strncpy_from_user(void *to, void *from, size_t n); + + /* gcc lib functions */ +EXPORT_SYMBOL(__ucmpdi2); +EXPORT_SYMBOL(__ashldi3); +EXPORT_SYMBOL(__ashrdi3); +EXPORT_SYMBOL(__lshrdi3); +EXPORT_SYMBOL(__divsi3); +EXPORT_SYMBOL(__modsi3); +EXPORT_SYMBOL(__umodsi3); +EXPORT_SYMBOL(__muldi3); +EXPORT_SYMBOL(__mulsi3); +EXPORT_SYMBOL(__udivsi3); +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memset); +EXPORT_SYMBOL(strncpy_from_user); diff --git a/arch/h8300/kernel/head_ram.S b/arch/h8300/kernel/head_ram.S new file mode 100644 index 000000000..dbf8429f5 --- /dev/null +++ b/arch/h8300/kernel/head_ram.S @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include <linux/sys.h> +#include <linux/init.h> +#include <asm/unistd.h> +#include <asm/setup.h> +#include <asm/segment.h> +#include <asm/linkage.h> +#include <asm/asm-offsets.h> +#include <asm/thread_info.h> +#include <asm/errno.h> + +#if defined(CONFIG_CPU_H8300H) + .h8300h +#define SYSCR 0xfee012 +#define IRAMTOP 0xffff20 +#endif +#if defined(CONFIG_CPU_H8S) + .h8300s +#define INTCR 0xffff31 +#define IRAMTOP 0xffc000 +#endif + + __HEAD + .global _start +_start: + mov.l #IRAMTOP,sp + /* .bss clear */ + mov.l #_sbss,er5 + mov.l #_ebss,er4 + sub.l er5,er4 + shlr er4 + shlr er4 + sub.l er2,er2 +1: + mov.l er2,@er5 + adds #4,er5 + dec.l #1,er4 + bne 1b + jsr @h8300_fdt_init + + /* linux kernel start */ +#if defined(CONFIG_CPU_H8300H) + ldc #0xd0,ccr /* running kernel */ + mov.l #SYSCR,er0 + bclr #3,@er0 +#endif +#if defined(CONFIG_CPU_H8S) + ldc #0x07,exr + bclr #4,@INTCR:8 + bset #5,@INTCR:8 /* Interrupt mode 2 */ + ldc #0x90,ccr /* running kernel */ +#endif + mov.l #init_thread_union,sp + add.l #0x2000,sp + jsr @start_kernel + +1: + bra 1b + + .end diff --git a/arch/h8300/kernel/head_rom.S b/arch/h8300/kernel/head_rom.S new file mode 100644 index 000000000..ab55a9cb2 --- /dev/null +++ b/arch/h8300/kernel/head_rom.S @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <linux/init.h> +#include <asm/thread_info.h> + +#if defined(CONFIG_CPU_H8300H) + .h8300h +#define SYSCR 0xfee012 +#define IRAMTOP 0xffff20 +#define NR_INT 64 +#endif +#if defined(CONFIG_CPU_H8S) + .h8300s +#define INTCR 0xffff31 +#define IRAMTOP 0xffc000 +#define NR_INT 128 +#endif + + __HEAD + .global _start +_start: + mov.l #IRAMTOP,sp +#if !defined(CONFIG_H8300H_SIM) && \ + !defined(CONFIG_H8S_SIM) + jsr @lowlevel_init + + /* copy .data */ + mov.l #_begin_data,er5 + mov.l #_sdata,er6 + mov.l #_edata,er4 + sub.l er6,er4 + shlr.l er4 + shlr.l er4 +1: + mov.l @er5+,er0 + mov.l er0,@er6 + adds #4,er6 + dec.l #1,er4 + bne 1b + /* .bss clear */ + mov.l #_sbss,er5 + mov.l #_ebss,er4 + sub.l er5,er4 + shlr er4 + shlr er4 + sub.l er0,er0 +1: + mov.l er0,@er5 + adds #4,er5 + dec.l #1,er4 + bne 1b +#else + /* get cmdline from gdb */ + jsr @0xcc + ;; er0 - argc + ;; er1 - argv + mov.l #command_line,er3 + adds #4,er1 + dec.l #1,er0 + beq 4f +1: + mov.l @er1+,er2 +2: + mov.b @er2+,r4l + beq 3f + mov.b r4l,@er3 + adds #1,er3 + bra 2b +3: + mov.b #' ',r4l + mov.b r4l,@er3 + adds #1,er3 + dec.l #1,er0 + bne 1b + subs #1,er3 + mov.b #0,r4l + mov.b r4l,@er3 +4: +#endif + sub.l er0,er0 + jsr @h8300_fdt_init + /* linux kernel start */ +#if defined(CONFIG_CPU_H8300H) + ldc #0xd0,ccr /* running kernel */ + mov.l #SYSCR,er0 + bclr #3,@er0 +#endif +#if defined(CONFIG_CPU_H8S) + ldc #0x07,exr + bclr #4,@INTCR:8 + bset #5,@INTCR:8 /* Interrupt mode 2 */ + ldc #0x90,ccr /* running kernel */ +#endif + mov.l #init_thread_union,sp + add.l #0x2000,sp + jsr @start_kernel + +1: + bra 1b + +#if defined(CONFIG_ROMKERNEL) + /* interrupt vector */ + .section .vectors,"ax" + .long _start + .long _start +vector = 2 + .rept NR_INT - 2 + .long _interrupt_redirect_table+vector*4 +vector = vector + 1 + .endr +#endif + .end diff --git a/arch/h8300/kernel/irq.c b/arch/h8300/kernel/irq.c new file mode 100644 index 000000000..834e4d7b1 --- /dev/null +++ b/arch/h8300/kernel/irq.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/arch/h8300/kernel/irq.c + * + * Copyright 2014-2015 Yoshinori Sato <ysato@users.sourceforge.jp> + */ + +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/of_irq.h> +#include <asm/traps.h> + +#ifdef CONFIG_RAMKERNEL +typedef void (*h8300_vector)(void); + +static const h8300_vector __initconst trap_table[] = { + 0, 0, 0, 0, + _trace_break, + 0, 0, + _nmi, + _system_call, + 0, 0, + _trace_break, +}; + +static unsigned long __init *get_vector_address(void) +{ + unsigned long *rom_vector = CPU_VECTOR; + unsigned long base, tmp; + int vec_no; + + base = rom_vector[EXT_IRQ0] & ADDR_MASK; + + /* check romvector format */ + for (vec_no = EXT_IRQ0 + 1; vec_no <= EXT_IRQ0+EXT_IRQS; vec_no++) { + if ((base+(vec_no - EXT_IRQ0)*4) != + (rom_vector[vec_no] & ADDR_MASK)) + return NULL; + } + + /* ramvector base address */ + base -= EXT_IRQ0*4; + + /* writerble? */ + tmp = ~(*(volatile unsigned long *)base); + (*(volatile unsigned long *)base) = tmp; + if ((*(volatile unsigned long *)base) != tmp) + return NULL; + return (unsigned long *)base; +} + +static void __init setup_vector(void) +{ + int i; + unsigned long *ramvec, *ramvec_p; + const h8300_vector *trap_entry; + + ramvec = get_vector_address(); + if (ramvec == NULL) + panic("interrupt vector serup failed."); + else + pr_debug("virtual vector at 0x%p\n", ramvec); + + /* create redirect table */ + ramvec_p = ramvec; + trap_entry = trap_table; + for (i = 0; i < NR_IRQS; i++) { + if (i < 12) { + if (*trap_entry) + *ramvec_p = VECTOR(*trap_entry); + ramvec_p++; + trap_entry++; + } else + *ramvec_p++ = REDIRECT(_interrupt_entry); + } + _interrupt_redirect_table = ramvec; +} +#else +void setup_vector(void) +{ + /* noting do */ +} +#endif + +void __init init_IRQ(void) +{ + setup_vector(); + irqchip_init(); +} + +asmlinkage void do_IRQ(int irq) +{ + irq_enter(); + generic_handle_irq(irq); + irq_exit(); +} diff --git a/arch/h8300/kernel/kgdb.c b/arch/h8300/kernel/kgdb.c new file mode 100644 index 000000000..602e478af --- /dev/null +++ b/arch/h8300/kernel/kgdb.c @@ -0,0 +1,135 @@ +/* + * H8/300 KGDB support + * + * Copyright (C) 2015 Yoshinori Sato <ysato@users.sourceforge.jp> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/ptrace.h> +#include <linux/kgdb.h> +#include <linux/kdebug.h> +#include <linux/io.h> + +struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = { + { "er0", GDB_SIZEOF_REG, offsetof(struct pt_regs, er0) }, + { "er1", GDB_SIZEOF_REG, offsetof(struct pt_regs, er1) }, + { "er2", GDB_SIZEOF_REG, offsetof(struct pt_regs, er2) }, + { "er3", GDB_SIZEOF_REG, offsetof(struct pt_regs, er3) }, + { "er4", GDB_SIZEOF_REG, offsetof(struct pt_regs, er4) }, + { "er5", GDB_SIZEOF_REG, offsetof(struct pt_regs, er5) }, + { "er6", GDB_SIZEOF_REG, offsetof(struct pt_regs, er6) }, + { "sp", GDB_SIZEOF_REG, offsetof(struct pt_regs, sp) }, + { "ccr", GDB_SIZEOF_REG, offsetof(struct pt_regs, ccr) }, + { "pc", GDB_SIZEOF_REG, offsetof(struct pt_regs, pc) }, + { "cycles", GDB_SIZEOF_REG, -1 }, +#if defined(CONFIG_CPU_H8S) + { "exr", GDB_SIZEOF_REG, offsetof(struct pt_regs, exr) }, +#endif + { "tick", GDB_SIZEOF_REG, -1 }, + { "inst", GDB_SIZEOF_REG, -1 }, +}; + +char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs) +{ + if (regno >= DBG_MAX_REG_NUM || regno < 0) + return NULL; + + switch (regno) { + case GDB_CCR: +#if defined(CONFIG_CPU_H8S) + case GDB_EXR: +#endif + *(u32 *)mem = *(u16 *)((void *)regs + + dbg_reg_def[regno].offset); + break; + default: + if (dbg_reg_def[regno].offset >= 0) + memcpy(mem, (void *)regs + dbg_reg_def[regno].offset, + dbg_reg_def[regno].size); + else + memset(mem, 0, dbg_reg_def[regno].size); + break; + } + return dbg_reg_def[regno].name; +} + +int dbg_set_reg(int regno, void *mem, struct pt_regs *regs) +{ + if (regno >= DBG_MAX_REG_NUM || regno < 0) + return -EINVAL; + + switch (regno) { + case GDB_CCR: +#if defined(CONFIG_CPU_H8S) + case GDB_EXR: +#endif + *(u16 *)((void *)regs + + dbg_reg_def[regno].offset) = *(u32 *)mem; + break; + default: + memcpy((void *)regs + dbg_reg_def[regno].offset, mem, + dbg_reg_def[regno].size); + } + return 0; +} + +asmlinkage void h8300_kgdb_trap(struct pt_regs *regs) +{ + regs->pc &= 0x00ffffff; + if (kgdb_handle_exception(10, SIGTRAP, 0, regs)) + return; + if (*(u16 *)(regs->pc) == *(u16 *)&arch_kgdb_ops.gdb_bpt_instr) + regs->pc += BREAK_INSTR_SIZE; + regs->pc |= regs->ccr << 24; +} + +void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) +{ + memset((char *)gdb_regs, 0, NUMREGBYTES); + gdb_regs[GDB_SP] = p->thread.ksp; + gdb_regs[GDB_PC] = KSTK_EIP(p); +} + +void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc) +{ + regs->pc = pc; +} + +int kgdb_arch_handle_exception(int vector, int signo, int err_code, + char *remcom_in_buffer, char *remcom_out_buffer, + struct pt_regs *regs) +{ + char *ptr; + unsigned long addr; + + switch (remcom_in_buffer[0]) { + case 's': + case 'c': + /* handle the optional parameters */ + ptr = &remcom_in_buffer[1]; + if (kgdb_hex2long(&ptr, &addr)) + regs->pc = addr; + + return 0; + } + + return -1; /* this means that we do not want to exit from the handler */ +} + +int kgdb_arch_init(void) +{ + return 0; +} + +void kgdb_arch_exit(void) +{ + /* Nothing to do */ +} + +const struct kgdb_arch arch_kgdb_ops = { + /* Breakpoint instruction: trapa #2 */ + .gdb_bpt_instr = { 0x57, 0x20 }, +}; diff --git a/arch/h8300/kernel/module.c b/arch/h8300/kernel/module.c new file mode 100644 index 000000000..cd802cc78 --- /dev/null +++ b/arch/h8300/kernel/module.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/moduleloader.h> +#include <linux/elf.h> +#include <linux/vmalloc.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/kernel.h> + +int apply_relocate_add(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + unsigned int i; + Elf32_Rela *rela = (void *)sechdrs[relsec].sh_addr; + + pr_debug("Applying relocate section %u to %u\n", relsec, + sechdrs[relsec].sh_info); + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) { + /* This is where to make the change */ + uint32_t *loc = + (uint32_t *)(sechdrs[sechdrs[relsec].sh_info].sh_addr + + rela[i].r_offset); + /* This is the symbol it is referring to. Note that all + undefined symbols have been resolved. */ + Elf32_Sym *sym = (Elf32_Sym *)sechdrs[symindex].sh_addr + + ELF32_R_SYM(rela[i].r_info); + uint32_t v = sym->st_value + rela[i].r_addend; + + switch (ELF32_R_TYPE(rela[i].r_info)) { + case R_H8_DIR24R8: + loc = (uint32_t *)((uint32_t)loc - 1); + *loc = (*loc & 0xff000000) | ((*loc & 0xffffff) + v); + break; + case R_H8_DIR24A8: + if (ELF32_R_SYM(rela[i].r_info)) + *loc += v; + break; + case R_H8_DIR32: + case R_H8_DIR32A16: + *loc += v; + break; + case R_H8_PCREL16: + v -= (unsigned long)loc + 2; + if ((Elf32_Sword)v > 0x7fff || + (Elf32_Sword)v < -(Elf32_Sword)0x8000) + goto overflow; + else + *(unsigned short *)loc = v; + break; + case R_H8_PCREL8: + v -= (unsigned long)loc + 1; + if ((Elf32_Sword)v > 0x7f || + (Elf32_Sword)v < -(Elf32_Sword)0x80) + goto overflow; + else + *(unsigned char *)loc = v; + break; + default: + pr_err("module %s: Unknown relocation: %u\n", + me->name, ELF32_R_TYPE(rela[i].r_info)); + return -ENOEXEC; + } + } + return 0; + overflow: + pr_err("module %s: relocation offset overflow: %08x\n", + me->name, rela[i].r_offset); + return -ENOEXEC; +} diff --git a/arch/h8300/kernel/process.c b/arch/h8300/kernel/process.c new file mode 100644 index 000000000..46b1342ce --- /dev/null +++ b/arch/h8300/kernel/process.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/arch/h8300/kernel/process.c + * + * Yoshinori Sato <ysato@users.sourceforge.jp> + * + * Based on: + * + * linux/arch/m68knommu/kernel/process.c + * + * Copyright (C) 1998 D. Jeff Dionne <jeff@ryeham.ee.ryerson.ca>, + * Kenneth Albanowski <kjahds@kjahds.com>, + * The Silver Hammer Group, Ltd. + * + * linux/arch/m68k/kernel/process.c + * + * Copyright (C) 1995 Hamish Macdonald + * + * 68060 fixes by Jesper Skov + */ + +/* + * This file handles the architecture-dependent parts of process handling.. + */ + +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/sched/debug.h> +#include <linux/sched/task.h> +#include <linux/sched/task_stack.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/user.h> +#include <linux/interrupt.h> +#include <linux/reboot.h> +#include <linux/fs.h> +#include <linux/slab.h> +#include <linux/rcupdate.h> + +#include <linux/uaccess.h> +#include <asm/traps.h> +#include <asm/setup.h> + +void (*pm_power_off)(void) = NULL; +EXPORT_SYMBOL(pm_power_off); + +asmlinkage void ret_from_fork(void); +asmlinkage void ret_from_kernel_thread(void); + +/* + * The idle loop on an H8/300.. + */ +void arch_cpu_idle(void) +{ + raw_local_irq_enable(); + __asm__("sleep"); +} + +void machine_restart(char *__unused) +{ + local_irq_disable(); + __asm__("jmp @@0"); +} + +void machine_halt(void) +{ + local_irq_disable(); + __asm__("sleep"); + for (;;) + ; +} + +void machine_power_off(void) +{ + local_irq_disable(); + __asm__("sleep"); + for (;;) + ; +} + +void show_regs(struct pt_regs *regs) +{ + show_regs_print_info(KERN_DEFAULT); + + pr_notice("\n"); + pr_notice("PC: %08lx Status: %02x\n", + regs->pc, regs->ccr); + pr_notice("ORIG_ER0: %08lx ER0: %08lx ER1: %08lx\n", + regs->orig_er0, regs->er0, regs->er1); + pr_notice("ER2: %08lx ER3: %08lx ER4: %08lx ER5: %08lx\n", + regs->er2, regs->er3, regs->er4, regs->er5); + pr_notice("ER6' %08lx ", regs->er6); + if (user_mode(regs)) + printk("USP: %08lx\n", rdusp()); + else + printk("\n"); +} + +void flush_thread(void) +{ +} + +int copy_thread(unsigned long clone_flags, unsigned long usp, + unsigned long topstk, struct task_struct *p, unsigned long tls) +{ + struct pt_regs *childregs; + + childregs = (struct pt_regs *) (THREAD_SIZE + task_stack_page(p)) - 1; + + if (unlikely(p->flags & (PF_KTHREAD | PF_IO_WORKER))) { + memset(childregs, 0, sizeof(struct pt_regs)); + childregs->retpc = (unsigned long) ret_from_kernel_thread; + childregs->er4 = topstk; /* arg */ + childregs->er5 = usp; /* fn */ + } else { + *childregs = *current_pt_regs(); + childregs->er0 = 0; + childregs->retpc = (unsigned long) ret_from_fork; + p->thread.usp = usp ?: rdusp(); + } + p->thread.ksp = (unsigned long)childregs; + + return 0; +} + +unsigned long get_wchan(struct task_struct *p) +{ + unsigned long fp, pc; + unsigned long stack_page; + int count = 0; + + if (!p || p == current || p->state == TASK_RUNNING) + return 0; + + stack_page = (unsigned long)p; + fp = ((struct pt_regs *)p->thread.ksp)->er6; + do { + if (fp < stack_page+sizeof(struct thread_info) || + fp >= 8184+stack_page) + return 0; + pc = ((unsigned long *)fp)[1]; + if (!in_sched_functions(pc)) + return pc; + fp = *(unsigned long *) fp; + } while (count++ < 16); + return 0; +} + +/* generic sys_clone is not enough registers */ +asmlinkage int sys_clone(unsigned long __user *args) +{ + unsigned long clone_flags; + unsigned long newsp; + uintptr_t parent_tidptr; + uintptr_t child_tidptr; + struct kernel_clone_args kargs = {}; + + get_user(clone_flags, &args[0]); + get_user(newsp, &args[1]); + get_user(parent_tidptr, &args[2]); + get_user(child_tidptr, &args[3]); + + kargs.flags = (lower_32_bits(clone_flags) & ~CSIGNAL); + kargs.pidfd = (int __user *)parent_tidptr; + kargs.child_tid = (int __user *)child_tidptr; + kargs.parent_tid = (int __user *)parent_tidptr; + kargs.exit_signal = (lower_32_bits(clone_flags) & CSIGNAL); + kargs.stack = newsp; + + return kernel_clone(&kargs); +} diff --git a/arch/h8300/kernel/ptrace.c b/arch/h8300/kernel/ptrace.c new file mode 100644 index 000000000..a11db009d --- /dev/null +++ b/arch/h8300/kernel/ptrace.c @@ -0,0 +1,200 @@ +/* + * linux/arch/h8300/kernel/ptrace.c + * + * Copyright 2015 Yoshinori Sato <ysato@users.sourceforge.jp> + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file COPYING in the main directory of + * this archive for more details. + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/audit.h> +#include <linux/tracehook.h> +#include <linux/regset.h> +#include <linux/elf.h> + +#define CCR_MASK 0x6f /* mode/imask not set */ +#define EXR_MASK 0x80 /* modify only T */ + +#define PT_REG(r) offsetof(struct pt_regs, r) + +extern void user_disable_single_step(struct task_struct *child); + +/* Mapping from PT_xxx to the stack offset at which the register is + saved. Notice that usp has no stack-slot and needs to be treated + specially (see get_reg/put_reg below). */ +static const int register_offset[] = { + PT_REG(er1), PT_REG(er2), PT_REG(er3), PT_REG(er4), + PT_REG(er5), PT_REG(er6), PT_REG(er0), -1, + PT_REG(orig_er0), PT_REG(ccr), PT_REG(pc), +#if defined(CONFIG_CPU_H8S) + PT_REG(exr), +#endif +}; + +/* read register */ +long h8300_get_reg(struct task_struct *task, int regno) +{ + switch (regno) { + case PT_USP: + return task->thread.usp + sizeof(long)*2; + case PT_CCR: + case PT_EXR: + return *(unsigned short *)(task->thread.esp0 + + register_offset[regno]); + default: + return *(unsigned long *)(task->thread.esp0 + + register_offset[regno]); + } +} + +int h8300_put_reg(struct task_struct *task, int regno, unsigned long data) +{ + unsigned short oldccr; + unsigned short oldexr; + + switch (regno) { + case PT_USP: + task->thread.usp = data - sizeof(long)*2; + case PT_CCR: + oldccr = *(unsigned short *)(task->thread.esp0 + + register_offset[regno]); + oldccr &= ~CCR_MASK; + data &= CCR_MASK; + data |= oldccr; + *(unsigned short *)(task->thread.esp0 + + register_offset[regno]) = data; + break; + case PT_EXR: + oldexr = *(unsigned short *)(task->thread.esp0 + + register_offset[regno]); + oldccr &= ~EXR_MASK; + data &= EXR_MASK; + data |= oldexr; + *(unsigned short *)(task->thread.esp0 + + register_offset[regno]) = data; + break; + default: + *(unsigned long *)(task->thread.esp0 + + register_offset[regno]) = data; + break; + } + return 0; +} + +static int regs_get(struct task_struct *target, + const struct user_regset *regset, + struct membuf to) +{ + int r; + + BUILD_BUG_ON(sizeof(struct user_regs_struct) % sizeof(long) != 0); + for (r = 0; r < ELF_NGREG; r++) + membuf_store(&to, h8300_get_reg(target, r)); + + return 0; +} + +static int regs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + int r; + int ret; + struct user_regs_struct regs; + long *reg; + + /* build user regs in buffer */ + BUILD_BUG_ON(sizeof(regs) % sizeof(long) != 0); + for (reg = (long *)®s, r = 0; r < sizeof(regs) / sizeof(long); r++) + *reg++ = h8300_get_reg(target, r); + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + ®s, 0, sizeof(regs)); + if (ret) + return ret; + + /* write back to pt_regs */ + for (reg = (long *)®s, r = 0; r < sizeof(regs) / sizeof(long); r++) + h8300_put_reg(target, r, *reg++); + return 0; +} + +enum h8300_regset { + REGSET_GENERAL, +}; + +static const struct user_regset h8300_regsets[] = { + [REGSET_GENERAL] = { + .core_note_type = NT_PRSTATUS, + .n = ELF_NGREG, + .size = sizeof(long), + .align = sizeof(long), + .regset_get = regs_get, + .set = regs_set, + }, +}; + +static const struct user_regset_view user_h8300_native_view = { + .name = "h8300", + .e_machine = EM_H8_300, + .regsets = h8300_regsets, + .n = ARRAY_SIZE(h8300_regsets), +}; + +const struct user_regset_view *task_user_regset_view(struct task_struct *task) +{ + return &user_h8300_native_view; +} + +void ptrace_disable(struct task_struct *child) +{ + user_disable_single_step(child); +} + +long arch_ptrace(struct task_struct *child, long request, + unsigned long addr, unsigned long data) +{ + int ret; + + switch (request) { + default: + ret = ptrace_request(child, request, addr, data); + break; + } + return ret; +} + +asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) +{ + long ret = 0; + + if (test_thread_flag(TIF_SYSCALL_TRACE) && + tracehook_report_syscall_entry(regs)) + /* + * Tracing decided this syscall should not happen. + * We'll return a bogus call number to get an ENOSYS + * error, but leave the original number in regs->regs[0]. + */ + ret = -1L; + + audit_syscall_entry(regs->er1, regs->er2, regs->er3, + regs->er4, regs->er5); + + return ret ?: regs->er0; +} + +asmlinkage void do_syscall_trace_leave(struct pt_regs *regs) +{ + int step; + + audit_syscall_exit(regs); + + step = test_thread_flag(TIF_SINGLESTEP); + if (step || test_thread_flag(TIF_SYSCALL_TRACE)) + tracehook_report_syscall_exit(regs, step); +} diff --git a/arch/h8300/kernel/ptrace_h.c b/arch/h8300/kernel/ptrace_h.c new file mode 100644 index 000000000..15db45a03 --- /dev/null +++ b/arch/h8300/kernel/ptrace_h.c @@ -0,0 +1,256 @@ +/* + * ptrace cpu depend helper functions + * + * Copyright 2003, 2015 Yoshinori Sato <ysato@users.sourceforge.jp> + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file COPYING in the main directory of + * this archive for more details. + */ + +#include <linux/linkage.h> +#include <linux/sched/signal.h> +#include <asm/ptrace.h> + +#define BREAKINST 0x5730 /* trapa #3 */ + +/* disable singlestep */ +void user_disable_single_step(struct task_struct *child) +{ + if ((long)child->thread.breakinfo.addr != -1L) { + *(child->thread.breakinfo.addr) = child->thread.breakinfo.inst; + child->thread.breakinfo.addr = (unsigned short *)-1L; + } +} + +/* calculate next pc */ +enum jump_type {none, /* normal instruction */ + jabs, /* absolute address jump */ + ind, /* indirect address jump */ + ret, /* return to subrutine */ + reg, /* register indexed jump */ + relb, /* pc relative jump (byte offset) */ + relw, /* pc relative jump (word offset) */ + }; + +/* opcode decode table define + ptn: opcode pattern + msk: opcode bitmask + len: instruction length (<0 next table index) + jmp: jump operation mode */ +struct optable { + unsigned char bitpattern; + unsigned char bitmask; + signed char length; + signed char type; +} __packed __aligned(1); + +#define OPTABLE(ptn, msk, len, jmp) \ + { \ + .bitpattern = ptn, \ + .bitmask = msk, \ + .length = len, \ + .type = jmp, \ + } + +static const struct optable optable_0[] = { + OPTABLE(0x00, 0xff, 1, none), /* 0x00 */ + OPTABLE(0x01, 0xff, -1, none), /* 0x01 */ + OPTABLE(0x02, 0xfe, 1, none), /* 0x02-0x03 */ + OPTABLE(0x04, 0xee, 1, none), /* 0x04-0x05/0x14-0x15 */ + OPTABLE(0x06, 0xfe, 1, none), /* 0x06-0x07 */ + OPTABLE(0x08, 0xea, 1, none), /* 0x08-0x09/0x0c-0x0d/0x18-0x19/0x1c-0x1d */ + OPTABLE(0x0a, 0xee, 1, none), /* 0x0a-0x0b/0x1a-0x1b */ + OPTABLE(0x0e, 0xee, 1, none), /* 0x0e-0x0f/0x1e-0x1f */ + OPTABLE(0x10, 0xfc, 1, none), /* 0x10-0x13 */ + OPTABLE(0x16, 0xfe, 1, none), /* 0x16-0x17 */ + OPTABLE(0x20, 0xe0, 1, none), /* 0x20-0x3f */ + OPTABLE(0x40, 0xf0, 1, relb), /* 0x40-0x4f */ + OPTABLE(0x50, 0xfc, 1, none), /* 0x50-0x53 */ + OPTABLE(0x54, 0xfd, 1, ret), /* 0x54/0x56 */ + OPTABLE(0x55, 0xff, 1, relb), /* 0x55 */ + OPTABLE(0x57, 0xff, 1, none), /* 0x57 */ + OPTABLE(0x58, 0xfb, 2, relw), /* 0x58/0x5c */ + OPTABLE(0x59, 0xfb, 1, reg), /* 0x59/0x5b */ + OPTABLE(0x5a, 0xfb, 2, jabs), /* 0x5a/0x5e */ + OPTABLE(0x5b, 0xfb, 2, ind), /* 0x5b/0x5f */ + OPTABLE(0x60, 0xe8, 1, none), /* 0x60-0x67/0x70-0x77 */ + OPTABLE(0x68, 0xfa, 1, none), /* 0x68-0x69/0x6c-0x6d */ + OPTABLE(0x6a, 0xfe, -2, none), /* 0x6a-0x6b */ + OPTABLE(0x6e, 0xfe, 2, none), /* 0x6e-0x6f */ + OPTABLE(0x78, 0xff, 4, none), /* 0x78 */ + OPTABLE(0x79, 0xff, 2, none), /* 0x79 */ + OPTABLE(0x7a, 0xff, 3, none), /* 0x7a */ + OPTABLE(0x7b, 0xff, 2, none), /* 0x7b */ + OPTABLE(0x7c, 0xfc, 2, none), /* 0x7c-0x7f */ + OPTABLE(0x80, 0x80, 1, none), /* 0x80-0xff */ +}; + +static const struct optable optable_1[] = { + OPTABLE(0x00, 0xff, -3, none), /* 0x0100 */ + OPTABLE(0x40, 0xf0, -3, none), /* 0x0140-0x14f */ + OPTABLE(0x80, 0xf0, 1, none), /* 0x0180-0x018f */ + OPTABLE(0xc0, 0xc0, 2, none), /* 0x01c0-0x01ff */ +}; + +static const struct optable optable_2[] = { + OPTABLE(0x00, 0x20, 2, none), /* 0x6a0?/0x6a8?/0x6b0?/0x6b8? */ + OPTABLE(0x20, 0x20, 3, none), /* 0x6a2?/0x6aa?/0x6b2?/0x6ba? */ +}; + +static const struct optable optable_3[] = { + OPTABLE(0x69, 0xfb, 2, none), /* 0x010069/0x01006d/014069/0x01406d */ + OPTABLE(0x6b, 0xff, -4, none), /* 0x01006b/0x01406b */ + OPTABLE(0x6f, 0xff, 3, none), /* 0x01006f/0x01406f */ + OPTABLE(0x78, 0xff, 5, none), /* 0x010078/0x014078 */ +}; + +static const struct optable optable_4[] = { +/* 0x0100690?/0x01006d0?/0140690?/0x01406d0?/ + 0x0100698?/0x01006d8?/0140698?/0x01406d8? */ + OPTABLE(0x00, 0x78, 3, none), +/* 0x0100692?/0x01006d2?/0140692?/0x01406d2?/ + 0x010069a?/0x01006da?/014069a?/0x01406da? */ + OPTABLE(0x20, 0x78, 4, none), +}; + +static const struct optables_list { + const struct optable *ptr; + int size; +} optables[] = { +#define OPTABLES(no) \ + { \ + .ptr = optable_##no, \ + .size = sizeof(optable_##no) / sizeof(struct optable), \ + } + OPTABLES(0), + OPTABLES(1), + OPTABLES(2), + OPTABLES(3), + OPTABLES(4), + +}; + +const unsigned char condmask[] = { + 0x00, 0x40, 0x01, 0x04, 0x02, 0x08, 0x10, 0x20 +}; + +static int isbranch(struct task_struct *task, int reson) +{ + unsigned char cond = h8300_get_reg(task, PT_CCR); + + /* encode complex conditions */ + /* B4: N^V + B5: Z|(N^V) + B6: C|Z */ + __asm__("bld #3,%w0\n\t" + "bxor #1,%w0\n\t" + "bst #4,%w0\n\t" + "bor #2,%w0\n\t" + "bst #5,%w0\n\t" + "bld #2,%w0\n\t" + "bor #0,%w0\n\t" + "bst #6,%w0\n\t" + : "=&r"(cond) : "0"(cond) : "cc"); + cond &= condmask[reson >> 1]; + if (!(reson & 1)) + return cond == 0; + else + return cond != 0; +} + +static unsigned short *decode(struct task_struct *child, + const struct optable *op, + char *fetch_p, unsigned short *pc, + unsigned char inst) +{ + unsigned long addr; + unsigned long *sp; + int regno; + + switch (op->type) { + case none: + return (unsigned short *)pc + op->length; + case jabs: + addr = *(unsigned long *)pc; + return (unsigned short *)(addr & 0x00ffffff); + case ind: + addr = *pc & 0xff; + return (unsigned short *)(*(unsigned long *)addr); + case ret: + sp = (unsigned long *)h8300_get_reg(child, PT_USP); + /* user stack frames + | er0 | temporary saved + +--------+ + | exp | exception stack frames + +--------+ + | ret pc | userspace return address + */ + return (unsigned short *)(*(sp+2) & 0x00ffffff); + case reg: + regno = (*pc >> 4) & 0x07; + if (regno == 0) + addr = h8300_get_reg(child, PT_ER0); + else + addr = h8300_get_reg(child, regno-1 + PT_ER1); + return (unsigned short *)addr; + case relb: + if (inst == 0x55 || isbranch(child, inst & 0x0f)) + pc = (unsigned short *)((unsigned long)pc + + ((signed char)(*fetch_p))); + return pc+1; /* skip myself */ + case relw: + if (inst == 0x5c || isbranch(child, (*fetch_p & 0xf0) >> 4)) + pc = (unsigned short *)((unsigned long)pc + + ((signed short)(*(pc+1)))); + return pc+2; /* skip myself */ + default: + return NULL; + } +} + +static unsigned short *nextpc(struct task_struct *child, unsigned short *pc) +{ + const struct optable *op; + unsigned char *fetch_p; + int op_len; + unsigned char inst; + + op = optables[0].ptr; + op_len = optables[0].size; + fetch_p = (unsigned char *)pc; + inst = *fetch_p++; + do { + if ((inst & op->bitmask) == op->bitpattern) { + if (op->length < 0) { + op = optables[-op->length].ptr; + op_len = optables[-op->length].size + 1; + inst = *fetch_p++; + } else + return decode(child, op, fetch_p, pc, inst); + } else + op++; + } while (--op_len > 0); + return NULL; +} + +/* Set breakpoint(s) to simulate a single step from the current PC. */ + +void user_enable_single_step(struct task_struct *child) +{ + unsigned short *next; + + next = nextpc(child, (unsigned short *)h8300_get_reg(child, PT_PC)); + child->thread.breakinfo.addr = next; + child->thread.breakinfo.inst = *next; + *next = BREAKINST; +} + +asmlinkage void trace_trap(unsigned long bp) +{ + if ((unsigned long)current->thread.breakinfo.addr == bp) { + user_disable_single_step(current); + force_sig(SIGTRAP); + } else + force_sig(SIGILL); +} diff --git a/arch/h8300/kernel/ptrace_s.c b/arch/h8300/kernel/ptrace_s.c new file mode 100644 index 000000000..ee21f37b7 --- /dev/null +++ b/arch/h8300/kernel/ptrace_s.c @@ -0,0 +1,44 @@ +/* + * linux/arch/h8300/kernel/ptrace_h8s.c + * ptrace cpu depend helper functions + * + * Yoshinori Sato <ysato@users.sourceforge.jp> + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file COPYING in the main directory of + * this archive for more details. + */ + +#include <linux/linkage.h> +#include <linux/sched/signal.h> +#include <linux/errno.h> +#include <asm/ptrace.h> + +#define CCR_MASK 0x6f +#define EXR_TRACE 0x80 + +/* disable singlestep */ +void user_disable_single_step(struct task_struct *child) +{ + unsigned char exr; + + exr = h8300_get_reg(child, PT_EXR); + exr &= ~EXR_TRACE; + h8300_put_reg(child, PT_EXR, exr); +} + +/* enable singlestep */ +void user_enable_single_step(struct task_struct *child) +{ + unsigned char exr; + + exr = h8300_get_reg(child, PT_EXR); + exr |= EXR_TRACE; + h8300_put_reg(child, PT_EXR, exr); +} + +asmlinkage void trace_trap(unsigned long bp) +{ + (void)bp; + force_sig(SIGTRAP); +} diff --git a/arch/h8300/kernel/setup.c b/arch/h8300/kernel/setup.c new file mode 100644 index 000000000..0281f92ee --- /dev/null +++ b/arch/h8300/kernel/setup.c @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * linux/arch/h8300/kernel/setup.c + * + * Copyright (C) 2001-2014 Yoshinori Sato <ysato@users.sourceforge.jp> + */ + +/* + * This file handles the architecture-dependent parts of system setup + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/mm.h> +#include <linux/fs.h> +#include <linux/console.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/seq_file.h> +#include <linux/init.h> +#include <linux/of.h> +#include <linux/of_fdt.h> +#include <linux/of_address.h> +#include <linux/clk-provider.h> +#include <linux/memblock.h> +#include <linux/screen_info.h> +#include <linux/clocksource.h> + +#include <asm/setup.h> +#include <asm/irq.h> +#include <asm/sections.h> +#include <asm/page.h> + +#if defined(CONFIG_CPU_H8300H) +#define CPU "H8/300H" +#elif defined(CONFIG_CPU_H8S) +#define CPU "H8S" +#else +#define CPU "Unknown" +#endif + +unsigned long memory_start; +unsigned long memory_end; +EXPORT_SYMBOL(memory_end); +static unsigned long freq; +extern char __dtb_start[]; + +#ifdef CONFIG_VT +struct screen_info screen_info; +#endif + +char __initdata command_line[COMMAND_LINE_SIZE]; + +void sim_console_register(void); + +void __init h8300_fdt_init(void *fdt, char *bootargs) +{ + if (!fdt) + fdt = __dtb_start; + else + strcpy(command_line, bootargs); + + early_init_dt_scan(fdt); + memblock_allow_resize(); +} + +static void __init bootmem_init(void) +{ + struct memblock_region *region; + + memory_end = memory_start = 0; + + /* Find main memory where is the kernel */ + memory_start = memblock_start_of_DRAM(); + memory_end = memblock_end_of_DRAM(); + + if (!memory_end) + panic("No memory!"); + + /* setup bootmem globals (we use no_bootmem, but mm still depends on this) */ + min_low_pfn = PFN_UP(memory_start); + max_low_pfn = PFN_DOWN(memory_end); + max_pfn = max_low_pfn; + + memblock_reserve(__pa(_stext), _end - _stext); + + early_init_fdt_reserve_self(); + early_init_fdt_scan_reserved_mem(); + + memblock_dump_all(); +} + +void __init setup_arch(char **cmdline_p) +{ + unflatten_and_copy_device_tree(); + + init_mm.start_code = (unsigned long) _stext; + init_mm.end_code = (unsigned long) _etext; + init_mm.end_data = (unsigned long) _edata; + init_mm.brk = (unsigned long) 0; + + pr_notice("\r\n\nuClinux " CPU "\n"); + pr_notice("Flat model support (C) 1998,1999 Kenneth Albanowski, D. Jeff Dionne\n"); + + if (*command_line) + strcpy(boot_command_line, command_line); + *cmdline_p = boot_command_line; + + parse_early_param(); + + bootmem_init(); + /* + * get kmalloc into gear + */ + paging_init(); +} + +/* + * Get CPU information for use by the procfs. + */ + +static int show_cpuinfo(struct seq_file *m, void *v) +{ + char *cpu; + + cpu = CPU; + + seq_printf(m, "CPU:\t\t%s\n" + "Clock:\t\t%lu.%1luMHz\n" + "BogoMips:\t%lu.%02lu\n" + "Calibration:\t%lu loops\n", + cpu, + freq/1000, freq%1000, + (loops_per_jiffy*HZ)/500000, + ((loops_per_jiffy*HZ)/5000)%100, + (loops_per_jiffy*HZ)); + + return 0; +} + +static void *c_start(struct seq_file *m, loff_t *pos) +{ + return *pos < num_possible_cpus() ? + ((void *) 0x12345678) : NULL; +} + +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return c_start(m, pos); +} + +static void c_stop(struct seq_file *m, void *v) +{ +} + +const struct seq_operations cpuinfo_op = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = show_cpuinfo, +}; + +#if defined(CONFIG_CPU_H8300H) +#define get_wait(base, addr) ({ \ + int baddr; \ + baddr = ((addr) / 0x200000 * 2); \ + w *= (readw((base) + 2) & (3 << baddr)) + 1; \ + }) +#endif +#if defined(CONFIG_CPU_H8S) +#define get_wait(base, addr) ({ \ + int baddr; \ + baddr = ((addr) / 0x200000 * 16); \ + w *= (readl((base) + 2) & (7 << baddr)) + 1; \ + }) +#endif + +static __init int access_timing(void) +{ + struct device_node *bsc; + void __iomem *base; + unsigned long addr = (unsigned long)&__delay; + int bit = 1 << (addr / 0x200000); + int w; + + bsc = of_find_compatible_node(NULL, NULL, "renesas,h8300-bsc"); + base = of_iomap(bsc, 0); + w = (readb(base + 0) & bit)?2:1; + if (readb(base + 1) & bit) + w *= get_wait(base, addr); + else + w *= 2; + return w * 3 / 2; +} + +void __init calibrate_delay(void) +{ + struct device_node *cpu; + int freq; + + cpu = of_find_compatible_node(NULL, NULL, "renesas,h8300"); + of_property_read_s32(cpu, "clock-frequency", &freq); + loops_per_jiffy = freq / HZ / (access_timing() * 2); + pr_cont("%lu.%02lu BogoMIPS (lpj=%lu)\n", + loops_per_jiffy / (500000 / HZ), + (loops_per_jiffy / (5000 / HZ)) % 100, loops_per_jiffy); +} + + +void __init time_init(void) +{ + of_clk_init(NULL); + timer_probe(); +} diff --git a/arch/h8300/kernel/signal.c b/arch/h8300/kernel/signal.c new file mode 100644 index 000000000..75a1c36b1 --- /dev/null +++ b/arch/h8300/kernel/signal.c @@ -0,0 +1,287 @@ +/* + * linux/arch/h8300/kernel/signal.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +/* + * uClinux H8/300 support by Yoshinori Sato <ysato@users.sourceforge.jp> + * and David McCullough <davidm@snapgear.com> + * + * Based on + * Linux/m68k by Hamish Macdonald + */ + +/* + * ++roman (07/09/96): implemented signal stacks (specially for tosemu on + * Atari :-) Current limitation: Only one sigstack can be active at one time. + * If a second signal with SA_ONSTACK set arrives while working on a sigstack, + * SA_ONSTACK is ignored. This behaviour avoids lots of trouble with nested + * signal handlers! + */ + +#include <linux/sched.h> +#include <linux/sched/task_stack.h> +#include <linux/mm.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/syscalls.h> +#include <linux/errno.h> +#include <linux/wait.h> +#include <linux/ptrace.h> +#include <linux/unistd.h> +#include <linux/stddef.h> +#include <linux/highuid.h> +#include <linux/personality.h> +#include <linux/tty.h> +#include <linux/binfmts.h> +#include <linux/tracehook.h> + +#include <asm/setup.h> +#include <linux/uaccess.h> +#include <asm/traps.h> +#include <asm/ucontext.h> + +/* + * Do a signal return; undo the signal stack. + * + * Keep the return code on the stack quadword aligned! + * That makes the cache flush below easier. + */ + +struct rt_sigframe { + long dummy_er0; + long dummy_vector; +#if defined(CONFIG_CPU_H8S) + short dummy_exr; +#endif + long dummy_pc; + char *pretcode; + struct siginfo *pinfo; + void *puc; + unsigned char retcode[8]; + struct siginfo info; + struct ucontext uc; + int sig; +} __packed __aligned(2); + +static inline int +restore_sigcontext(struct sigcontext *usc, int *pd0) +{ + struct pt_regs *regs = current_pt_regs(); + int err = 0; + unsigned int ccr; + unsigned int usp; + unsigned int er0; + + /* Always make any pending restarted system calls return -EINTR */ + current->restart_block.fn = do_no_restart_syscall; + + /* restore passed registers */ +#define COPY(r) do { err |= get_user(regs->r, &usc->sc_##r); } while (0) + COPY(er1); + COPY(er2); + COPY(er3); + COPY(er5); + COPY(pc); + ccr = regs->ccr & 0x10; + COPY(ccr); +#undef COPY + regs->ccr &= 0xef; + regs->ccr |= ccr; + regs->orig_er0 = -1; /* disable syscall checks */ + err |= __get_user(usp, &usc->sc_usp); + regs->sp = usp; + + err |= __get_user(er0, &usc->sc_er0); + *pd0 = er0; + return err; +} + +asmlinkage int sys_rt_sigreturn(void) +{ + unsigned long usp = rdusp(); + struct rt_sigframe *frame = (struct rt_sigframe *)(usp - 4); + sigset_t set; + int er0; + + if (!access_ok(frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) + goto badframe; + + set_current_blocked(&set); + + if (restore_sigcontext(&frame->uc.uc_mcontext, &er0)) + goto badframe; + + if (restore_altstack(&frame->uc.uc_stack)) + goto badframe; + + return er0; + +badframe: + force_sig(SIGSEGV); + return 0; +} + +static int setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, + unsigned long mask) +{ + int err = 0; + + err |= __put_user(regs->er0, &sc->sc_er0); + err |= __put_user(regs->er1, &sc->sc_er1); + err |= __put_user(regs->er2, &sc->sc_er2); + err |= __put_user(regs->er3, &sc->sc_er3); + err |= __put_user(regs->er4, &sc->sc_er4); + err |= __put_user(regs->er5, &sc->sc_er5); + err |= __put_user(regs->er6, &sc->sc_er6); + err |= __put_user(rdusp(), &sc->sc_usp); + err |= __put_user(regs->pc, &sc->sc_pc); + err |= __put_user(regs->ccr, &sc->sc_ccr); + err |= __put_user(mask, &sc->sc_mask); + + return err; +} + +static inline void __user * +get_sigframe(struct ksignal *ksig, struct pt_regs *regs, size_t frame_size) +{ + return (void __user *)((sigsp(rdusp(), ksig) - frame_size) & -8UL); +} + +static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, + struct pt_regs *regs) +{ + struct rt_sigframe *frame; + int err = 0; + unsigned char *ret; + + frame = get_sigframe(ksig, regs, sizeof(*frame)); + + if (!access_ok(frame, sizeof(*frame))) + return -EFAULT; + + if (ksig->ka.sa.sa_flags & SA_SIGINFO) + err |= copy_siginfo_to_user(&frame->info, &ksig->info); + + /* Create the ucontext. */ + err |= __put_user(0, &frame->uc.uc_flags); + err |= __put_user(0, &frame->uc.uc_link); + err |= __save_altstack(&frame->uc.uc_stack, rdusp()); + err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0]); + err |= copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + if (err) + return -EFAULT; + + /* Set up to return from userspace. */ + ret = (unsigned char *)&frame->retcode; + if (ksig->ka.sa.sa_flags & SA_RESTORER) + ret = (unsigned char *)(ksig->ka.sa.sa_restorer); + else { + /* sub.l er0,er0; mov.b #__NR_rt_sigreturn,r0l; trapa #0 */ + err |= __put_user(0x1a80f800 + (__NR_rt_sigreturn & 0xff), + (unsigned long *)(frame->retcode + 0)); + err |= __put_user(0x5700, + (unsigned short *)(frame->retcode + 4)); + } + err |= __put_user(ret, &frame->pretcode); + + if (err) + return -EFAULT; + + /* Set up registers for signal handler */ + regs->sp = (unsigned long)frame; + regs->pc = (unsigned long)ksig->ka.sa.sa_handler; + regs->er0 = ksig->sig; + regs->er1 = (unsigned long)&(frame->info); + regs->er2 = (unsigned long)&frame->uc; + regs->er5 = current->mm->start_data; /* GOT base */ + + return 0; +} + +static void +handle_restart(struct pt_regs *regs, struct k_sigaction *ka) +{ + switch (regs->er0) { + case -ERESTARTNOHAND: + if (!ka) + goto do_restart; + regs->er0 = -EINTR; + break; + case -ERESTART_RESTARTBLOCK: + if (!ka) { + regs->er0 = __NR_restart_syscall; + regs->pc -= 2; + } else + regs->er0 = -EINTR; + break; + case -ERESTARTSYS: + if (!(ka->sa.sa_flags & SA_RESTART)) { + regs->er0 = -EINTR; + break; + } + fallthrough; + case -ERESTARTNOINTR: +do_restart: + regs->er0 = regs->orig_er0; + regs->pc -= 2; + break; + } +} + +/* + * OK, we're invoking a handler + */ +static void +handle_signal(struct ksignal *ksig, struct pt_regs *regs) +{ + sigset_t *oldset = sigmask_to_save(); + int ret; + /* are we from a system call? */ + if (regs->orig_er0 >= 0) + handle_restart(regs, &ksig->ka); + + ret = setup_rt_frame(ksig, oldset, regs); + + signal_setup_done(ret, ksig, 0); +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + */ +static void do_signal(struct pt_regs *regs) +{ + struct ksignal ksig; + + current->thread.esp0 = (unsigned long) regs; + + if (get_signal(&ksig)) { + /* Whee! Actually deliver the signal. */ + handle_signal(&ksig, regs); + return; + } + /* Did we come from a system call? */ + if (regs->orig_er0 >= 0) + handle_restart(regs, NULL); + + /* If there's no signal to deliver, we just restore the saved mask. */ + restore_saved_sigmask(); +} + +asmlinkage void do_notify_resume(struct pt_regs *regs, u32 thread_info_flags) +{ + if (thread_info_flags & (_TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL)) + do_signal(regs); + + if (thread_info_flags & _TIF_NOTIFY_RESUME) + tracehook_notify_resume(regs); +} diff --git a/arch/h8300/kernel/sim-console.c b/arch/h8300/kernel/sim-console.c new file mode 100644 index 000000000..03aa35b1a --- /dev/null +++ b/arch/h8300/kernel/sim-console.c @@ -0,0 +1,31 @@ +/* + * arch/h8300/kernel/sim-console.c + * + * Copyright (C) 2015 Yoshinori Sato <ysato@users.sourceforge.jp> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include <linux/console.h> +#include <linux/init.h> +#include <linux/serial_core.h> + +static void sim_write(struct console *con, const char *s, unsigned n) +{ + register const char *_ptr __asm__("er1") = s; + register const unsigned _len __asm__("er2") = n; + + __asm__("sub.l er0,er0\n\t" /* er0 = 1 (stdout) */ + "inc.l #1,er0\n\t" + ".byte 0x5e,0x00,0x00,0xc7\n\t" /* jsr @0xc7 (sys_write) */ + : : "g"(_ptr), "g"(_len):"er0"); +} + +static int __init sim_setup(struct earlycon_device *device, const char *opt) +{ + device->con->write = sim_write; + return 0; +} + +EARLYCON_DECLARE(h8sim, sim_setup); diff --git a/arch/h8300/kernel/syscalls.c b/arch/h8300/kernel/syscalls.c new file mode 100644 index 000000000..9022036fb --- /dev/null +++ b/arch/h8300/kernel/syscalls.c @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <linux/syscalls.h> +#include <linux/signal.h> +#include <linux/unistd.h> + +#undef __SYSCALL +#define __SYSCALL(nr, call) [nr] = (call), + +#define sys_mmap2 sys_mmap_pgoff + +asmlinkage int sys_rt_sigreturn(void); + +void *_sys_call_table[__NR_syscalls] = { +#include <asm/unistd.h> +}; diff --git a/arch/h8300/kernel/traps.c b/arch/h8300/kernel/traps.c new file mode 100644 index 000000000..cf23ccb50 --- /dev/null +++ b/arch/h8300/kernel/traps.c @@ -0,0 +1,160 @@ +/* + * linux/arch/h8300/boot/traps.c -- general exception handling code + * H8/300 support Yoshinori Sato <ysato@users.sourceforge.jp> + * + * Cloned from Linux/m68k. + * + * No original Copyright holder listed, + * Probable original (C) Roman Zippel (assigned DJD, 1999) + * + * Copyright 1999-2000 D. Jeff Dionne, <jeff@rt-control.com> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/sched/debug.h> +#include <linux/sched/task.h> +#include <linux/mm_types.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/bug.h> + +#include <asm/irq.h> +#include <asm/traps.h> +#include <asm/page.h> + +static DEFINE_SPINLOCK(die_lock); + +/* + * this must be called very early as the kernel might + * use some instruction that are emulated on the 060 + */ + +void __init base_trap_init(void) +{ +} + +void __init trap_init(void) +{ +} + +asmlinkage void set_esp0(unsigned long ssp) +{ + current->thread.esp0 = ssp; +} + +/* + * Generic dumping code. Used for panic and debug. + */ + +static void dump(struct pt_regs *fp) +{ + unsigned long *sp; + unsigned char *tp; + int i; + + pr_info("\nCURRENT PROCESS:\n\n"); + pr_info("COMM=%s PID=%d\n", current->comm, current->pid); + if (current->mm) { + pr_info("TEXT=%08x-%08x DATA=%08x-%08x BSS=%08x-%08x\n", + (int) current->mm->start_code, + (int) current->mm->end_code, + (int) current->mm->start_data, + (int) current->mm->end_data, + (int) current->mm->end_data, + (int) current->mm->brk); + pr_info("USER-STACK=%08x KERNEL-STACK=%08lx\n\n", + (int) current->mm->start_stack, + (int) PAGE_SIZE+(unsigned long)current); + } + + show_regs(fp); + pr_info("\nCODE:"); + tp = ((unsigned char *) fp->pc) - 0x20; + for (sp = (unsigned long *) tp, i = 0; (i < 0x40); i += 4) { + if ((i % 0x10) == 0) + pr_info("\n%08x: ", (int) (tp + i)); + pr_info("%08x ", (int) *sp++); + } + pr_info("\n"); + + pr_info("\nKERNEL STACK:"); + tp = ((unsigned char *) fp) - 0x40; + for (sp = (unsigned long *) tp, i = 0; (i < 0xc0); i += 4) { + if ((i % 0x10) == 0) + pr_info("\n%08x: ", (int) (tp + i)); + pr_info("%08x ", (int) *sp++); + } + pr_info("\n"); + if (STACK_MAGIC != *(unsigned long *)((unsigned long)current+PAGE_SIZE)) + pr_info("(Possibly corrupted stack page??)\n"); + + pr_info("\n\n"); +} + +void die(const char *str, struct pt_regs *fp, unsigned long err) +{ + static int diecount; + + oops_enter(); + + console_verbose(); + spin_lock_irq(&die_lock); + report_bug(fp->pc, fp); + pr_crit("%s: %04lx [#%d] ", str, err & 0xffff, ++diecount); + dump(fp); + + spin_unlock_irq(&die_lock); + make_task_dead(SIGSEGV); +} + +static int kstack_depth_to_print = 24; + +void show_stack(struct task_struct *task, unsigned long *esp, const char *loglvl) +{ + unsigned long *stack, addr; + int i; + + if (esp == NULL) + esp = (unsigned long *) &esp; + + stack = esp; + + printk("%sStack from %08lx:", loglvl, (unsigned long)stack); + for (i = 0; i < kstack_depth_to_print; i++) { + if (((unsigned long)stack & (THREAD_SIZE - 1)) >= + THREAD_SIZE-4) + break; + if (i % 8 == 0) + printk("%s ", loglvl); + pr_cont(" %08lx", *stack++); + } + + printk("%s\nCall Trace:\n", loglvl); + i = 0; + stack = esp; + while (((unsigned long)stack & (THREAD_SIZE - 1)) < THREAD_SIZE-4) { + addr = *stack++; + /* + * If the address is either in the text segment of the + * kernel, or in the region which contains vmalloc'ed + * memory, it *may* be the address of a calling + * routine; if so, print it so that someone tracing + * down the cause of the crash will be able to figure + * out the call path that was taken. + */ + if (check_kernel_text(addr)) { + if (i % 4 == 0) + printk("%s ", loglvl); + pr_cont(" [<%08lx>]", addr); + i++; + } + } + printk("%s\n", loglvl); +} diff --git a/arch/h8300/kernel/vmlinux.lds.S b/arch/h8300/kernel/vmlinux.lds.S new file mode 100644 index 000000000..6b1afc2f9 --- /dev/null +++ b/arch/h8300/kernel/vmlinux.lds.S @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#define RO_EXCEPTION_TABLE_ALIGN 16 + +#include <asm-generic/vmlinux.lds.h> +#include <asm/page.h> +#include <asm/thread_info.h> + +#define ROMTOP 0x000000 +#define RAMTOP 0x400000 + +jiffies = jiffies_64 + 4; + +ENTRY(_start) + +SECTIONS +{ +#if defined(CONFIG_ROMKERNEL) + . = ROMTOP; + .vectors : + { + _vector = . ; + *(.vector*) + } +#else + . = RAMTOP; + _ramstart = .; + . = . + CONFIG_OFFSET; +#endif + _text = .; + HEAD_TEXT_SECTION + .text : { + _stext = . ; + TEXT_TEXT + SCHED_TEXT + CPUIDLE_TEXT + LOCK_TEXT +#if defined(CONFIG_ROMKERNEL) + *(.int_redirect) +#endif + _etext = . ; + } + RO_DATA(4) + ROMEND = .; +#if defined(CONFIG_ROMKERNEL) + . = RAMTOP; + _ramstart = .; +#define ADDR(x) ROMEND +#endif + _sdata = . ; + __data_start = . ; + RW_DATA(0, PAGE_SIZE, THREAD_SIZE) +#if defined(CONFIG_ROMKERNEL) +#undef ADDR +#endif + . = ALIGN(0x4) ; + __init_begin = .; + INIT_TEXT_SECTION(4) + INIT_DATA_SECTION(4) + __init_end = .; + _edata = . ; + _begin_data = LOADADDR(.data); + _sbss =.; + BSS_SECTION(0, 0 ,0) + _ebss =.; + _ramend = .; + _end = .; + DISCARDS +} |