/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8     -*-│
│vi: set et ft=asm ts=8 sw=8 fenc=utf-8                                     :vi│
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2020 Justine Alexandra Roberts Tunney                              │
│                                                                              │
│ Permission to use, copy, modify, and/or distribute this software for         │
│ any purpose with or without fee is hereby granted, provided that the         │
│ above copyright notice and this permission notice appear in all copies.      │
│                                                                              │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL                │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED                │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE             │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL         │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR        │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER               │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR             │
│ PERFORMANCE OF THIS SOFTWARE.                                                │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/dce.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/consts/nr.h"
#include "libc/sysv/consts/map.h"
#include "libc/macros.internal.h"

/*                                           ▄▄▄
                       ▄▄▄                    ▀▓▓▒▄
                     ▄▓▒▒░                      ▀▓▒▒▒▄
                   ▄▓▓▓▒▀              ▄▄▄▄      ▒▓▒▒░▒▄
                  ▄▓▓▓▒▓        ▄▄▓██▓▓▓▓▒▒▒▒▓▓▄▄▓▓▒▒▒░░▒
                  ▓▓▓▓▒▒▒▄▄  ░▒█▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▓▒░░▒░
                  ██▓▓▓▒▒░░▒▒▒▒▓▓▓▓▓▓▒▓▒░▒▒░▀▒▒▒▒░▀░▒▒▒░▒
                  ▓▓▓▓▓▓▓▒▒▒▒▒▒▓▓▒▓▓▒▒▒░▒▒░░  ░▒▒░  ░▒▒▒▒
                   ▀▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒░░▒░░    ░▒▒  ░ ▀▒▒
                     ▀▓▓█▓▓▓▓▓▓▓▓▓▓▒▒░░▒▒░░   ░░░▓░ ▓░░░▒
                       ▀▀█▓███▓▓▓▓▓▒▒░░░▒░░  ░█▓░█▓░█▓▓▄▒░
                          ░▓██▓▓▓▓▓▒▒░░░▒░░  ░████▓▒▓█▓▀░▀▄
                          ░▓██▓▓▓▓▓▒▒▒░░░▒░░  ▒██▓▒▒▒▒▒▒░░░▒
                           ████▓▓▓▓▓▒▒▒▒▒▒▒▒▒░░▒▓▓▒░░░░▒░░░▒░ ░░░░░
                           ░▓███▓▓▓▓▓▒▒░░░░░░░▒▒▒▒▒▒▒▒▒▒▒░░░ ░░░░░   ░
                             ▓███▓▓▓▓▓▒▓▒▒▒▒░░░░░░░░░▒▓▒▒░▀ ░░░  ░░░░░
                              ▀▒██▓▓▓▓▒▒▒▓▓▓▓▒▒▒▒▒▒▒▓▀▀░    ░░░░░░░░░     ░
                                 ▓▓▓▓▓▓▓▒▓▒▒▒▒▓▓▓▒▀░ ░░░░░▄░░░  ░░░  ░░░░░░
                                 ▓▓▓▒▒▒▒▒▒▒▒▒▒▒▓     █▓▒░░▒░░░░ ░░░░░░░░
                                ▄▓▓▓▒▒▒▒▒░░░░░░░▒▄▄▄░▒▓▓▒▒░▀░
                               ░▓█▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▒░░░▒  besiyata
                               ▓▓█▓▓▒▓▓▓▒▒▒░░░░░░▒▓▓▓▓▒▒▒▒▒░   dishmaya
                               ▓▓█▓▓▓▓▓▓▒▒▒░░░░░░░▒▓▓▒▀▀▀
                               ▓▓██▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▀
                                █▓▓█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀
                               ▒▓▓▓▓▀░░▒▓▓▓▓▓▓▓▓▒▒░░▒
                              ▄▓▓▀░░░▄▓▓▓▓▒▒▒▒▒░░░░▄░
                             ▄███▄▄▓▓▓▓▓▓▓▒▒▒▒▒░░▒▒░
                           ▄▓▓▓█▓█▓▓███▓▓▓▓▓▓▓▓▓▓▓░
                       ▄░▓▓▓▓▓▓▀▒▓▓▓▒▒▓▒░░░▒▓▒░░░▓
               ▄▄▄░▒▓▓▓▓▓▓░▀▀   ▓▓▒░▓▒▒▒▒▒▒▒▒▒▒▄░░▀▀░░ ▄▄▄▄
     ▄▄▄▒▒▓▓█▓▓▓▓▓▀▀▀▀▀        ▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▀░░▀░░▒▒▒░░░   ░░░░░
  ▄▓▓▓▒▀▀                      ▓▒▓▓▓▓▓▒▒▒▒▒▒▒▒▓░░░       ▒▒▒░░░░░░░░▒
  █▓▓▒      ▄▄▄                  ▀▓▒▓▒▒▒▓▓▓▓▓▓▒▒▒░░░░░░░░░▒▒░░░░░░░
   ▀▓▓▓▓▒▄▄▒▒▒▒▒▒▄▄                    ▀▀▀▀░░▒▒▒▒░░░░░░
       ▀▀▀▓▓▓▓▒▒▒▒▒▓▓▄▄
╔────────────────────────────────────────────────────────────────────────────│─╗
│ cosmopolitan § bell system five » system call support                    ─╬─│┼
╚────────────────────────────────────────────────────────────────────────────│*/

	.initbss 300,_init_systemfive
