This did corrupt register s0 which the caller of self_ipi expects to be unchanged. This is a kernel bug which will only be triggered with the compilers which compile __smtc_ipi_replay to use s0 across the invocation of self_ipi. Gcc 4.1.2 does this, for example. Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
		
			
				
	
	
		
			130 lines
		
	
	
	
		
			3.5 KiB
			
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
			
		
		
	
	
			130 lines
		
	
	
	
		
			3.5 KiB
			
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
/*
 | 
						|
 * Assembly Language Functions for MIPS MT SMTC support
 | 
						|
 */
 | 
						|
 | 
						|
/*
 | 
						|
 * This file should be built into the kernel only if CONFIG_MIPS_MT_SMTC is set. */
 | 
						|
 | 
						|
#include <asm/regdef.h>
 | 
						|
#include <asm/asmmacro.h>
 | 
						|
#include <asm/stackframe.h>
 | 
						|
#include <asm/irqflags.h>
 | 
						|
 | 
						|
/*
 | 
						|
 * "Software Interrupt" linkage.
 | 
						|
 *
 | 
						|
 * This is invoked when an "Interrupt" is sent from one TC to another,
 | 
						|
 * where the TC to be interrupted is halted, has it's Restart address
 | 
						|
 * and Status values saved by the "remote control" thread, then modified
 | 
						|
 * to cause execution to begin here, in kenel mode. This code then
 | 
						|
 * disguises the TC state as that of an exception and transfers
 | 
						|
 * control to the general exception or vectored interrupt handler.
 | 
						|
 */
 | 
						|
	.set noreorder
 | 
						|
 | 
						|
/*
 | 
						|
The __smtc_ipi_vector would use k0 and k1 as temporaries and
 | 
						|
1) Set EXL (this is per-VPE, so this can't be done by proxy!)
 | 
						|
2) Restore the K/CU and IXMT bits to the pre "exception" state
 | 
						|
   (EXL means no interrupts and access to the kernel map).
 | 
						|
3) Set EPC to be the saved value of TCRestart.
 | 
						|
4) Jump to the exception handler entry point passed by the sender.
 | 
						|
 | 
						|
CAN WE PROVE THAT WE WON'T DO THIS IF INTS DISABLED??
 | 
						|
*/
 | 
						|
 | 
						|
/*
 | 
						|
 * Reviled and slandered vision: Set EXL and restore K/CU/IXMT
 | 
						|
 * state of pre-halt thread, then save everything and call
 | 
						|
 * thought some function pointer to imaginary_exception, which
 | 
						|
 * will parse a register value or memory message queue to
 | 
						|
 * deliver things like interprocessor interrupts. On return
 | 
						|
 * from that function, jump to the global ret_from_irq code
 | 
						|
 * to invoke the scheduler and return as appropriate.
 | 
						|
 */
 | 
						|
 | 
						|
#define PT_PADSLOT4 (PT_R0-8)
 | 
						|
#define PT_PADSLOT5 (PT_R0-4)
 | 
						|
 | 
						|
	.text
 | 
						|
	.align 5
 | 
						|
FEXPORT(__smtc_ipi_vector)
 | 
						|
	.set	noat
 | 
						|
	/* Disable thread scheduling to make Status update atomic */
 | 
						|
	DMT	27					# dmt	k1
 | 
						|
	_ehb
 | 
						|
	/* Set EXL */
 | 
						|
	mfc0	k0,CP0_STATUS
 | 
						|
	ori	k0,k0,ST0_EXL
 | 
						|
	mtc0	k0,CP0_STATUS
 | 
						|
	_ehb
 | 
						|
	/* Thread scheduling now inhibited by EXL. Restore TE state. */
 | 
						|
	andi	k1,k1,VPECONTROL_TE
 | 
						|
	beqz	k1,1f
 | 
						|
	emt
 | 
						|
1:
 | 
						|
	/*
 | 
						|
	 * The IPI sender has put some information on the anticipated
 | 
						|
	 * kernel stack frame.  If we were in user mode, this will be
 | 
						|
	 * built above the saved kernel SP.  If we were already in the
 | 
						|
	 * kernel, it will be built above the current CPU SP.
 | 
						|
	 *
 | 
						|
	 * Were we in kernel mode, as indicated by CU0?
 | 
						|
	 */
 | 
						|
	sll	k1,k0,3
 | 
						|
	.set noreorder
 | 
						|
	bltz	k1,2f
 | 
						|
	move	k1,sp
 | 
						|
	.set reorder
 | 
						|
	/*
 | 
						|
	 * If previously in user mode, set CU0 and use kernel stack.
 | 
						|
	 */
 | 
						|
	li	k1,ST0_CU0
 | 
						|
	or	k1,k1,k0
 | 
						|
	mtc0	k1,CP0_STATUS
 | 
						|
	_ehb
 | 
						|
	get_saved_sp
 | 
						|
	/* Interrupting TC will have pre-set values in slots in the new frame */
 | 
						|
2:	subu	k1,k1,PT_SIZE
 | 
						|
	/* Load TCStatus Value */
 | 
						|
	lw	k0,PT_TCSTATUS(k1)
 | 
						|
	/* Write it to TCStatus to restore CU/KSU/IXMT state */
 | 
						|
	mtc0	k0,$2,1
 | 
						|
	_ehb
 | 
						|
	lw	k0,PT_EPC(k1)
 | 
						|
	mtc0	k0,CP0_EPC
 | 
						|
	/* Save all will redundantly recompute the SP, but use it for now */
 | 
						|
	SAVE_ALL
 | 
						|
	CLI
 | 
						|
	TRACE_IRQS_OFF
 | 
						|
	/* Function to be invoked passed stack pad slot 5 */
 | 
						|
	lw	t0,PT_PADSLOT5(sp)
 | 
						|
	/* Argument from sender passed in stack pad slot 4 */
 | 
						|
	lw	a0,PT_PADSLOT4(sp)
 | 
						|
	LONG_L	s0, TI_REGS($28)
 | 
						|
	LONG_S	sp, TI_REGS($28)
 | 
						|
	PTR_LA	ra, ret_from_irq
 | 
						|
	jr	t0
 | 
						|
 | 
						|
/*
 | 
						|
 * Called from idle loop to provoke processing of queued IPIs
 | 
						|
 * First IPI message in queue passed as argument.
 | 
						|
 */
 | 
						|
 | 
						|
LEAF(self_ipi)
 | 
						|
	/* Before anything else, block interrupts */
 | 
						|
	mfc0	t0,CP0_TCSTATUS
 | 
						|
	ori	t1,t0,TCSTATUS_IXMT
 | 
						|
	mtc0	t1,CP0_TCSTATUS
 | 
						|
	_ehb
 | 
						|
	/* We know we're in kernel mode, so prepare stack frame */
 | 
						|
	subu	t1,sp,PT_SIZE
 | 
						|
	sw	ra,PT_EPC(t1)
 | 
						|
	sw	a0,PT_PADSLOT4(t1)
 | 
						|
	la	t2,ipi_decode
 | 
						|
	sw	t2,PT_PADSLOT5(t1)
 | 
						|
	/* Save pre-disable value of TCStatus */
 | 
						|
	sw	t0,PT_TCSTATUS(t1)
 | 
						|
	j	__smtc_ipi_vector
 | 
						|
	nop
 | 
						|
END(self_ipi)
 |