// Copyright 2015 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 ARM64, Darwin
// System calls are implemented in libSystem, this file contains
// trampolines that convert from Go to C calling convention.

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

TEXT notok<>(SB),NOSPLIT,$0
	MOVD	$0, R8
	MOVD	R8, (R8)
	B	0(PC)

TEXT runtime·open_trampoline(SB),NOSPLIT,$0
	SUB	$16, RSP
	MOVW	8(R0), R1	// arg 2 flags
	MOVW	12(R0), R2	// arg 3 mode
	MOVW	R2, (RSP)	// arg 3 is variadic, pass on stack
	MOVD	0(R0), R0	// arg 1 pathname
	BL	libc_open(SB)
	ADD	$16, RSP
	RET

TEXT runtime·close_trampoline(SB),NOSPLIT,$0
	MOVW	0(R0), R0	// arg 1 fd
	BL	libc_close(SB)
	RET

TEXT runtime·write_trampoline(SB),NOSPLIT,$0
	MOVD	8(R0), R1	// arg 2 buf
	MOVW	16(R0), R2	// arg 3 count
	MOVW	0(R0), R0	// arg 1 fd
	BL	libc_write(SB)
	RET

TEXT runtime·read_trampoline(SB),NOSPLIT,$0
	MOVD	8(R0), R1	// arg 2 buf
	MOVW	16(R0), R2	// arg 3 count
	MOVW	0(R0), R0	// arg 1 fd
	BL libc_read(SB)
	RET

TEXT runtime·exit_trampoline(SB),NOSPLIT|NOFRAME,$0
	MOVW	0(R0), R0
	BL	libc_exit(SB)
	MOVD	$1234, R0
	MOVD	$1002, R1
	MOVD	R0, (R1)	// fail hard

TEXT runtime·raiseproc_trampoline(SB),NOSPLIT,$0
	MOVD	0(R0), R19	// signal
	BL	libc_getpid(SB)
	// arg 1 pid already in R0 from getpid
	MOVD	R19, R1	// arg 2 signal
	BL	libc_kill(SB)
	RET

TEXT runtime·mmap_trampoline(SB),NOSPLIT,$0
	MOVD	R0, R19
	MOVD	0(R19), R0	// arg 1 addr
	MOVD	8(R19), R1	// arg 2 len
	MOVW	16(R19), R2	// arg 3 prot
	MOVW	20(R19), R3	// arg 4 flags
	MOVW	24(R19), R4	// arg 5 fd
	MOVW	28(R19), R5	// arg 6 off
	BL	libc_mmap(SB)
	MOVD	$0, R1
	MOVD	$-1, R2
	CMP	R0, R2
	BNE	ok
	BL libc_error(SB)
	MOVW	(R0), R1
	MOVD	$0, R0
ok:
	MOVD	R0, 32(R19) // ret 1 p
	MOVD	R1, 40(R19)	// ret 2 err
	RET

TEXT runtime·munmap_trampoline(SB),NOSPLIT,$0
	MOVD	8(R0), R1	// arg 2 len
	MOVD	0(R0), R0	// arg 1 addr
	BL	libc_munmap(SB)
	CMP $0, R0
	BEQ 2(PC)
	BL	notok<>(SB)
	RET

TEXT runtime·madvise_trampoline(SB),NOSPLIT,$0
	MOVD	8(R0), R1	// arg 2 len
	MOVW	16(R0), R2	// arg 3 advice
	MOVD	0(R0), R0	// arg 1 addr
	BL	libc_madvise(SB)
	RET

TEXT runtime·setitimer_trampoline(SB),NOSPLIT,$0
	MOVD	8(R0), R1	// arg 2 new
	MOVD	16(R0), R2	// arg 3 old
	MOVW	0(R0), R0	// arg 1 which
	BL	libc_setitimer(SB)
	RET

TEXT runtime·walltime_trampoline(SB),NOSPLIT,$0
	// R0 already has *timeval
	MOVD	$0, R1 // no timezone needed
	BL	libc_gettimeofday(SB)
	RET

GLOBL timebase<>(SB),NOPTR,$(machTimebaseInfo__size)

