// Copyright 2014 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.
//
// System calls and other sys.stuff for AMD64, SunOS
// /usr/include/sys/syscall.h for syscall numbers.
//

#include "zasm_GOOS_GOARCH.h"
#include "../../cmd/ld/textflag.h"

// This is needed by asm_amd64.s
TEXT runtime·settls(SB),NOSPLIT,$8
	RET

// void libc·miniterrno(void *(*___errno)(void));
//
// Set the TLS errno pointer in M.
//
// Called using runtime·asmcgocall from os_solaris.c:/minit.
TEXT runtime·miniterrno(SB),NOSPLIT,$0
	// asmcgocall will put first argument into DI.
	CALL	DI	// SysV ABI so returns in AX
	get_tls(CX)
	MOVQ	m(CX), BX
	MOVQ	AX,	m_perrno(BX)
	RET

// int64 runtime·nanotime1(void);
//
// clock_gettime(3c) wrapper because Timespec is too large for
// runtime·nanotime stack.
//
// Called using runtime·sysvicall6 from os_solaris.c:/nanotime.
TEXT runtime·nanotime1(SB),NOSPLIT,$0
	// need space for the timespec argument.
	SUBQ	$64, SP	// 16 bytes will do, but who knows in the future?
	MOVQ	$3, DI	// CLOCK_REALTIME from <sys/time_impl.h>
	MOVQ	SP, SI
	MOVQ	libc·clock_gettime(SB), AX
	CALL	AX
	MOVQ	(SP), AX	// tv_sec from struct timespec
	IMULQ	$1000000000, AX	// multiply into nanoseconds
	ADDQ	8(SP), AX	// tv_nsec, offset should be stable.
	ADDQ	$64, SP
	RET

// pipe(3c) wrapper that returns fds in AX, DX.
TEXT runtime·pipe1(SB),NOSPLIT,$0
	SUBQ	$16, SP // 8 bytes will do, but stack has to be 16-byte alligned
	MOVQ	SP, DI
	MOVQ	libc·pipe(SB), AX
	CALL	AX
	MOVL	0(SP), AX
	MOVL	4(SP), DX
	ADDQ	$16, SP
	RET

// Call a library function with SysV calling conventions.
// The called function can take a maximum of 6 INTEGER class arguments,
// see 
//   Michael Matz, Jan Hubicka, Andreas Jaeger, and Mark Mitchell
//   System V Application Binary Interface 
//   AMD64 Architecture Processor Supplement
// section 3.2.3.
//
// Called by runtime·asmcgocall or runtime·cgocall.
TEXT runtime·asmsysvicall6(SB),NOSPLIT,$0
	// asmcgocall will put first argument into DI.
	PUSHQ	DI			// save for later
	MOVQ	libcall_fn(DI), AX
	MOVQ	libcall_args(DI), R11
	MOVQ	libcall_n(DI), R10

	get_tls(CX)
	MOVQ	m(CX), BX
	MOVQ	m_perrno(BX), DX
	CMPQ	DX, $0
	JEQ	skiperrno1
	MOVL	$0, 0(DX)

skiperrno1:
	CMPQ	R11, $0
	JEQ	skipargs
	// Load 6 args into correspondent registers.
	MOVQ	0(R11), DI
	MOVQ	8(R11), SI
	MOVQ	16(R11), DX
	MOVQ	24(R11), CX
	MOVQ	32(R11), R8
	MOVQ	40(R11), R9
skipargs:

	// Call SysV function
	CALL	AX

	// Return result
	POPQ	DI
	MOVQ	AX, libcall_r1(DI)
	MOVQ	DX, libcall_r2(DI)

	get_tls(CX)
	MOVQ	m(CX), BX
	MOVQ	m_perrno(BX), AX
	CMPQ	AX, $0
	JEQ	skiperrno2
	MOVL	0(AX), AX
	MOVQ	AX, libcall_err(DI)

skiperrno2:	
	RET

// uint32 tstart_sysvicall(M *newm);
TEXT runtime·tstart_sysvicall(SB),NOSPLIT,$0
	// DI contains first arg newm
	MOVQ	m_g0(DI), DX		// g

	// Make TLS entries point at g and m.
	get_tls(BX)
	MOVQ	DX, g(BX)
	MOVQ	DI, m(BX)

	// Layout new m scheduler stack on os stack.
	MOVQ	SP, AX
	MOVQ	AX, g_stackbase(DX)
	SUBQ	$(0x100000), AX		// stack size
	MOVQ	AX, g_stackguard(DX)
	MOVQ	AX, g_stackguard0(DX)

	// Someday the convention will be D is always cleared.
	CLD

	CALL	runtime·stackcheck(SB)	// clobbers AX,CX
	CALL	runtime·mstart(SB)

	XORL	AX, AX			// return 0 == success
	RET

