// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#include "go_asm.h"
#include "go_tls.h"
#include "textflag.h"
#include "syscall_nacl.h"

#define NACL_SYSCALL(code) \
	MOVL $(0x10000 + ((code)<<5)), AX; CALL AX

TEXT runtime·settls(SB),NOSPLIT,$0
	MOVL	DI, TLS // really BP
	RET

TEXT runtime·exit(SB),NOSPLIT,$0
	MOVL code+0(FP), DI
	NACL_SYSCALL(SYS_exit)
	RET

// func exitThread(wait *uint32)
TEXT runtime·exitThread(SB),NOSPLIT,$0-4
	MOVL wait+0(FP), DI
	// SYS_thread_exit will clear *wait when the stack is free.
	NACL_SYSCALL(SYS_thread_exit)
	JMP 0(PC)

TEXT runtime·open(SB),NOSPLIT,$0
	MOVL name+0(FP), DI
	MOVL mode+4(FP), SI
	MOVL perm+8(FP), DX
	NACL_SYSCALL(SYS_open)
	MOVL AX, ret+16(FP)
	RET

TEXT runtime·closefd(SB),NOSPLIT,$0
	MOVL fd+0(FP), DI
	NACL_SYSCALL(SYS_close)
	MOVL AX, ret+8(FP)
	RET

TEXT runtime·read(SB),NOSPLIT,$0
	MOVL fd+0(FP), DI
	MOVL p+4(FP), SI
	MOVL n+8(FP), DX
	NACL_SYSCALL(SYS_read)
	MOVL AX, ret+16(FP)
	RET

TEXT syscall·naclWrite(SB), NOSPLIT, $24-20
	MOVL arg1+0(FP), DI
	MOVL arg2+4(FP), SI
	MOVL arg3+8(FP), DX
	MOVL DI, 0(SP)
	MOVL SI, 4(SP)
	MOVL DX, 8(SP)
	CALL runtime·write(SB)
	MOVL 16(SP), AX
	MOVL AX, ret+16(FP)
	RET

TEXT runtime·write(SB),NOSPLIT,$16-20
	// If using fake time and writing to stdout or stderr,
	// emit playback header before actual data.
	MOVQ runtime·faketime(SB), AX
	CMPQ AX, $0
	JEQ write
	MOVL fd+0(FP), DI
	CMPL DI, $1
	JEQ playback
	CMPL DI, $2
	JEQ playback

write:
	// Ordinary write.
	MOVL fd+0(FP), DI
	MOVL p+4(FP), SI
	MOVL n+8(FP), DX
	NACL_SYSCALL(SYS_write)
	MOVL	AX, ret+16(FP)
	RET

	// Write with playback header.
	// First, lock to avoid interleaving writes.
playback:
	MOVL $1, BX
	XCHGL	runtime·writelock(SB), BX
	CMPL BX, $0
	JNE playback

	MOVQ runtime·lastfaketime(SB), CX
	MOVL runtime·lastfaketimefd(SB), BX
	CMPL DI, BX
	JE samefd

	// If the current fd doesn't match the fd of the previous write,
	// ensure that the timestamp is strictly greater. That way, we can
	// recover the original order even if we read the fds separately.
	INCQ CX
	MOVL DI, runtime·lastfaketimefd(SB)

samefd:
	CMPQ AX, CX
	CMOVQLT CX, AX
	MOVQ AX, runtime·lastfaketime(SB)

	// Playback header: 0 0 P B <8-byte time> <4-byte data length>
	MOVL $(('B'<<24) | ('P'<<16)), 0(SP)
	BSWAPQ AX
	MOVQ AX, 4(SP)
	MOVL n+8(FP), DX
	BSWAPL DX
	MOVL DX, 12(SP)
	MOVL fd+0(FP), DI
	MOVL SP, SI
	MOVL $16, DX
	NACL_SYSCALL(SYS_write)

	// Write actual data.
	MOVL fd+0(FP), DI
	MOVL p+4(FP), SI
	MOVL n+8(FP), DX
	NACL_SYSCALL(SYS_write)

	// Unlock.
	MOVL	$0, runtime·writelock(SB)

	MOVL	AX, ret+16(FP)
	RET

TEXT runtime·nacl_exception_stack(SB),NOSPLIT,$0
	MOVL p+0(FP), DI
	MOVL size+4(FP), SI
	NACL_SYSCALL(SYS_exception_stack)
	MOVL AX, ret+8(FP)
	RET