__hostos:
	.quad	0
	.endobj	__hostos,globl,hidden

//	Performs System Five System Call.
//
//	Cosmopolitan is designed to delegate all function calls into the
//	Linux, FreeBSD, OpenBSD, and XNU kernels via this function, with
//	few exceptions. This function should generally only be called by
//	generated thunks in the libc/sysv/syscalls/ directory.
//
//	It's safe to call this function on Windows, where it will always
//	return -1 with errno == ENOSYS. Further note that -1 is the only
//	return value that means error, a common anti-pattern is to check
//	for values less than 0 (which is more problematic on 32-bit).
//
//	It is important to consider that system calls are one order of a
//	magnitude more expensive than normal function calls. For example
//	getpid() on Linux usually takes 500ns, and cached i/o calls will
//	take 1µs or more. So we don't need to inline them like Chromium.
//
//	Another thing to consider is that BSDs only loosely follow the
//	System Five ABI for the SYSCALL instruction. For example Linux
//	always follows the six argument limit but the FreeBSD sendfile
//	system call accepts a seventh argument that is passed on stack
//	and OpenBSD modifies functions like mmap so that the sixth arg
//	is passed on the stack. There's also the carry flag convention
//	that XNU, FreeBSD, and OpenBSD inherited from 386BSD aka Jolix
//
//	@param	%rax function ordinal supplied by jump slot
//	@param	%rdi,%rsi,%rdx,%rcx,%r8,%r9 and rest on stack
//	@return	%rax:%rdx is result, or -1 w/ errno on error
//	@clob	%rcx,%r10,%r11
//	@see	syscalls.sh
__systemfive:
	.quad	0
	.endobj	__systemfive,globl,hidden
	.previous

	.privileged
.Lanchorpoint:
#if SupportsLinux() || SupportsMetal()
systemfive_linux:
	and	$0xfff,%eax
	cmp	$0xfff,%eax
	je	systemfive_enosys
	mov	%rcx,%r10		# syscall instruction clobbers %rcx
	push	%rbp			# linux never reads args from stack
	mov	%rsp,%rbp		# having frame will help backtraces
	syscall				# this is known as a context switch
	pop	%rbp			# next we check to see if it failed
	cmp	$-4095,%rax		# system five nexgen32e abi § A.2.1
	jae	systemfive_error	# encodes errno as neg return value
	ret
	.endfn	systemfive_linux,globl,hidden
systemfive_error:
	neg	%eax
//	𝑠𝑙𝑖𝑑𝑒
	.endfn	systemfive_error,globl,hidden
#endif
systemfive_errno:
	mov	%eax,errno(%rip)	# normalize to c library convention
	push	$-1			# negative one is only error result
	pop	%rax			# the push pop is to save code size
	ret
	.endfn	systemfive_errno,globl,hidden