TEXT runtime·nanotime_trampoline(SB),NOSPLIT,$40
	MOVD	R0, R19
	BL	libc_mach_absolute_time(SB)
	MOVD	R0, 0(R19)
	MOVW	timebase<>+machTimebaseInfo_numer(SB), R20
	MOVD	$timebase<>+machTimebaseInfo_denom(SB), R21
	LDARW	(R21), R21	// atomic read
	CMP	$0, R21
	BNE	initialized

	SUB	$(machTimebaseInfo__size+15)/16*16, RSP
	MOVD	RSP, R0
	BL	libc_mach_timebase_info(SB)
	MOVW	machTimebaseInfo_numer(RSP), R20
	MOVW	machTimebaseInfo_denom(RSP), R21
	ADD	$(machTimebaseInfo__size+15)/16*16, RSP

	MOVW	R20, timebase<>+machTimebaseInfo_numer(SB)
	MOVD	$timebase<>+machTimebaseInfo_denom(SB), R22
	STLRW	R21, (R22)	// atomic write

initialized:
	MOVW	R20, 8(R19)
	MOVW	R21, 12(R19)
	RET

TEXT runtime·sigfwd(SB),NOSPLIT,$0-32
	MOVW	sig+8(FP), R0
	MOVD	info+16(FP), R1
	MOVD	ctx+24(FP), R2
	MOVD	fn+0(FP), R11
	BL	(R11)
	RET

TEXT runtime·sigtramp(SB),NOSPLIT,$0
	// Reserve space for callee-save registers and arguments.
	SUB	$(8*16), RSP

	// Save callee-save registers.
	MOVD	R19, (8*4)(RSP)
	MOVD	R20, (8*5)(RSP)
	MOVD	R21, (8*6)(RSP)
	MOVD	R22, (8*7)(RSP)
	MOVD	R23, (8*8)(RSP)
	MOVD	R24, (8*9)(RSP)
	MOVD	R25, (8*10)(RSP)
	MOVD	R26, (8*11)(RSP)
	MOVD	R27, (8*12)(RSP)
	MOVD	g, (8*13)(RSP)
	MOVD	R29, (8*14)(RSP)

	// Save arguments.
	MOVW	R0, (8*1)(RSP)	// sig
	MOVD	R1, (8*2)(RSP)	// info
	MOVD	R2, (8*3)(RSP)	// ctx

	// this might be called in external code context,
	// where g is not set.
	MOVB	runtime·iscgo(SB), R0
	CMP	$0, R0
	BEQ	2(PC)
	BL	runtime·load_g(SB)

	MOVD RSP, R6
	CMP	 $0, g
	BEQ	 nog
	// iOS always use the main stack to run the signal handler.
	// We need to switch to gsignal ourselves.
	MOVD	g_m(g), R11
	MOVD	m_gsignal(R11), R5
	MOVD	(g_stack+stack_hi)(R5), R6

nog:
	// Restore arguments.
	MOVW	(8*1)(RSP), R0
	MOVD	(8*2)(RSP), R1
	MOVD	(8*3)(RSP), R2

	// Reserve space for args and the stack pointer on the
	// gsignal stack.
	SUB	$48, R6
	// Save stack pointer.
	MOVD	RSP, R4
	MOVD	R4, (8*4)(R6)
	// Switch to gsignal stack.
	MOVD	R6, RSP

	// Call sigtrampgo.
	MOVW	R0, (8*1)(RSP)
	MOVD	R1, (8*2)(RSP)
	MOVD	R2, (8*3)(RSP)
	MOVD	$runtime·sigtrampgo(SB), R11
	BL	(R11)

	// Switch to old stack.
	MOVD	(8*4)(RSP), R5
	MOVD	R5, RSP

	// Restore callee-save registers.
	MOVD	(8*4)(RSP), R19
	MOVD	(8*5)(RSP), R20
	MOVD	(8*6)(RSP), R21
	MOVD	(8*7)(RSP), R22
	MOVD	(8*8)(RSP), R23
	MOVD	(8*9)(RSP), R24
	MOVD	(8*10)(RSP), R25
	MOVD	(8*11)(RSP), R26
	MOVD	(8*12)(RSP), R27
	MOVD	(8*13)(RSP), g
	MOVD	(8*14)(RSP), R29

	ADD $(8*16), RSP

	RET

TEXT runtime·cgoSigtramp(SB),NOSPLIT,$0
	JMP	runtime·sigtramp(SB)