TEXT runtime·nacl_exception_handler(SB),NOSPLIT,$0
	MOVL fn+0(FP), DI
	MOVL arg+4(FP), SI
	NACL_SYSCALL(SYS_exception_handler)
	MOVL AX, ret+8(FP)
	RET

TEXT runtime·nacl_sem_create(SB),NOSPLIT,$0
	MOVL flag+0(FP), DI
	NACL_SYSCALL(SYS_sem_create)
	MOVL AX, ret+8(FP)
	RET

TEXT runtime·nacl_sem_wait(SB),NOSPLIT,$0
	MOVL sem+0(FP), DI
	NACL_SYSCALL(SYS_sem_wait)
	MOVL AX, ret+8(FP)
	RET

TEXT runtime·nacl_sem_post(SB),NOSPLIT,$0
	MOVL sem+0(FP), DI
	NACL_SYSCALL(SYS_sem_post)
	MOVL AX, ret+8(FP)
	RET

TEXT runtime·nacl_mutex_create(SB),NOSPLIT,$0
	MOVL flag+0(FP), DI
	NACL_SYSCALL(SYS_mutex_create)
	MOVL AX, ret+8(FP)
	RET

TEXT runtime·nacl_mutex_lock(SB),NOSPLIT,$0
	MOVL mutex+0(FP), DI
	NACL_SYSCALL(SYS_mutex_lock)
	MOVL AX, ret+8(FP)
	RET

TEXT runtime·nacl_mutex_trylock(SB),NOSPLIT,$0
	MOVL mutex+0(FP), DI
	NACL_SYSCALL(SYS_mutex_trylock)
	MOVL AX, ret+8(FP)
	RET

TEXT runtime·nacl_mutex_unlock(SB),NOSPLIT,$0
	MOVL mutex+0(FP), DI
	NACL_SYSCALL(SYS_mutex_unlock)
	MOVL AX, ret+8(FP)
	RET

TEXT runtime·nacl_cond_create(SB),NOSPLIT,$0
	MOVL flag+0(FP), DI
	NACL_SYSCALL(SYS_cond_create)
	MOVL AX, ret+8(FP)
	RET

TEXT runtime·nacl_cond_wait(SB),NOSPLIT,$0
	MOVL cond+0(FP), DI
	MOVL n+4(FP), SI
	NACL_SYSCALL(SYS_cond_wait)
	MOVL AX, ret+8(FP)
	RET

TEXT runtime·nacl_cond_signal(SB),NOSPLIT,$0
	MOVL cond+0(FP), DI
	NACL_SYSCALL(SYS_cond_signal)
	MOVL AX, ret+8(FP)
	RET

TEXT runtime·nacl_cond_broadcast(SB),NOSPLIT,$0
	MOVL cond+0(FP), DI
	NACL_SYSCALL(SYS_cond_broadcast)
	MOVL AX, ret+8(FP)
	RET

TEXT runtime·nacl_cond_timed_wait_abs(SB),NOSPLIT,$0
	MOVL cond+0(FP), DI
	MOVL lock+4(FP), SI
	MOVL ts+8(FP), DX
	NACL_SYSCALL(SYS_cond_timed_wait_abs)
	MOVL AX, ret+16(FP)
	RET

TEXT runtime·nacl_thread_create(SB),NOSPLIT,$0
	MOVL fn+0(FP), DI
	MOVL stk+4(FP), SI
	MOVL tls+8(FP), DX
	MOVL xx+12(FP), CX
	NACL_SYSCALL(SYS_thread_create)
	MOVL AX, ret+16(FP)
	RET

TEXT runtime·mstart_nacl(SB),NOSPLIT,$0
	NACL_SYSCALL(SYS_tls_get)
	SUBL	$8, AX
	MOVL	AX, TLS
	JMP runtime·mstart(SB)

TEXT runtime·nacl_nanosleep(SB),NOSPLIT,$0
	MOVL ts+0(FP), DI
	MOVL extra+4(FP), SI
	NACL_SYSCALL(SYS_nanosleep)
	MOVL AX, ret+8(FP)
	RET

TEXT runtime·osyield(SB),NOSPLIT,$0
	NACL_SYSCALL(SYS_sched_yield)
	RET