systemfive_enosys:
	mov	ENOSYS(%rip),%eax
	jmp	systemfive_errno
	.endfn	systemfive_enosys,globl,hidden
#if SupportsNetbsd()
systemfive_netbsd:
	shr	$4*13,%rax
	jmp	systemfive_bsdscrub
	.endfn	systemfive_netbsd,globl,hidden
#endif
#if SupportsOpenbsd()
systemfive_openbsd:
	shr	$4*10,%rax
	jmp	systemfive_bsdscrub
	.endfn	systemfive_openbsd,globl,hidden
#endif
#if SupportsFreebsd()
systemfive_freebsd:
	shr	$4*7,%rax
	movzwl	%ax,%eax
//	𝑠𝑙𝑖𝑑𝑒
	.endfn	systemfive_freebsd,globl,hidden
#endif
#if SupportsBsd()
systemfive_bsdscrub:
	and	$0xfff,%eax
//	𝑠𝑙𝑖𝑑𝑒
	.endfn	systemfive_bsdscrub,globl,hidden
systemfive_bsd:
	cmp	$0xfff,%ax
	je	systemfive_enosys
	mov	%rcx,%r10		# note: we do not create a stack frame
	syscall				# bsd will need arg on stack sometimes
	jc	systemfive_errno	# bsd sets carry flag if %rax is errno
	ret
	.endfn	systemfive_bsd
#endif
#if SupportsXnu()
systemfive_xnu:
//	0x?????????2153???		# how syscalls.sh encodes xnu ordinals
//	           │└┴┴┐
//	           │   ├┬┐
//	0x0000000002000153		# how xnu wants ordinals to be encoded
	mov	%eax,%r11d
	and	$0x0f000000,%r11d
	shl	$8,%eax
	shr	$20,%eax
	or	%r11d,%eax
	jmp	systemfive_bsd
	.endfn	systemfive_xnu,globl,hidden
#endif
	.previous

//	Initializes System Five system call support.
//
//	  (1) Extracts parameters passed by kernel
//	  (2) Detects OS without issuing system calls
//	  (3) Unpacks magnums from libc/sysv/consts.sh
//	  (4) Replaces stack with one we control
//
//	@param	%r15 is auxv
//	@note	OpenBSD devs: let us know if you start using auxv
	.init.start 300,_init_systemfive
	push	%rbx
	push	%rsi
	mov	(%rdi),%eax
#if SupportsOpenbsd()
	cmpq	$0,(%r15)			# OpenBSD has no auxv
	jnz	0f
	mov	$OPENBSD,%al
0:
#endif
#if SupportsNetbsd()
	xor	%ecx,%ecx
0:	cmpq	$2014,(%r15,%rcx,8)		# NetBSD AT_EXECFN
	jne	1f
	mov	$NETBSD,%al
1:	cmpq	$0,(%r15,%rcx,8)
	lea	2(%ecx),%ecx
	jnz	0b
2:
#endif
	test	%eax,%eax
	jnz	1f
	mov	$LINUX,%al
1:	stosq	#→ __hostos
	bsr	%eax,%eax
	mov	$_init_systemfive_jmptab,%ebx
	movzbl	(%rbx,%rax),%eax
	lea	(%rbx,%rax),%eax
	jmp	*%rax
_init_systemfive_jmptab:
	.byte	_init_systemfive_linux-_init_systemfive_jmptab
	.byte	_init_systemfive_metal-_init_systemfive_jmptab
	.byte	_init_systemfive_windows-_init_systemfive_jmptab
	.byte	_init_systemfive_xnu-_init_systemfive_jmptab
	.byte	_init_systemfive_openbsd-_init_systemfive_jmptab
	.byte	_init_systemfive_freebsd-_init_systemfive_jmptab
	.byte	_init_systemfive_netbsd-_init_systemfive_jmptab
	.endobj	_init_systemfive_jmptab
_init_systemfive_linux:
#if SupportsLinux()
	pushb	systemfive_linux-.Lanchorpoint
	mov	$syscon_linux,%esi
	jmp	_init_systemfive_os
