211 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
			
		
		
	
	
			211 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
| #include <machine/asm.h>
 | |
| 
 | |
| #ifdef __ACK__
 | |
| .text
 | |
| begtext:
 | |
| #ifdef __ACK__
 | |
| .rom
 | |
| #else
 | |
| .data
 | |
| #endif
 | |
| begrom:
 | |
| .data
 | |
| begdata:
 | |
| .bss
 | |
| begbss:
 | |
| #endif
 | |
| 
 | |
| 
 | |
| IMPORT(getuctx)
 | |
| IMPORT(setuctx)
 | |
| IMPORT(resumecontext)
 | |
| 
 | |
| 
 | |
| /* Offsets into ucontext_t structure. Keep in sync with <sys/ucontext.h>! */
 | |
| #define UC_FLAGS	0 
 | |
| #define UC_LINK		UC_FLAGS + 4
 | |
| #define MCTX		UC_LINK + 4
 | |
| #define MAGIC		MCTX
 | |
| #define GS		MAGIC+4
 | |
| #define FS		GS+2
 | |
| #define ES		FS+2
 | |
| #define DS		ES+2
 | |
| #define DI		DS+2
 | |
| #define SI		DI+4
 | |
| #define BP		SI+4
 | |
| #define ST		BP+4	/* Hole for another SP */
 | |
| #define BX		ST+4
 | |
| #define DX		BX+4
 | |
| #define CX		DX+4
 | |
| #define AX		CX+4
 | |
| #define RETADR		AX+4
 | |
| #define PC		RETADR+4
 | |
| #define CS		PC+4
 | |
| #define PSW		CS+4
 | |
| #define SP		PSW+4
 | |
| #define SS		SP+4
 | |
| 
 | |
| 
 | |
| /* MCF_MAGIC value from <mcontext.h> */
 | |
| #define MCF_MAGIC	0xc0ffee
 | |
| 
 | |
| /* Values from <sys/ucontext.h> */
 | |
| #define UCF_IGNFPU	0x002
 | |
| #define UCF_IGNSIGM	0x004
 | |
| 
 | |
| 
 | |
| /* EINVAL from errno.h */
 | |
| #define EFAULT		14
 | |
| #define EINVAL 		22
 | |
| 
 | |
| 
 | |
| /* int getcontext(ucontext_t *ucp) 
 | |
|  *	Initialise the structure pointed to by ucp to the current user context
 | |
|  *	of the calling thread. */
 | |
| ENTRY(getcontext)
 | |
| 	/* In case a process does not use the FPU and is neither interested in
 | |
| 	 * saving its signal mask, then we can skip the context switch to
 | |
| 	 * PM and kernel altogether and only save general-purpose registers. */
 | |
| 
 | |
| 	mov (%esp), %ecx	/* Save return address:
 | |
| 				 * When setcontext or swapcontext is called,
 | |
| 				 * we jump to this address and continue
 | |
| 				 * running. */
 | |
| 
 | |
| 	mov 4(%esp), %edx		/* edx = ucp */
 | |
| 	/* Check null pointer */
 | |
| 	cmp $0, %edx			/* edx == NULL? */
 | |
| 	jne 3f				/* Not null, continue */
 | |
| 	movl $EFAULT, (_C_LABEL(errno))
 | |
| 	xor %eax, %eax
 | |
| 	dec %eax			/* return -1 */
 | |
| 	ret
 | |
| 	
 | |
| 3:	/* Check flags */
 | |
| 	push %ecx			/* save ecx */
 | |
| 	push %ebx			/* save ebx */
 | |
| 	lea UC_FLAGS(%edx), %ebx	/* ebx = &(ucp->uc_flags) */
 | |
| 	mov (%ebx), %ecx		/* ecx = ucp->uc_flags */
 | |
| 	mov $UCF_IGNFPU, %eax
 | |
| 	or $UCF_IGNSIGM, %eax
 | |
| 	cmp %eax, %ecx			/* is UCF_IGNFPU or UCF_IGNSIGM set? */
 | |
| 	pop %ebx			/* restore ebx */
 | |
| 	pop %ecx			/* restore ecx */
 | |
| 	jz 1f				/* Both are set, skip getuctx */
 | |
| 
 | |
| 0:
 | |
| 	push %ecx			/* Save ecx */
 | |
| 	push %edx		 
 | |
| 	call _C_LABEL(getuctx)		/* getuctx(ucp) */
 | |
| 	pop %edx			/* clean up stack and restore edx */
 | |
| 	pop %ecx			/* Restore ecx */
 | |
| 
 | |
| 1: 
 | |
| 	/* Save the context */
 | |
| 	mov 4(%esp), %edx		/* edx = ucp */
 | |
| 	pop %eax			/* retaddr */
 | |
| 	mov %eax, PC(%edx)		/* Save real RTA in mcp struct */
 | |
| 	mov %esp, SP(%edx)	/* Save stack pointer (now pointing to ucp) */
 | |
| 	/* Save GP registers */
 | |
| 	mov %ebp, BP(%edx)		/* Save EBP */
 | |
| 	mov %esi, SI(%edx)		/* Save ESI */
 | |
| 	mov %edi, DI(%edx)		/* Save EDI */
 | |
| 	mov %ebx, BX(%edx)		/* Save EBX */
 | |
| 	mov %ecx, CX(%edx)		/* Save ECX */
 | |
| 	movl $MCF_MAGIC, MAGIC(%edx)	/* Set magic value */
 | |