TEXT runtime·mmap(SB),NOSPLIT,$8
	MOVL addr+0(FP), DI
	MOVL n+4(FP), SI
	MOVL prot+8(FP), DX
	MOVL flags+12(FP), CX
	MOVL fd+16(FP), R8
	MOVL off+20(FP), AX
	MOVQ AX, 0(SP)
	MOVL SP, R9
	NACL_SYSCALL(SYS_mmap)
	CMPL AX, $-4095
	JNA ok
	NEGL AX
	MOVL	$0, p+24(FP)
	MOVL	AX, err+28(FP)
	RET
ok:
	MOVL	AX, p+24(FP)
	MOVL	$0, err+28(FP)
	RET

TEXT runtime·walltime(SB),NOSPLIT,$16
	MOVQ runtime·faketime(SB), AX
	CMPQ AX, $0
	JEQ realtime
	MOVQ $0, DX
	MOVQ $1000000000, CX
	DIVQ CX
	MOVQ AX, sec+0(FP)
	MOVL DX, nsec+8(FP)
	RET
realtime:
	MOVL $0, DI // real time clock
	LEAL 0(SP), AX
	MOVL AX, SI // timespec
	NACL_SYSCALL(SYS_clock_gettime)
	MOVL 0(SP), AX // low 32 sec
	MOVL 4(SP), CX // high 32 sec
	MOVL 8(SP), BX // nsec

	// sec is in AX, nsec in BX
	MOVL	AX, sec_lo+0(FP)
	MOVL	CX, sec_hi+4(FP)
	MOVL	BX, nsec+8(FP)
	RET

TEXT syscall·now(SB),NOSPLIT,$0
	JMP runtime·walltime(SB)

TEXT runtime·nacl_clock_gettime(SB),NOSPLIT,$0
	MOVL arg1+0(FP), DI
	MOVL arg2+4(FP), SI
	NACL_SYSCALL(SYS_clock_gettime)
	MOVL AX, ret+8(FP)
	RET

TEXT runtime·nanotime(SB),NOSPLIT,$16
	MOVQ runtime·faketime(SB), AX
	CMPQ AX, $0
	JEQ 3(PC)
	MOVQ	AX, ret+0(FP)
	RET
	MOVL $0, DI // real time clock
	LEAL 0(SP), AX
	MOVL AX, SI // timespec
	NACL_SYSCALL(SYS_clock_gettime)
	MOVQ 0(SP), AX // sec
	MOVL 8(SP), DX // nsec

	// sec is in AX, nsec in DX
	// return nsec in AX
	IMULQ	$1000000000, AX
	ADDQ	DX, AX
	MOVQ	AX, ret+0(FP)
	RET

TEXT runtime·sigtramp(SB),NOSPLIT,$80
	// restore TLS register at time of execution,
	// in case it's been smashed.
	// the TLS register is really BP, but for consistency
	// with non-NaCl systems it is referred to here as TLS.
	// NOTE: Cannot use SYS_tls_get here (like we do in mstart_nacl),
	// because the main thread never calls tls_set.
	LEAL ctxt+0(FP), AX
	MOVL (16*4+5*8)(AX), AX
	MOVL	AX, TLS

	// check that g exists
	get_tls(CX)
	MOVL	g(CX), DI

	CMPL	DI, $0
	JEQ	nog

	// save g
	MOVL	DI, 20(SP)

	// g = m->gsignal
	MOVL	g_m(DI), BX
	MOVL	m_gsignal(BX), BX
	MOVL	BX, g(CX)

//JMP debughandler

	// copy arguments for sighandler
	MOVL	$11, 0(SP) // signal
	MOVL	$0, 4(SP) // siginfo
	LEAL	ctxt+0(FP), AX
	MOVL	AX, 8(SP) // context
	MOVL	DI, 12(SP) // g

	CALL	runtime·sighandler(SB)

	// restore g
	get_tls(CX)
	MOVL	20(SP), BX
	MOVL	BX, g(CX)

	// Enable exceptions again.
	NACL_SYSCALL(SYS_exception_clear_flag)

	// Restore registers as best we can. Impossible to do perfectly.
	// See comment in sys_nacl_386.s for extended rationale.
	LEAL	ctxt+0(FP), SI
	ADDL	$64, SI
	MOVQ	0(SI), AX
	MOVQ	8(SI), CX
	MOVQ	16(SI), DX
	MOVQ	24(SI), BX
	MOVL	32(SI), SP	// MOVL for SP sandboxing
	// 40(SI) is saved BP aka TLS, already restored above
	// 48(SI) is saved SI, never to be seen again
	MOVQ	56(SI), DI
	MOVQ	64(SI), R8
	MOVQ	72(SI), R9
	MOVQ	80(SI), R10
	MOVQ	88(SI), R11
	MOVQ	96(SI), R12
	MOVQ	104(SI), R13
	MOVQ	112(SI), R14
	// 120(SI) is R15, which is owned by Native Client and must not be modified
	MOVQ	128(SI), SI // saved PC
	// 136(SI) is saved EFLAGS, never to be seen again
	JMP	SI