#endif
	.endfn	_init_systemfive_linux
_init_systemfive_metal:
#if SupportsMetal()
	pushb	systemfive_linux-.Lanchorpoint
	mov	$syscon_linux,%esi
	jmp	_init_systemfive_os
#endif
	.endfn	_init_systemfive_metal
_init_systemfive_windows:
#if SupportsWindows()
	pushb	systemfive_enosys-.Lanchorpoint
	mov	$syscon_windows,%esi
	jmp	_init_systemfive_os
#endif
	.endfn	_init_systemfive_windows
_init_systemfive_xnu:
#if SupportsXnu()
	pushb	systemfive_xnu-.Lanchorpoint
	mov	$syscon_xnu,%esi
	jmp	_init_systemfive_os
#endif
	.endfn	_init_systemfive_xnu
_init_systemfive_openbsd:
#if SupportsOpenbsd()
	pushb	systemfive_openbsd-.Lanchorpoint
	mov	$syscon_openbsd,%esi
	jmp	_init_systemfive_os
#endif
	.endfn	_init_systemfive_openbsd
_init_systemfive_freebsd:
#if SupportsFreebsd()
	pushb	systemfive_freebsd-.Lanchorpoint
	mov	$syscon_freebsd,%esi
	jmp	_init_systemfive_os
#endif
	.endfn	_init_systemfive_freebsd
_init_systemfive_netbsd:
#if SupportsNetbsd()
	pushb	systemfive_netbsd-.Lanchorpoint
	mov	$syscon_netbsd,%esi
//	𝑠𝑙𝑖𝑑𝑒
#endif
	.endfn	_init_systemfive_netbsd
_init_systemfive_os:
	pop	%rax
	add	$.Lanchorpoint,%eax
	stosq	#→ __systemfive
//	𝑠𝑙𝑖𝑑𝑒
	.endfn	_init_systemfive_os
_init_systemfive_magnums:
	push	%rdi
	mov	$syscon_start,%edi
2:	cmp	$syscon_end,%edi
	jnb	5f
	xor	%ebx,%ebx
	xor	%ecx,%ecx
	xor	%edx,%edx
3:	lodsb						# decodes uleb128
	movzbl	%al,%edx
	and	$127,%dl
	shl	%cl,%rdx
	or	%rdx,%rbx
	add	$7,%cl
	test	$128,%al
	jnz	3b
	xchg	%rbx,%rax
	stosq
	jmp	2b
5:	pop	%rdi
	pop	%rsi
	pop	%rbx
//	𝑠𝑙𝑖𝑑𝑒
	.endfn	_init_systemfive_magnums
#if SupportsSystemv() && !defined(TINY)
_init_systemfive_stack:					# determinism ftw!
#if SupportsWindows() || SupportsMetal()
	testb	$WINDOWS|METAL,__hostos(%rip)
	jnz	_init_systemfive_done
#endif
	push	%rdi
	push	%rsi
	mov	__NR_mmap,%eax
	movabs	$ape_stack_vaddr,%rdi
	mov	$ape_stack_memsz,%esi
	mov	$PROT_READ|PROT_WRITE,%edx
	mov	$MAP_PRIVATE|MAP_FIXED,%r10d
	or	MAP_ANONYMOUS,%r10d
	or	$-1,%r8d
	xor	%r9d,%r9d
	push	%rdi					# vaddr of stack
	push	%rsi					# size of stack
	push	%r9					# openbsd:pad
	push	%r9					# openbsd:align
#if SupportsOpenbsd()
	testb	IsOpenbsd()
	jz	0f
	syscall						# openbsd:dubstack
	jc	1f
	mov	__NR_mmap,%eax
#endif
0:	or	MAP_GROWSDOWN,%r10d			# openbsd:mapstack
	clc
	syscall
	pop	%r9
	pop	%r9
	pop	%r9					# size of stack
	pop	%r11					# vaddr of stack
	jnc	2f
1:	mov	%eax,%edi
	mov	__NR_exit_group,%eax
	syscall