| 	push %eax			/* Restore retaddr */
 | |
| 
 | |
| 	xor %eax, %eax			/* Return 0 */
 | |
| 
 | |
| 2:	
 | |
| 	add $4, %esp			/* Remove stale (setcontext) RTA */
 | |
| 	jmp *%ecx			/* Restore return address */
 | |
| 	
 | |
| 
 | |
| /* int setcontext(const ucontext_t *ucp)
 | |
|  *	Restore the user context pointed to by ucp. A successful call to
 | |
|  *	setcontext does not return; program execution resumes at the point
 | |
|  *	specified by the ucp argument. If ucp was created with getcontext(), 
 | |
|  *	program execution continues as if the corresponding call of getcontext()
 | |
|  *	had just returned. If ucp was created with makecontext(), program
 | |
|  *	execution continues with the function passed to makecontext(). */
 | |
| ENTRY(setcontext)
 | |
| 	/* In case a process does not use the FPU and is neither interested in
 | |
| 	 * restoring its signal mask, then we can skip the context switch to
 | |
| 	 * PM and kernel altogether and restore state here. */
 | |
| 
 | |
| 	mov 4(%esp), %edx		/* edx = ucp */
 | |
| 
 | |
| 	/* Check null pointer */
 | |
| 	cmp $0, %edx			/* edx == NULL? */
 | |
| 	jnz 3f				/* Not null, continue */
 | |
| 	movl $EFAULT, (_C_LABEL(errno))
 | |
| 	xor %eax, %eax
 | |
| 	dec %eax			/* return -1 */
 | |
| 	ret
 | |
| 	
 | |
| 3:	/* Check flags */
 | |
| 	push %ebx			/* save ebx */
 | |
| 	lea MAGIC(%edx), %ebx		/* ebx = &(ucp->mc_context.mc_magic) */
 | |
| 	mov (%ebx), %ecx		/* ecx = ucp->mc_context.mc_magic */
 | |
| 	pop %ebx			/* restore ebx */
 | |
| 	cmp $MCF_MAGIC, %ecx	/* is the magic value set (is context valid)?*/
 | |
| 	jz 4f				/* is set, proceed */
 | |
| 	movl $EINVAL, (_C_LABEL(errno)) /* not set, return error code */
 | |
| 	xor %eax, %eax
 | |
| 	dec %eax			/* return -1 */
 | |
| 	ret	
 | |
| 
 | |
| 
 | |
| 4:	push %ebx			/* save ebx */
 | |
| 	lea UC_FLAGS(%edx), %ebx 	/* ebx = &(ucp->uc_flags) */
 | |
| 	mov (%ebx), %ecx		/* ecx = ucp->uc_flags */
 | |
| 	pop %ebx			/* restore ebx */
 | |
| 	mov $UCF_IGNFPU, %eax
 | |
| 	or $UCF_IGNSIGM, %eax
 | |
| 	cmp %eax, %ecx		/* Are UCF_IGNFPU and UCF_IGNSIGM flags set? */
 | |
| 	jz 1f			/* Both are set, so don't bother restoring FPU
 | |
| 				 * state and signal mask */
 | |
| 
 | |
| 0:	push %ecx			/* Save ecx */
 | |
| 	push %edx		 
 | |
| 	call _C_LABEL(setuctx)		/* setuctx(ucp) */
 | |
| 	pop %edx			/* Clean up stack and restore edx */
 | |
| 	pop %ecx			/* Restore ecx */
 | |
| 
 | |
| 1:	/* Restore the registers */
 | |
| 	mov 4(%esp), %edx		/* edx = ucp */
 | |
| 	mov CX(%edx), %ecx		/* Restore ECX */
 | |
| 	mov BX(%edx), %ebx		/* Restore EBX */
 | |
| 	mov DI(%edx), %edi		/* Restore EDI */
 | |
| 	mov SI(%edx), %esi		/* Restore ESI */
 | |
| 	mov BP(%edx), %ebp		/* Restore EBP */
 | |
| 	mov SP(%edx), %esp		/* Restore stack pointer */
 | |
| 
 | |
| 2:
 | |
| 	jmp *PC(%edx)  	/* Push RTA onto stack so we can return to it */
 | |
| 
 | |
| 
 | |
| /* void ctx_start((void *func)(int arg1, ..., argn), arg1, ..., argn,
 | |
|  *		  ucontext_t *ucp)
 | |
|  *	A wrapper to start function `func'. ESI register will contain a pointer
 | |
|  *	to ucp on the stack. By setting ESP to ESI, we effectively 'remove' all
 | |
|  *	arguments to `func' from the stack. Finally, a call to resumecontext
 | |
|  *	will start the next context in the linked list (or exit the program if
 | |
|  *	there is no context). */
 | |
| ENTRY(ctx_start)
 | |
| 	/* 0(esp) -> func
 | |
| 	 * 4(esp) -> arg1
 | |
| 	 * ...
 | |
| 	 * 4*n(esp) -> argn
 | |
| 	 * 4*(n+1)(esp) -> ucp */
 | |
| 
 | |
| 	pop %eax			/* eax = func */
 | |
| 	call *%eax			/* func(arg1, ..., argn) */
 | |
| 	mov %esi, %esp			/* Clean up stack */
 | |
| 	/* ucp is now at the top of the stack again */
 | |
| 	call _C_LABEL(resumecontext)	/* resumecontext(ucp) */
 | |
| 	ret			/* never reached */
 | |
| 
 | |
| 
 | 