//debughandler:
	//// print basic information
	//LEAL	ctxt+0(FP), DI
	//MOVL	$runtime·sigtrampf(SB), AX
	//MOVL	AX, 0(SP)
	//MOVQ	(16*4+16*8)(DI), BX // rip
	//MOVQ	BX, 8(SP)
	//MOVQ	(16*4+0*8)(DI), BX // rax
	//MOVQ	BX, 16(SP)
	//MOVQ	(16*4+1*8)(DI), BX // rcx
	//MOVQ	BX, 24(SP)
	//MOVQ	(16*4+2*8)(DI), BX // rdx
	//MOVQ	BX, 32(SP)
	//MOVQ	(16*4+3*8)(DI), BX // rbx
	//MOVQ	BX, 40(SP)
	//MOVQ	(16*4+7*8)(DI), BX // rdi
	//MOVQ	BX, 48(SP)
	//MOVQ	(16*4+15*8)(DI), BX // r15
	//MOVQ	BX, 56(SP)
	//MOVQ	(16*4+4*8)(DI), BX // rsp
	//MOVQ	0(BX), BX
	//MOVQ	BX, 64(SP)
	//CALL	runtime·printf(SB)
	//
	//LEAL	ctxt+0(FP), DI
	//MOVQ	(16*4+16*8)(DI), BX // rip
	//MOVL	BX, 0(SP)
	//MOVQ	(16*4+4*8)(DI), BX // rsp
	//MOVL	BX, 4(SP)
	//MOVL	$0, 8(SP)	// lr
	//get_tls(CX)
	//MOVL	g(CX), BX
	//MOVL	BX, 12(SP)	// gp
	//CALL	runtime·traceback(SB)

notls:
	MOVL	0, AX
	RET

nog:
	MOVL	0, AX
	RET

// cannot do real signal handling yet, because gsignal has not been allocated.
MOVL $1, DI; NACL_SYSCALL(SYS_exit)

// func getRandomData([]byte)
TEXT runtime·getRandomData(SB),NOSPLIT,$0-12
	MOVL arg_base+0(FP), DI
	MOVL arg_len+4(FP), SI
	NACL_SYSCALL(SYS_get_random_bytes)
	RET

TEXT runtime·nacl_sysinfo(SB),NOSPLIT,$16
/*
	MOVL	di+0(FP), DI
	LEAL	12(DI), BX
	MOVL	8(DI), AX
	ADDL	4(DI), AX
	ADDL	$2, AX
	LEAL	(BX)(AX*4), BX
	MOVL	BX, runtime·nacl_irt_query(SB)
auxloop:
	MOVL	0(BX), DX
	CMPL	DX, $0
	JNE	2(PC)
	RET
	CMPL	DX, $32
	JEQ	auxfound
	ADDL	$8, BX
	JMP	auxloop
auxfound:
	MOVL	4(BX), BX
	MOVL	BX, runtime·nacl_irt_query(SB)

	LEAL	runtime·nacl_irt_basic_v0_1_str(SB), DI
	LEAL	runtime·nacl_irt_basic_v0_1(SB), SI
	MOVL	runtime·nacl_irt_basic_v0_1_size(SB), DX
	MOVL	runtime·nacl_irt_query(SB), BX
	CALL	BX

	LEAL	runtime·nacl_irt_memory_v0_3_str(SB), DI
	LEAL	runtime·nacl_irt_memory_v0_3(SB), SI
	MOVL	runtime·nacl_irt_memory_v0_3_size(SB), DX
	MOVL	runtime·nacl_irt_query(SB), BX
	CALL	BX

	LEAL	runtime·nacl_irt_thread_v0_1_str(SB), DI
	LEAL	runtime·nacl_irt_thread_v0_1(SB), SI
	MOVL	runtime·nacl_irt_thread_v0_1_size(SB), DX
	MOVL	runtime·nacl_irt_query(SB), BX
	CALL	BX

	// TODO: Once we have a NaCl SDK with futex syscall support,
	// try switching to futex syscalls and here load the
	// nacl-irt-futex-0.1 table.
*/
	RET