2:	test	%rax,%rax
	js	1b
	.weak	_mmi
	ezlea	_mmi,cx
	test	%rcx,%rcx
	push	%r9					# save the stack size
	jz	3f
	lea	-1(%r11,%r9),%r9			# need incl. interval
	shr	$16,%r11				# for the stack range
	shr	$16,%r9
	movb	$1,(%rcx)				# _mmi.i
	mov	%r11d,8(%rcx)				# _mmi.p[0].x
	mov	%r9d,12(%rcx)				# _mmi.p[0].y
	mov	%edx,20(%rcx)				# _mmi.p[0].prot
	mov	%r10d,24(%rcx)				# _mmi.p[0].flags
3:	pop	%r9					# restore stack size
	pop	%rsi
	pop	%rdi
	leave
	pop	%rcx
	lea	(%rax,%r9),%rsp
	sub	$ape_stack_align,%rsp			# openbsd:stackbound
	mov	%rbp,(%rsp)
	push	%rcx
	push	%rbp
	mov	%rsp,%rbp
//	𝑠𝑙𝑖𝑑𝑒
_init_systemfive_syscall:
	mov	__NR_msyscall,%eax			# syscall origin protect
	cmp	$0xfff,%ax				# openbsd is pretty cool
	jae	_init_systemfive_done
	push	%rdi
	push	%rsi
	.weak	__privileged_addr
	.weak	__privileged_size
	mov	$__privileged_addr,%edi
	mov	$__privileged_size,%esi
	syscall
	pop	%rsi
	pop	%rdi
//	𝑠𝑙𝑖𝑑𝑒
#endif /* TINY */
_init_systemfive_done:
	nop
	.init.end 300,_init_systemfive,globl,hidden

//	Sections for varint encoded magic numbers.
//
//	These sections are all ordered by (group_name, constant_name).
//	They're populated by modules simply referencing the symbols.
//
//	@see libc/sysv/consts.sh
//	@see libc/sysv/consts/syscon_h
	.section .piro.bss.sort.syscon.1,"aw",@nobits
	.align	8
syscon_start:/*
	...decentralized quadwords...
	*/.previous
	.section .piro.bss.sort.syscon.3,"aw",@nobits
syscon_end:
	.previous
	.type	syscon_start,@object
	.type	syscon_end,@object
	.globl	syscon_start
	.globl	syscon_end
#if SupportsLinux() || SupportsMetal()
	.section .sort.rodata.syscon.linux.1,"a",@progbits
	.align	1
syscon_linux:/*
	...decentralized leb128...
	*/.previous
	.type	syscon_linux,@object
	.globl	syscon_linux
#endif
#if SupportsXnu()
	.section .sort.rodata.syscon.xnu.1,"a",@progbits
	.align	1
syscon_xnu:/*
	...decentralized leb128...
	*/.previous
	.type	syscon_xnu,@object
	.globl	syscon_xnu
#endif
#if SupportsFreebsd()
	.section .sort.rodata.syscon.freebsd.1,"a",@progbits
	.align	1
syscon_freebsd:/*
	...decentralized leb128...
	*/.previous
	.type	syscon_freebsd,@object
	.globl	syscon_freebsd
#endif
#if SupportsOpenbsd()
	.section .sort.rodata.syscon.openbsd.1,"a",@progbits
	.align	1
syscon_openbsd:/*
	...decentralized leb128...
	*/.previous
	.type	syscon_openbsd,@object
	.globl	syscon_openbsd
#endif
#if SupportsNetbsd()
	.section .sort.rodata.syscon.netbsd.1,"a",@progbits
	.align	1
syscon_netbsd:/*
	...decentralized leb128...
	*/.previous
	.type	syscon_netbsd,@object
	.globl	syscon_netbsd
#endif
#if SupportsWindows()
	.section .sort.rodata.syscon.windows.1,"a",@progbits
	.align	1
syscon_windows:/*
	...decentralized leb128...
	*/.previous
	.type	syscon_windows,@object
	.globl	syscon_windows
#endif

	.weak	ape_stack_vaddr
	.weak	ape_stack_memsz
	.weak	ape_stack_align
