Appana Durga Kedareswara raomicroblaze: Add support for error injectionadc4cef4 years agoHistory
1311 lines · 34.2 KB · Assembly
RawDownload
/* * Low-level system-call handling, trap handlers and context-switching * * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu> * Copyright (C) 2008-2009 PetaLogix * Copyright (C) 2003		John Williams <jwilliams@itee.uq.edu.au> * Copyright (C) 2001,2002	NEC Corporation * Copyright (C) 2001,2002	Miles Bader <miles@gnu.org> * * 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. * * Written by Miles Bader <miles@gnu.org> * Heavily modified by John Williams for Microblaze */ #include <linux/sys.h>#include <linux/linkage.h> #include <asm/entry.h>#include <asm/current.h>#include <asm/processor.h>#include <asm/exceptions.h>#include <asm/asm-offsets.h>#include <asm/thread_info.h> #include <asm/page.h>#include <asm/unistd.h>#include <asm/xilinx_mb_manager.h> #include <linux/errno.h>#include <asm/signal.h>#include <asm/mmu.h> #undef DEBUG #ifdef DEBUG/* Create space for syscalls counting. */.section .data.global syscall_debug_table.align 4syscall_debug_table:	.space	(__NR_syscalls * 4)#endif /* DEBUG */ #define C_ENTRY(name)	.globl name; .align 4; name /* * Various ways of setting and clearing BIP in flags reg. * This is mucky, but necessary using microblaze version that * allows msr ops to write to BIP */#if CONFIG_XILINX_MICROBLAZE0_USE_MSR_INSTR	.macro	clear_bip	msrclr	r0, MSR_BIP	.endm 	.macro	set_bip	msrset	r0, MSR_BIP	.endm 	.macro	clear_eip	msrclr	r0, MSR_EIP	.endm 	.macro	set_ee	msrset	r0, MSR_EE	.endm 	.macro	disable_irq	msrclr	r0, MSR_IE	.endm 	.macro	enable_irq	msrset	r0, MSR_IE	.endm 	.macro	set_ums	msrset	r0, MSR_UMS	msrclr	r0, MSR_VMS	.endm 	.macro	set_vms	msrclr	r0, MSR_UMS	msrset	r0, MSR_VMS	.endm 	.macro	clear_ums	msrclr	r0, MSR_UMS	.endm 	.macro	clear_vms_ums	msrclr	r0, MSR_VMS | MSR_UMS	.endm#else	.macro	clear_bip	mfs	r11, rmsr	andi	r11, r11, ~MSR_BIP	mts	rmsr, r11	.endm 	.macro	set_bip	mfs	r11, rmsr	ori	r11, r11, MSR_BIP	mts	rmsr, r11	.endm 	.macro	clear_eip	mfs	r11, rmsr	andi	r11, r11, ~MSR_EIP	mts	rmsr, r11	.endm 	.macro	set_ee	mfs	r11, rmsr	ori	r11, r11, MSR_EE	mts	rmsr, r11	.endm 	.macro	disable_irq	mfs	r11, rmsr	andi	r11, r11, ~MSR_IE	mts	rmsr, r11	.endm 	.macro	enable_irq	mfs	r11, rmsr	ori	r11, r11, MSR_IE	mts	rmsr, r11	.endm 	.macro set_ums	mfs	r11, rmsr	ori	r11, r11, MSR_VMS	andni	r11, r11, MSR_UMS	mts	rmsr, r11	.endm 	.macro	set_vms	mfs	r11, rmsr	ori	r11, r11, MSR_VMS	andni	r11, r11, MSR_UMS	mts	rmsr, r11	.endm 	.macro	clear_ums	mfs	r11, rmsr	andni	r11, r11, MSR_UMS	mts	rmsr,r11	.endm 	.macro	clear_vms_ums	mfs	r11, rmsr	andni	r11, r11, (MSR_VMS|MSR_UMS)	mts	rmsr,r11	.endm#endif /* Define how to call high-level functions. With MMU, virtual mode must be * enabled when calling the high-level function. Clobbers R11. * VM_ON, VM_OFF, DO_JUMP_BIPCLR, DO_CALL */ /* turn on virtual protected mode save */#define VM_ON		\	set_ums;	\	rted	r0, 2f;	\	nop; \2: /* turn off virtual protected mode save and user mode save*/#define VM_OFF			\	clear_vms_ums;		\	rted	r0, TOPHYS(1f);	\	nop; \1: #define SAVE_REGS \	swi	r2, r1, PT_R2;	/* Save SDA */			\	swi	r3, r1, PT_R3;					\	swi	r4, r1, PT_R4;					\	swi	r5, r1, PT_R5;					\	swi	r6, r1, PT_R6;					\	swi	r7, r1, PT_R7;					\	swi	r8, r1, PT_R8;					\	swi	r9, r1, PT_R9;					\	swi	r10, r1, PT_R10;					\	swi	r11, r1, PT_R11;	/* save clobbered regs after rval */\	swi	r12, r1, PT_R12;					\	swi	r13, r1, PT_R13;	/* Save SDA2 */			\	swi	r14, r1, PT_PC;	/* PC, before IRQ/trap */	\	swi	r15, r1, PT_R15;	/* Save LP */			\	swi	r16, r1, PT_R16;					\	swi	r17, r1, PT_R17;					\	swi	r18, r1, PT_R18;	/* Save asm scratch reg */	\	swi	r19, r1, PT_R19;					\	swi	r20, r1, PT_R20;					\	swi	r21, r1, PT_R21;					\	swi	r22, r1, PT_R22;					\	swi	r23, r1, PT_R23;					\	swi	r24, r1, PT_R24;					\	swi	r25, r1, PT_R25;					\	swi	r26, r1, PT_R26;					\	swi	r27, r1, PT_R27;					\	swi	r28, r1, PT_R28;					\	swi	r29, r1, PT_R29;					\	swi	r30, r1, PT_R30;					\	swi	r31, r1, PT_R31;	/* Save current task reg */	\	mfs	r11, rmsr;		/* save MSR */			\	swi	r11, r1, PT_MSR; #define RESTORE_REGS_GP \	lwi	r2, r1, PT_R2;	/* restore SDA */		\	lwi	r3, r1, PT_R3;					\	lwi	r4, r1, PT_R4;					\	lwi	r5, r1, PT_R5;					\	lwi	r6, r1, PT_R6;					\	lwi	r7, r1, PT_R7;					\	lwi	r8, r1, PT_R8;					\	lwi	r9, r1, PT_R9;					\	lwi	r10, r1, PT_R10;					\	lwi	r11, r1, PT_R11;	/* restore clobbered regs after rval */\	lwi	r12, r1, PT_R12;					\	lwi	r13, r1, PT_R13;	/* restore SDA2 */		\	lwi	r14, r1, PT_PC;	/* RESTORE_LINK PC, before IRQ/trap */\	lwi	r15, r1, PT_R15;	/* restore LP */		\	lwi	r16, r1, PT_R16;					\	lwi	r17, r1, PT_R17;					\	lwi	r18, r1, PT_R18;	/* restore asm scratch reg */	\	lwi	r19, r1, PT_R19;					\	lwi	r20, r1, PT_R20;					\	lwi	r21, r1, PT_R21;					\	lwi	r22, r1, PT_R22;					\	lwi	r23, r1, PT_R23;					\	lwi	r24, r1, PT_R24;					\	lwi	r25, r1, PT_R25;					\	lwi	r26, r1, PT_R26;					\	lwi	r27, r1, PT_R27;					\	lwi	r28, r1, PT_R28;					\	lwi	r29, r1, PT_R29;					\	lwi	r30, r1, PT_R30;					\	lwi	r31, r1, PT_R31;	/* Restore cur task reg */ #define RESTORE_REGS \	lwi	r11, r1, PT_MSR;					\	mts	rmsr , r11;						\	RESTORE_REGS_GP #define RESTORE_REGS_RTBD \	lwi	r11, r1, PT_MSR;					\	andni	r11, r11, MSR_EIP;          /* clear EIP */             \	ori	r11, r11, MSR_EE | MSR_BIP; /* set EE and BIP */        \	mts	rmsr , r11;						\	RESTORE_REGS_GP #define SAVE_STATE	\	swi	r1, r0, TOPHYS(PER_CPU(ENTRY_SP)); /* save stack */	\	/* See if already in kernel mode.*/				\	mfs	r1, rmsr;						\	andi	r1, r1, MSR_UMS;					\	bnei	r1, 1f;						\	/* Kernel-mode state save.  */					\	/* Reload kernel stack-ptr. */					\	lwi	r1, r0, TOPHYS(PER_CPU(ENTRY_SP));			\	/* FIXME: I can add these two lines to one */			\	/* tophys(r1,r1); */						\	/* addik	r1, r1, -PT_SIZE; */				\	addik	r1, r1, CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - PT_SIZE; \	SAVE_REGS							\	brid	2f;							\	swi	r1, r1, PT_MODE; 	 				\1:	/* User-mode state save.  */					\	lwi	r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */\	tophys(r1,r1);							\	lwi	r1, r1, TS_THREAD_INFO;	/* get the thread info */	\	/* MS these three instructions can be added to one */		\	/* addik	r1, r1, THREAD_SIZE; */				\	/* tophys(r1,r1); */						\	/* addik	r1, r1, -PT_SIZE; */			\	addik r1, r1, THREAD_SIZE + CONFIG_KERNEL_BASE_ADDR - CONFIG_KERNEL_START - PT_SIZE; \	SAVE_REGS							\	lwi	r11, r0, TOPHYS(PER_CPU(ENTRY_SP));			\	swi	r11, r1, PT_R1; /* Store user SP.  */		\	swi	r0, r1, PT_MODE; /* Was in user-mode.  */		\	/* MS: I am clearing UMS even in case when I come from kernel space */ \	clear_ums; 							\2:	lwi	CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); .text .extern cpuinfo C_ENTRY(mb_flush_dcache):	addik	r1, r1, -PT_SIZE	SAVE_REGS 	addik	r3, r0, cpuinfo	lwi	r7, r3, CI_DCS	lwi	r8, r3, CI_DCL	sub	r9, r7, r81:	wdc.flush r9, r0	bgtid	r9, 1b	addk	r9, r9, r8 	RESTORE_REGS	addik	r1, r1, PT_SIZE	rtsd	r15, 8	nop C_ENTRY(mb_invalidate_icache):	addik	r1, r1, -PT_SIZE	SAVE_REGS 	addik	r3, r0, cpuinfo	lwi	r7, r3, CI_ICS	lwi	r8, r3, CI_ICL	sub	r9, r7, r81:	wic 	r9, r0	bgtid	r9, 1b	addk	r9, r9, r8 	RESTORE_REGS	addik	r1, r1, PT_SIZE	rtsd	r15, 8	nop /* * User trap. * * System calls are handled here. * * Syscall protocol: * Syscall number in r12, args in r5-r10 * Return value in r3 * * Trap entered via brki instruction, so BIP bit is set, and interrupts * are masked. This is nice, means we don't have to CLI before state save */C_ENTRY(_user_exception):	swi	r1, r0, TOPHYS(PER_CPU(ENTRY_SP)) /* save stack */	addi	r14, r14, 4	/* return address is 4 byte after call */ 	lwi	r1, r0, TOPHYS(PER_CPU(CURRENT_SAVE)); /* get saved current */	tophys(r1,r1);	lwi	r1, r1, TS_THREAD_INFO;	/* get stack from task_struct *//* calculate kernel stack pointer from task struct 8k */	addik	r1, r1, THREAD_SIZE;	tophys(r1,r1); 	addik	r1, r1, -PT_SIZE; /* Make room on the stack.  */	SAVE_REGS	swi	r0, r1, PT_R3	swi	r0, r1, PT_R4 	swi	r0, r1, PT_MODE;			/* Was in user-mode. */	lwi	r11, r0, TOPHYS(PER_CPU(ENTRY_SP));	swi	r11, r1, PT_R1;		/* Store user SP.  */	clear_ums;2:	lwi	CURRENT_TASK, r0, TOPHYS(PER_CPU(CURRENT_SAVE));	/* Save away the syscall number.  */	swi	r12, r1, PT_R0;	tovirt(r1,r1) /* where the trap should return need -8 to adjust for rtsd r15, 8*//* Jump to the appropriate function for the system call number in r12 * (r12 is not preserved), or return an error if r12 is not valid. The LP * register should point to the location where * the called function should return.  [note that MAKE_SYS_CALL uses label 1] */ 	/* Step into virtual mode */	rtbd	r0, 3f	nop3:	lwi	r11, CURRENT_TASK, TS_THREAD_INFO /* get thread info */	lwi	r11, r11, TI_FLAGS	 /* get flags in thread info */	andi	r11, r11, _TIF_WORK_SYSCALL_MASK	beqi	r11, 4f 	addik	r3, r0, -ENOSYS	swi	r3, r1, PT_R3	brlid	r15, do_syscall_trace_enter	addik	r5, r1, PT_R0 	# do_syscall_trace_enter returns the new syscall nr.	addk	r12, r0, r3	lwi	r5, r1, PT_R5;	lwi	r6, r1, PT_R6;	lwi	r7, r1, PT_R7;	lwi	r8, r1, PT_R8;	lwi	r9, r1, PT_R9;	lwi	r10, r1, PT_R10;4:/* Jump to the appropriate function for the system call number in r12 * (r12 is not preserved), or return an error if r12 is not valid. * The LP register should point to the location where the called function * should return.  [note that MAKE_SYS_CALL uses label 1] */	/* See if the system call number is valid */	blti	r12, 5f	addi	r11, r12, -__NR_syscalls;	bgei	r11, 5f;	/* Figure out which function to use for this system call.  */	/* Note Microblaze barrel shift is optional, so don't rely on it */	add	r12, r12, r12;			/* convert num -> ptr */	add	r12, r12, r12;	addi	r30, r0, 1			/* restarts allowed */ #ifdef DEBUG	/* Trac syscalls and stored them to syscall_debug_table */	/* The first syscall location stores total syscall number */	lwi	r3, r0, syscall_debug_table	addi	r3, r3, 1	swi	r3, r0, syscall_debug_table	lwi	r3, r12, syscall_debug_table	addi	r3, r3, 1	swi	r3, r12, syscall_debug_table#endif 	# Find and jump into the syscall handler.	lwi	r12, r12, sys_call_table	/* where the trap should return need -8 to adjust for rtsd r15, 8 */	addi	r15, r0, ret_from_trap-8	bra	r12 	/* The syscall number is invalid, return an error.  */5:	braid	ret_from_trap	addi	r3, r0, -ENOSYS; /* Entry point used to return from a syscall/trap *//* We re-enable BIP bit before state restore */C_ENTRY(ret_from_trap):	swi	r3, r1, PT_R3	swi	r4, r1, PT_R4 	lwi	r11, r1, PT_MODE;/* See if returning to kernel mode, if so, skip resched &c.  */	bnei	r11, 2f;	/* We're returning to user mode, so check for various conditions that	 * trigger rescheduling. */	/* FIXME: Restructure all these flag checks. */	lwi	r11, CURRENT_TASK, TS_THREAD_INFO;	/* get thread info */	lwi	r11, r11, TI_FLAGS;		/* get flags in thread info */	andi	r11, r11, _TIF_WORK_SYSCALL_MASK	beqi	r11, 1f 	brlid	r15, do_syscall_trace_leave	addik	r5, r1, PT_R01:	/* We're returning to user mode, so check for various conditions that	 * trigger rescheduling. */	/* get thread info from current task */	lwi	r11, CURRENT_TASK, TS_THREAD_INFO;	lwi	r19, r11, TI_FLAGS;		/* get flags in thread info */	andi	r11, r19, _TIF_NEED_RESCHED;	beqi	r11, 5f; 	bralid	r15, schedule;	/* Call scheduler */	nop;				/* delay slot */	bri	1b 	/* Maybe handle a signal */5:	andi	r11, r19, _TIF_SIGPENDING | _TIF_NOTIFY_RESUME;	beqi	r11, 4f;		/* Signals to handle, handle them */ 	addik	r5, r1, 0;		/* Arg 1: struct pt_regs *regs */	bralid	r15, do_notify_resume;	/* Handle any signals */	add	r6, r30, r0;		/* Arg 2: int in_syscall */	add	r30, r0, r0		/* no more restarts */	bri	1b /* Finally, return to user state.  */4:	set_bip;			/*  Ints masked for state restore */	swi	CURRENT_TASK, r0, PER_CPU(CURRENT_SAVE); /* save current */	VM_OFF;	tophys(r1,r1);	RESTORE_REGS_RTBD;	addik	r1, r1, PT_SIZE		/* Clean up stack space.  */	lwi	r1, r1, PT_R1 - PT_SIZE;/* Restore user stack pointer. */	bri	6f; /* Return to kernel state.  */2:	set_bip;			/*  Ints masked for state restore */	VM_OFF;	tophys(r1,r1);	RESTORE_REGS_RTBD;	addik	r1, r1, PT_SIZE		/* Clean up stack space.  */	tovirt(r1,r1);6:TRAP_return:		/* Make global symbol for debugging */	rtbd	r14, 0;	/* Instructions to return from an IRQ */	nop;  /* This the initial entry point for a new child thread, with an appropriate   stack in place that makes it look like the child is in the middle of a   syscall.  This function is actually `returned to' from switch_thread

Lines 1–500 of 1311.