TEXT runtime·sigprocmask_trampoline(SB),NOSPLIT,$0
	MOVD	8(R0), R1	// arg 2 new
	MOVD	16(R0), R2	// arg 3 old
	MOVW	0(R0), R0	// arg 1 how
	BL	libc_pthread_sigmask(SB)
	CMP $0, R0
	BEQ	2(PC)
	BL	notok<>(SB)
	RET

TEXT runtime·sigaction_trampoline(SB),NOSPLIT,$0
	MOVD	8(R0), R1	// arg 2 new
	MOVD	16(R0), R2	// arg 3 old
	MOVW	0(R0), R0	// arg 1 how
	BL	libc_sigaction(SB)
	CMP	$0, R0
	BEQ	2(PC)
	BL	notok<>(SB)
	RET

TEXT runtime·usleep_trampoline(SB),NOSPLIT,$0
	MOVW	0(R0), R0	// arg 1 usec
	BL	libc_usleep(SB)
	RET

TEXT runtime·sysctl_trampoline(SB),NOSPLIT,$0
	MOVW	8(R0), R1	// arg 2 miblen
	MOVD	16(R0), R2	// arg 3 out
	MOVD	24(R0), R3	// arg 4 size
	MOVD	32(R0), R4	// arg 5 dst
	MOVD	40(R0), R5	// arg 6 ndst
	MOVD	0(R0), R0	// arg 1 mib
	BL	libc_sysctl(SB)
	RET

TEXT runtime·kqueue_trampoline(SB),NOSPLIT,$0
	BL	libc_kqueue(SB)
	RET

TEXT runtime·kevent_trampoline(SB),NOSPLIT,$0
	MOVD	8(R0), R1	// arg 2 keventt
	MOVW	16(R0), R2	// arg 3 nch
	MOVD	24(R0), R3	// arg 4 ev
	MOVW	32(R0), R4	// arg 5 nev
	MOVD	40(R0), R5	// arg 6 ts
	MOVW	0(R0), R0	// arg 1 kq
	BL	libc_kevent(SB)
	MOVD	$-1, R2
	CMP	R0, R2
	BNE	ok
	BL libc_error(SB)
	MOVW	(R0), R0	// errno
	NEG	R0, R0	// caller wants it as a negative error code
ok:
	RET

TEXT runtime·fcntl_trampoline(SB),NOSPLIT,$0
	SUB	$16, RSP
	MOVW	4(R0), R1	// arg 2 cmd
	MOVW	8(R0), R2	// arg 3 arg
	MOVW	R2, (RSP)	// arg 3 is variadic, pass on stack
	MOVW	0(R0), R0	// arg 1 fd
	BL	libc_fcntl(SB)
	ADD	$16, RSP
	RET

// sigaltstack on iOS is not supported and will always
// run the signal handler on the main stack, so our sigtramp has
// to do the stack switch ourselves.
TEXT runtime·sigaltstack_trampoline(SB),NOSPLIT,$0
	MOVW	$43, R0
	BL	libc_exit(SB)
	RET

// Thread related functions
// Note: On darwin/arm64, the runtime always use runtime/cgo to
// create threads, so all thread related functions will just exit with a
// unique status.

TEXT runtime·mstart_stub(SB),NOSPLIT,$0
	MOVW	$44, R0
	BL	libc_exit(SB)
	RET

TEXT runtime·pthread_attr_init_trampoline(SB),NOSPLIT,$0
	MOVW	$45, R0
	BL	libc_exit(SB)
	RET

TEXT runtime·pthread_attr_setstacksize_trampoline(SB),NOSPLIT,$0
	MOVW	$46, R0
	BL	libc_exit(SB)
	RET

TEXT runtime·pthread_attr_setdetachstate_trampoline(SB),NOSPLIT,$0
	MOVW	$47, R0
	BL	libc_exit(SB)
	RET

TEXT runtime·pthread_create_trampoline(SB),NOSPLIT,$0
	MOVW	$48, R0
	BL	libc_exit(SB)
	RET

TEXT runtime·raise_trampoline(SB),NOSPLIT,$0
	MOVW	0(R0), R0	// arg 1 sig
	BL	libc_raise(SB)
	RET

TEXT runtime·pthread_mutex_init_trampoline(SB),NOSPLIT,$0
	MOVD	8(R0), R1	// arg 2 attr
	MOVD	0(R0), R0	// arg 1 mutex
	BL	libc_pthread_mutex_init(SB)
	RET

TEXT runtime·pthread_mutex_lock_trampoline(SB),NOSPLIT,$0
	MOVD	0(R0), R0	// arg 1 mutex
	BL	libc_pthread_mutex_lock(SB)
	RET