// Careful, this is called by __sighndlr, a libc function. We must preserve
// registers as per AMD 64 ABI.
TEXT runtime·sigtramp(SB),NOSPLIT,$0
	// Note that we are executing on altsigstack here, so we have
	// more stack available than NOSPLIT would have us believe.
	// To defeat the linker, we make our own stack frame with
	// more space:
	SUBQ    $184, SP

	// save registers
	MOVQ    BX, 32(SP)
	MOVQ    BP, 40(SP)
	MOVQ	R12, 48(SP)
	MOVQ	R13, 56(SP)
	MOVQ	R14, 64(SP)
	MOVQ	R15, 72(SP)

	get_tls(BX)
	// check that m exists
	MOVQ	m(BX), BP
	CMPQ	BP, $0
	JNE	allgood
	MOVQ	DI, 0(SP)
	MOVQ	$runtime·badsignal(SB), AX
	CALL	AX
	RET

allgood:
	// save g
	MOVQ	g(BX), R10
	MOVQ	R10, 80(SP)

	// Save m->libcall and m->scratch. We need to do this because we
	// might get interrupted by a signal in runtime·asmcgocall.

	// save m->libcall 
	LEAQ	m_libcall(BP), R11
	MOVQ	libcall_fn(R11), R10
	MOVQ	R10, 88(SP)
	MOVQ	libcall_args(R11), R10
	MOVQ	R10, 96(SP)
	MOVQ	libcall_n(R11), R10
	MOVQ	R10, 104(SP)
	MOVQ    libcall_r1(R11), R10
	MOVQ    R10, 168(SP)
	MOVQ    libcall_r2(R11), R10
	MOVQ    R10, 176(SP)

	// save m->scratch
	LEAQ	m_scratch(BP), R11
	MOVQ	0(R11), R10
	MOVQ	R10, 112(SP)
	MOVQ	8(R11), R10
	MOVQ	R10, 120(SP)
	MOVQ	16(R11), R10
	MOVQ	R10, 128(SP)
	MOVQ	24(R11), R10
	MOVQ	R10, 136(SP)
	MOVQ	32(R11), R10
	MOVQ	R10, 144(SP)
	MOVQ	40(R11), R10
	MOVQ	R10, 152(SP)

	// save errno, it might be EINTR; stuff we do here might reset it.
	MOVQ	m_perrno(BP), R10
	MOVL	0(R10), R10
	MOVQ	R10, 160(SP)

	MOVQ	g(BX), R10
	// g = m->gsignal
	MOVQ	m_gsignal(BP), BP
	MOVQ	BP, g(BX)

	// prepare call
	MOVQ	DI, 0(SP)
	MOVQ	SI, 8(SP)
	MOVQ	DX, 16(SP)
	MOVQ	R10, 24(SP)
	CALL	runtime·sighandler(SB)

	get_tls(BX)
	MOVQ	m(BX), BP
	// restore libcall
	LEAQ	m_libcall(BP), R11
	MOVQ	88(SP), R10
	MOVQ	R10, libcall_fn(R11)
	MOVQ	96(SP), R10
	MOVQ	R10, libcall_args(R11)
	MOVQ	104(SP), R10
	MOVQ	R10, libcall_n(R11)
	MOVQ    168(SP), R10
	MOVQ    R10, libcall_r1(R11)
	MOVQ    176(SP), R10
	MOVQ    R10, libcall_r2(R11)

	// restore scratch
	LEAQ	m_scratch(BP), R11
	MOVQ	112(SP), R10
	MOVQ	R10, 0(R11)
	MOVQ	120(SP), R10
	MOVQ	R10, 8(R11)
	MOVQ	128(SP), R10
	MOVQ	R10, 16(R11)
	MOVQ	136(SP), R10
	MOVQ	R10, 24(R11)
	MOVQ	144(SP), R10
	MOVQ	R10, 32(R11)
	MOVQ	152(SP), R10
	MOVQ	R10, 40(R11)

	// restore errno
	MOVQ	m_perrno(BP), R11
	MOVQ	160(SP), R10
	MOVL	R10, 0(R11)

	// restore g
	MOVQ	80(SP), R10
	MOVQ	R10, g(BX)

	// restore registers
	MOVQ	32(SP), BX
	MOVQ	40(SP), BP
	MOVQ	48(SP), R12
	MOVQ	56(SP), R13
	MOVQ	64(SP), R14
	MOVQ	72(SP), R15

	ADDQ    $184, SP
	RET
