Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 01ab1039 authored by Michael Trimarchi's avatar Michael Trimarchi Committed by Paul Mundt
Browse files

sh: Fix up DSP context save/restore.



There were a number of issues with the DSP context save/restore code,
mostly left-over relics from when it was introduced on SH3-DSP with
little follow-up testing, resulting in things like task_pt_dspregs()
referencing incorrect state on the stack.

This follows the MIPS convention of tracking the DSP state in the
thread_struct and handling the state save/restore in switch_to() and
finish_arch_switch() respectively. The regset interface is also updated,
which allows us to finally be rid of task_pt_dspregs() and the special
cased task_pt_regs().

Signed-off-by: default avatarMichael Trimarchi <michael@evidence.eu.com>
Signed-off-by: default avatarPaul Mundt <lethal@linux-sh.org>
parent a27873cd
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -57,6 +57,14 @@ asmlinkage void __init sh_cpu_init(void);
#define SR_IMASK	0x000000f0
#define SR_FD		0x00008000

/*
 * DSP structure and data
 */
struct sh_dsp_struct {
	unsigned long dsp_regs[14];
	long status;
};

/*
 * FPU structure and data
 */
@@ -96,6 +104,11 @@ struct thread_struct {

	/* floating point info */
	union sh_fpu_union fpu;

#ifdef CONFIG_SH_DSP
	/* Dsp status information */
	struct sh_dsp_struct dsp_status;
#endif
};

/* Count of active tasks with UBC settings */
+0 −8
Original line number Diff line number Diff line
@@ -119,16 +119,8 @@ struct task_struct;
extern void user_enable_single_step(struct task_struct *);
extern void user_disable_single_step(struct task_struct *);

#ifdef CONFIG_SH_DSP
#define task_pt_regs(task) \
	((struct pt_regs *) (task_stack_page(task) + THREAD_SIZE \
		 - sizeof(struct pt_dspregs)) - 1)
#define task_pt_dspregs(task) \
	((struct pt_dspregs *) (task_stack_page(task) + THREAD_SIZE) - 1)
#else
#define task_pt_regs(task) \
	((struct pt_regs *) (task_stack_page(task) + THREAD_SIZE) - 1)
#endif