TEXT runtime·pthread_mutex_unlock_trampoline(SB),NOSPLIT,$0
	MOVD	0(R0), R0	// arg 1 mutex
	BL	libc_pthread_mutex_unlock(SB)
	RET

TEXT runtime·pthread_cond_init_trampoline(SB),NOSPLIT,$0
	MOVD	8(R0), R1	// arg 2 attr
	MOVD	0(R0), R0	// arg 1 cond
	BL	libc_pthread_cond_init(SB)
	RET

TEXT runtime·pthread_cond_wait_trampoline(SB),NOSPLIT,$0
	MOVD	8(R0), R1	// arg 2 mutex
	MOVD	0(R0), R0	// arg 1 cond
	BL	libc_pthread_cond_wait(SB)
	RET

TEXT runtime·pthread_cond_timedwait_relative_np_trampoline(SB),NOSPLIT,$0
	MOVD	8(R0), R1	// arg 2 mutex
	MOVD	16(R0), R2	// arg 3 timeout
	MOVD	0(R0), R0	// arg 1 cond
	BL	libc_pthread_cond_timedwait_relative_np(SB)
	RET

TEXT runtime·pthread_cond_signal_trampoline(SB),NOSPLIT,$0
	MOVD	0(R0), R0	// arg 1 cond
	BL	libc_pthread_cond_signal(SB)
	RET

// syscall calls a function in libc on behalf of the syscall package.
// syscall takes a pointer to a struct like:
// struct {
//	fn    uintptr
//	a1    uintptr
//	a2    uintptr
//	a3    uintptr
//	r1    uintptr
//	r2    uintptr
//	err   uintptr
// }
// syscall must be called on the g0 stack with the
// C calling convention (use libcCall).
TEXT runtime·syscall(SB),NOSPLIT,$0
	SUB	$16, RSP	// push structure pointer
	MOVD	R0, 8(RSP)

	MOVD	0(R0), R12	// fn
	MOVD	16(R0), R1	// a2
	MOVD	24(R0), R2	// a3
	MOVD	8(R0), R0	// a1

	// If fn is declared as vararg, we have to pass the vararg arguments on the stack.
	// (Because ios decided not to adhere to the standard arm64 calling convention, sigh...)
	// The only libSystem calls we support that are vararg are open, fcntl, and ioctl,
	// which are all of the form fn(x, y, ...). So we just need to put the 3rd arg
	// on the stack as well.
	// If we ever have other vararg libSystem calls, we might need to handle more cases.
	MOVD	R2, (RSP)

	BL	(R12)

	MOVD	8(RSP), R2	// pop structure pointer
	ADD	$16, RSP
	MOVD	R0, 32(R2)	// save r1
	MOVD	R1, 40(R2)	// save r2
	CMPW	$-1, R0
	BNE	ok
	SUB	$16, RSP	// push structure pointer
	MOVD	R2, 8(RSP)
	BL	libc_error(SB)
	MOVW	(R0), R0
	MOVD	8(RSP), R2	// pop structure pointer
	ADD	$16, RSP
	MOVD	R0, 48(R2)	// save err
ok:
	RET

// syscallX calls a function in libc on behalf of the syscall package.
// syscallX takes a pointer to a struct like:
// struct {
//	fn    uintptr
//	a1    uintptr
//	a2    uintptr
//	a3    uintptr
//	r1    uintptr
//	r2    uintptr
//	err   uintptr
// }
// syscallX must be called on the g0 stack with the
// C calling convention (use libcCall).
TEXT runtime·syscallX(SB),NOSPLIT,$0
	SUB	$16, RSP	// push structure pointer
	MOVD	R0, (RSP)

	MOVD	0(R0), R12	// fn
	MOVD	16(R0), R1	// a2
	MOVD	24(R0), R2	// a3
	MOVD	8(R0), R0	// a1
	BL	(R12)

	MOVD	(RSP), R2	// pop structure pointer
	ADD	$16, RSP
	MOVD	R0, 32(R2)	// save r1
	MOVD	R1, 40(R2)	// save r2
	CMP	$-1, R0
	BNE	ok
	SUB	$16, RSP	// push structure pointer
	MOVD	R2, (RSP)
	BL	libc_error(SB)
	MOVW	(R0), R0
	MOVD	(RSP), R2	// pop structure pointer
	ADD	$16, RSP
	MOVD	R0, 48(R2)	// save err
