// 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 "go_asm.h"
#include "go_tls.h"
#include "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.
// NOT USING GO CALLING CONVENTION.
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	g(CX), BX
	MOVQ	g_m(BX), BX
	MOVQ	AX,	(m_mOS+mOS_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.
// NOT USING GO CALLING CONVENTION.
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
	LEAQ	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.
// NOT USING GO CALLING CONVENTION.
TEXT runtime·pipe1(SB),NOSPLIT,$0
	SUBQ	$16, SP // 8 bytes will do, but stack has to be 16-byte aligned
	MOVQ	SP, DI
	LEAQ	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.
// NOT USING GO CALLING CONVENTION.
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	g(CX), BX
	CMPQ	BX, $0
	JEQ	skiperrno1
	MOVQ	g_m(BX), BX
	MOVQ	(m_mOS+mOS_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	g(CX), BX
	CMPQ	BX, $0
	JEQ	skiperrno2
	MOVQ	g_m(BX), BX
	MOVQ	(m_mOS+mOS_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, g_m(DX)

	// Layout new m scheduler stack on os stack.
	MOVQ	SP, AX
	MOVQ	AX, (g_stack+stack_hi)(DX)
	SUBQ	$(0x100000), AX		// stack size
	MOVQ	AX, (g_stack+stack_lo)(DX)
	ADDQ	$const__StackGuard, AX
	MOVQ	AX, g_stackguard0(DX)
	MOVQ	AX, g_stackguard1(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
	MOVL	AX, ret+8(FP)
	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 g exists
	MOVQ	g(BX), R10
	CMPQ	R10, $0
	JNE	allgood
	MOVQ	SI, 80(SP)
	MOVQ	DX, 88(SP)
	LEAQ	80(SP), AX
	MOVQ	DI, 0(SP)
	MOVQ	AX, 8(SP)
	MOVQ	$runtime·badsignal(SB), AX
	CALL	AX
	JMP	exit

allgood:
	// save g
	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 
	MOVQ	g_m(R10), BP
	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_mOS+mOS_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_mOS+mOS_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)

	// TODO: If current SP is not in gsignal.stack, then adjust.

	// 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	g(BX), BP
	MOVQ	g_m(BP), 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_mOS+mOS_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_mOS+mOS_perrno)(BP), R11
	MOVQ	160(SP), R10
	MOVL	R10, 0(R11)

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

exit:
	// 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

TEXT runtime·sigfwd(SB),NOSPLIT,$0-32
	MOVQ	fn+0(FP),    AX
	MOVL	sig+8(FP),   DI
	MOVQ	info+16(FP), SI
	MOVQ	ctx+24(FP),  DX
	PUSHQ	BP
	MOVQ	SP, BP
	ANDQ	$~15, SP     // alignment for x86_64 ABI
	CALL	AX
	MOVQ	BP, SP
	POPQ	BP
	RET

// Called from runtime·usleep (Go). Can be called on Go stack, on OS stack,
// can also be called in cgo callback path without a g->m.
TEXT runtime·usleep1(SB),NOSPLIT,$0
	MOVL	usec+0(FP), DI
	MOVQ	$runtime·usleep2(SB), AX // to hide from 6l

	// Execute call on m->g0.
	get_tls(R15)
	CMPQ	R15, $0
	JE	noswitch

	MOVQ	g(R15), R13
	CMPQ	R13, $0
	JE	noswitch
	MOVQ	g_m(R13), R13
	CMPQ	R13, $0
	JE	noswitch
	// TODO(aram): do something about the cpu profiler here.

	MOVQ	m_g0(R13), R14
	CMPQ	g(R15), R14
	JNE	switch
	// executing on m->g0 already
	CALL	AX
	RET

switch:
	// Switch to m->g0 stack and back.
	MOVQ	(g_sched+gobuf_sp)(R14), R14
	MOVQ	SP, -8(R14)
	LEAQ	-8(R14), SP
	CALL	AX
	MOVQ	0(SP), SP
	RET

noswitch:
	// Not a Go-managed thread. Do not switch stack.
	CALL	AX
	RET

// Runs on OS stack. duration (in µs units) is in DI.
TEXT runtime·usleep2(SB),NOSPLIT,$0
	LEAQ	libc_usleep(SB), AX
	CALL	AX
	RET

// Runs on OS stack, called from runtime·osyield.
TEXT runtime·osyield1(SB),NOSPLIT,$0
	LEAQ	libc_sched_yield(SB), AX
	CALL	AX
	RET

// func walltime() (sec int64, nsec int32)
TEXT runtime·walltime(SB),NOSPLIT,$8-12
	CALL	runtime·nanotime(SB)
	MOVQ	0(SP), AX

	// generated code for
	//	func f(x uint64) (uint64, uint64) { return x/1000000000, x%100000000 }
	// adapted to reduce duplication
	MOVQ	AX, CX
	MOVQ	$1360296554856532783, AX
	MULQ	CX
	ADDQ	CX, DX
	RCRQ	$1, DX
	SHRQ	$29, DX
	MOVQ	DX, sec+0(FP)
	IMULQ	$1000000000, DX
	SUBQ	DX, CX
	MOVL	CX, nsec+8(FP)
	RET