static inline unsigned long profile_pc(struct pt_regs *regs)
{
+123 −47
Original line number Diff line number Diff line
@@ -3,6 +3,66 @@

#include <linux/types.h>

#ifdef CONFIG_SH_DSP

#define is_dsp_enabled(tsk)						\
	(!!(tsk->thread.dsp_status.status & SR_DSP))

#define __restore_dsp(tsk)						\
do {									\
	register u32 *__ts2 __asm__ ("r2") =				\
			(u32 *)&tsk->thread.dsp_status;			\
	__asm__ __volatile__ (						\
		".balign 4\n\t"						\
		"movs.l	@r2+, a1\n\t"					\
		"movs.l	@r2+, a0g\n\t"					\
		"movs.l	@r2+, a1g\n\t"					\
		"movs.l	@r2+, m0\n\t"					\
		"movs.l	@r2+, m1\n\t"					\
		"movs.l	@r2+, a0\n\t"					\
		"movs.l	@r2+, x0\n\t"					\
		"movs.l	@r2+, x1\n\t"					\
		"movs.l	@r2+, y0\n\t"					\
		"movs.l	@r2+, y1\n\t"					\
		"lds.l	@r2+, dsr\n\t"					\
		"ldc.l	@r2+, rs\n\t"					\
		"ldc.l	@r2+, re\n\t"					\
		"ldc.l	@r2+, mod\n\t"					\
		: : "r" (__ts2));					\
} while (0)


#define __save_dsp(tsk)							\
do {									\
	register u32 *__ts2 __asm__ ("r2") =				\
			(u32 *)&tsk->thread.dsp_status + 14;		\
									\
	__asm__ __volatile__ (						\
		".balign 4\n\t"						\
		"stc.l	mod, @-r2\n\t"				\
		"stc.l	re, @-r2\n\t"					\
		"stc.l	rs, @-r2\n\t"					\
		"sts.l	dsr, @-r2\n\t"				\
		"sts.l	y1, @-r2\n\t"					\
		"sts.l	y0, @-r2\n\t"					\
		"sts.l	x1, @-r2\n\t"					\
		"sts.l	x0, @-r2\n\t"					\
		"sts.l	a0, @-r2\n\t"					\
		".word	0xf653		! movs.l	a1, @-r2\n\t"	\
		".word	0xf6f3		! movs.l	a0g, @-r2\n\t"	\
		".word	0xf6d3		! movs.l	a1g, @-r2\n\t"	\
		".word	0xf6c3		! movs.l        m0, @-r2\n\t"	\
		".word	0xf6e3		! movs.l        m1, @-r2\n\t"	\
		: : "r" (__ts2));					\
} while (0)

#else

#define is_dsp_enabled(tsk)	(0)
#define __save_dsp(tsk)		do { } while (0)
#define __restore_dsp(tsk)	do { } while (0)
#endif

struct task_struct *__switch_to(struct task_struct *prev,
				struct task_struct *next);

@@ -11,14 +71,24 @@ struct task_struct *__switch_to(struct task_struct *prev,
 */
#define switch_to(prev, next, last)				\
do {								\
	register u32 *__ts1 __asm__ ("r1") = (u32 *)&prev->thread.sp;	\
	register u32 *__ts2 __asm__ ("r2") = (u32 *)&prev->thread.pc;	\
	register u32 *__ts4 __asm__ ("r4") = (u32 *)prev;		\
	register u32 *__ts5 __asm__ ("r5") = (u32 *)next;		\
	register u32 *__ts6 __asm__ ("r6") = (u32 *)&next->thread.sp;	\
	register u32 __ts7 __asm__ ("r7") = next->thread.pc;		\
	register u32 *__ts1 __asm__ ("r1");			\
	register u32 *__ts2 __asm__ ("r2");			\
	register u32 *__ts4 __asm__ ("r4");			\
	register u32 *__ts5 __asm__ ("r5");			\
	register u32 *__ts6 __asm__ ("r6");			\
	register u32 __ts7 __asm__ ("r7");			\
	struct task_struct *__last;				\
								\
	if (is_dsp_enabled(prev))				\
		__save_dsp(prev);				\
								\
	__ts1 = (u32 *)&prev->thread.sp;			\
	__ts2 = (u32 *)&prev->thread.pc;			\
	__ts4 = (u32 *)prev;					\
	__ts5 = (u32 *)next;					\
	__ts6 = (u32 *)&next->thread.sp;			\
	__ts7 = next->thread.pc;				\
								\
	__asm__ __volatile__ (					\
		".balign 4\n\t"					\
		"stc.l	gbr, @-r15\n\t"				\
@@ -58,6 +128,12 @@ do { \
	last = __last;						\
} while (0)

#define finish_arch_switch(prev)				\
do {								\
	if (is_dsp_enabled(prev))				\
		__restore_dsp(prev);				\
} while (0)

#define __uses_jump_to_uncached \
	noinline __attribute__ ((__section__ (".uncached.text")))

+6 −79
Original line number Diff line number Diff line
@@ -254,40 +254,6 @@ restore_all:

	lds	k2, pr			! restore pr
	!
#ifdef CONFIG_SH_DSP
	mov.l	@r15+, k0		! DSP mode marker
	mov.l	5f, k1
	cmp/eq	k0, k1			! Do we have a DSP stack frame?
	bf	skip_restore

	stc	sr, k0			! Enable CPU DSP mode
	or	k1, k0			! (within kernel it may be disabled)
	ldc	k0, sr
	mov	r2, k0			! Backup r2

	! Restore DSP registers from stack
	mov	r15, r2
	movs.l	@r2+, a1
	movs.l	@r2+, a0g
	movs.l	@r2+, a1g
	movs.l	@r2+, m0
	movs.l	@r2+, m1
	mov	r2, r15

	lds.l	@r15+, a0
	lds.l	@r15+, x0
	lds.l	@r15+, x1
	lds.l	@r15+, y0
	lds.l	@r15+, y1
	lds.l	@r15+, dsr
	ldc.l	@r15+, rs
	ldc.l	@r15+, re
	ldc.l	@r15+, mod

	mov	k0, r2			! Restore r2
skip_restore:
#endif
	!
	! Calculate new SR value
	mov	k3, k2			! original SR value
	mov	#0xf0, k1
@@ -358,7 +324,7 @@ general_exception:
	add	k0, k4
0:
	! Setup stack and save DSP context (k0 contains original r15 on return)
	bsr	prepare_stack_save_dsp
	bsr	prepare_stack
	 nop

	! Save registers / Switch to bank 0
@@ -374,15 +340,14 @@ general_exception:
1:	.long	EXPEVT
#endif

! prepare_stack_save_dsp()
! prepare_stack()
! - roll back gRB
! - switch to kernel stack
! - save DSP
! k0 returns original sp (after roll back)
! k1 trashed
! k2 trashed

prepare_stack_save_dsp:
prepare_stack:
#ifdef CONFIG_GUSA
	! Check for roll back gRB (User and Kernel)
	mov	r15, k0
@@ -416,47 +381,9 @@ prepare_stack_save_dsp:
	mov	k1, r15		! change to kernel stack
	!
1:
#ifdef CONFIG_SH_DSP
	! Save DSP context if needed
	stc	sr, k1
	mov	#0x10, k2
	shll8   k2			! DSP=1 (0x00001000)
	tst	k2, k1			! Check if in DSP mode (passed in k2)
	bt/s	skip_save
	 mov	#0, k1			! Set marker for no stack frame

	mov	k2, k1			! Save has-frame marker

	! Save DSP registers on stack
	stc.l	mod, @-r15
	stc.l	re, @-r15
	stc.l	rs, @-r15
	sts.l	dsr, @-r15
	sts.l	y1, @-r15
	sts.l	y0, @-r15
	sts.l	x1, @-r15
	sts.l	x0, @-r15
	sts.l	a0, @-r15

	! GAS is broken, does not generate correct "movs.l Ds,@-As" instr.

	! FIXME: Make sure that this is still the case with newer toolchains,
	! as we're not at all interested in supporting ancient toolchains at
	! this point. -- PFM.

	mov	r15, k2
	.word	0xf653			! movs.l	a1, @-r2
	.word	0xf6f3			! movs.l	a0g, @-r2
	.word	0xf6d3			! movs.l	a1g, @-r2
	.word	0xf6c3			! movs.l	m0, @-r2
	.word	0xf6e3			! movs.l	m1, @-r2
	mov	k2, r15

skip_save:
	mov.l	k1, @-r15		! Push DSP mode marker onto stack
#endif
	rts
	 nop

!
! 0x400: Instruction and Data TLB miss exception vector
!
@@ -468,7 +395,7 @@ handle_exception:
	mova	exception_data, k0

	! Setup stack and save DSP context (k0 contains original r15 on return)
	bsr	prepare_stack_save_dsp
	bsr	prepare_stack
	 PREF(k0)

	! Save registers / Switch to bank 0
@@ -572,7 +499,7 @@ ENTRY(handle_interrupt)
	mova	exception_data, k0

	! Setup stack and save DSP context (k0 contains original r15 on return)
	bsr	prepare_stack_save_dsp
	bsr	prepare_stack
	 PREF(k0)

	! Save registers / Switch to bank 0
+13 −1
Original line number Diff line number Diff line
@@ -176,14 +176,26 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp,
{
	struct thread_info *ti = task_thread_info(p);
	struct pt_regs *childregs;
#if defined(CONFIG_SH_FPU)
#if defined(CONFIG_SH_FPU) || defined(CONFIG_SH_DSP)
	struct task_struct *tsk = current;
#endif

#if defined(CONFIG_SH_FPU)
	unlazy_fpu(tsk, regs);
	p->thread.fpu = tsk->thread.fpu;
	copy_to_stopped_child_used_math(p);
#endif

#if defined(CONFIG_SH_DSP)
	if (is_dsp_enabled(tsk)) {
		/* We can use the __save_dsp or just copy the struct:
		 * __save_dsp(p);
		 * p->thread.dsp_status.status |= SR_DSP
		 */
		p->thread.dsp_status = tsk->thread.dsp_status;
	}
#endif

	childregs = task_pt_regs(p);
	*childregs = *regs;

Loading