ok:
	RET

// syscallXPtr is like syscallX except that the libc function reports an
// error by returning NULL.
TEXT runtime·syscallXPtr(SB),NOSPLIT,$0
	SUB	$16, RSP	// push structure pointer
	MOVD	R0, (RSP)

	MOVD	0(R0), R12	// fn
	MOVD	16(R0), R1	// a2
	MOVD	24(R0), R2	// a3
	MOVD	8(R0), R0	// a1
	BL	(R12)

	MOVD	(RSP), R2	// pop structure pointer
	ADD	$16, RSP
	MOVD	R0, 32(R2)	// save r1
	MOVD	R1, 40(R2)	// save r2
	CMP	$0, R0
	BNE	ok
	SUB	$16, RSP	// push structure pointer
	MOVD	R2, (RSP)
	BL	libc_error(SB)
	MOVW	(R0), R0
	MOVD	(RSP), R2	// pop structure pointer
	ADD	$16, RSP
	MOVD	R0, 48(R2)	// save err
ok:
	RET

// syscall6 calls a function in libc on behalf of the syscall package.
// syscall6 takes a pointer to a struct like:
// struct {
//	fn    uintptr
//	a1    uintptr
//	a2    uintptr
//	a3    uintptr
//	a4    uintptr
//	a5    uintptr
//	a6    uintptr
//	r1    uintptr
//	r2    uintptr
//	err   uintptr
// }
// syscall6 must be called on the g0 stack with the
// C calling convention (use libcCall).
TEXT runtime·syscall6(SB),NOSPLIT,$0
	SUB	$16, RSP	// push structure pointer
	MOVD	R0, 8(RSP)

	MOVD	0(R0), R12	// fn
	MOVD	16(R0), R1	// a2
	MOVD	24(R0), R2	// a3
	MOVD	32(R0), R3	// a4
	MOVD	40(R0), R4	// a5
	MOVD	48(R0), R5	// a6
	MOVD	8(R0), R0	// a1

	// If fn is declared as vararg, we have to pass the vararg arguments on the stack.
	// See syscall above. The only function this applies to is openat, for which the 4th
	// arg must be on the stack.
	MOVD	R3, (RSP)

	BL	(R12)

	MOVD	8(RSP), R2	// pop structure pointer
	ADD	$16, RSP
	MOVD	R0, 56(R2)	// save r1
	MOVD	R1, 64(R2)	// save r2
	CMPW	$-1, R0
	BNE	ok
	SUB	$16, RSP	// push structure pointer
	MOVD	R2, 8(RSP)
	BL	libc_error(SB)
	MOVW	(R0), R0
	MOVD	8(RSP), R2	// pop structure pointer
	ADD	$16, RSP
	MOVD	R0, 72(R2)	// save err
ok:
	RET

// syscall6X calls a function in libc on behalf of the syscall package.
// syscall6X takes a pointer to a struct like:
// struct {
//	fn    uintptr
//	a1    uintptr
//	a2    uintptr
//	a3    uintptr
//	a4    uintptr
//	a5    uintptr
//	a6    uintptr
//	r1    uintptr
//	r2    uintptr
//	err   uintptr
// }
// syscall6X must be called on the g0 stack with the
// C calling convention (use libcCall).
TEXT runtime·syscall6X(SB),NOSPLIT,$0
	SUB	$16, RSP	// push structure pointer
	MOVD	R0, (RSP)

	MOVD	0(R0), R12	// fn
	MOVD	16(R0), R1	// a2
	MOVD	24(R0), R2	// a3
	MOVD	32(R0), R3	// a4
	MOVD	40(R0), R4	// a5
	MOVD	48(R0), R5	// a6
	MOVD	8(R0), R0	// a1
	BL	(R12)

	MOVD	(RSP), R2	// pop structure pointer
	ADD	$16, RSP
	MOVD	R0, 56(R2)	// save r1
	MOVD	R1, 64(R2)	// save r2
	CMP	$-1, R0
	BNE	ok
	SUB	$16, RSP	// push structure pointer
	MOVD	R2, (RSP)
	BL	libc_error(SB)
	MOVW	(R0), R0
	MOVD	(RSP), R2	// pop structure pointer
	ADD	$16, RSP
	MOVD	R0, 72(R2)	// save err
ok:
	RET
