// (Assuming that int 80 works,) test that syscall32/sysenter32 works
//
// i686-gcc -Os -Wall -static
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <elf.h>

long syscall_addr;
long get_syscall(char **envp)
{
	Elf32_auxv_t *auxv;
	while (*envp++ != NULL)
		continue;
	for (auxv = (void *)envp; auxv->a_type != AT_NULL; auxv++)
		if( auxv->a_type == AT_SYSINFO)
			return auxv->a_un.a_val;
	fprintf(stderr, "AT_SYSINFO not supplied, can't test\n");
	exit(0); /* this is not a failure */
}

int nfds;
fd_set rfds;
fd_set wfds;
fd_set efds;
struct timespec timeout;
sigset_t sigmask;
struct {
	sigset_t *sp;
	int sz;
} sigmask_desc;

char RES[] = "Result:0\n";

void prep_args()
{
	nfds = 42;
	FD_ZERO(&rfds);
	FD_ZERO(&wfds);
	FD_ZERO(&efds);
	FD_SET(0, &rfds);
	FD_SET(1, &wfds);
	FD_SET(2, &efds);
	timeout.tv_sec = 0;
	timeout.tv_nsec = 123;
	sigemptyset(&sigmask);
	sigaddset(&sigmask, SIGINT);
	sigaddset(&sigmask, SIGUSR2);
	sigaddset(&sigmask, SIGRTMAX);
	sigmask_desc.sp = &sigmask;
	sigmask_desc.sz = 8; /* bytes */
}

#define PSELECT 308
// pselect(nfds,rfds,wfds,efds,timeout,sigmask)
int main(int argc, char **argv, char **envp)
{
	// This only works for non-static builds:
	//syscall_addr = dlsym(dlopen("linux-gate.so.1", RTLD_NOW), "__kernel_vsyscall");
	syscall_addr = get_syscall(envp);
	prep_args();

	asm("\n"
	// Try 6-arg syscall: pselect. It should return quickly
	"	mov	$308,%eax\n"     // PSELECT
	"	mov	nfds,%ebx\n"     // ebx  arg1
	"	mov	$rfds,%ecx\n"    // ecx  arg2
	"	mov	$wfds,%edx\n"    // edx  arg3
	"	mov	$efds,%esi\n"    // esi  arg4
	"	mov	$timeout,%edi\n" // edi  arg5
	"	mov	$sigmask_desc,%ebp\n" // %ebp arg6
	"	std\n"
	"	call	*syscall_addr\n"
	// Check that registers are not clobbered
	"	pushf\n"
	"	pop	%eax\n"
	"	cmp	nfds,%ebx\n"     // ebx  arg1
	"	movb	$0x32,RES+7\n"
	"	jne	end\n"
	"	cmp	$rfds,%ecx\n"    // ecx  arg2
	"	movb	$0x33,RES+7\n"
	"	jne	end\n"
	"	cmp	$wfds,%edx\n"    // edx  arg3
	"	movb	$0x34,RES+7\n"
	"	jne	end\n"
	"	cmp	$efds,%esi\n"    // esi  arg4
	"	movb	$0x35,RES+7\n"
	"	jne	end\n"
	"	cmp	$timeout,%edi\n" // edi  arg5
	"	movb	$0x36,RES+7\n"
	"	jne	end\n"
	"	cmpl	$sigmask_desc,%ebp\n" // %ebp arg6
	"	movb	$0x37,RES+7\n"
	"	jne	end\n"
// This fails (DF is not preserved)
// on 32-bit kernels. 64-bit ones worked:
	"	bt	$10,%eax\n"      //flags.DF still set?
	"	movb	$0x31,RES+7\n"
	"	jnc	end\n"

	"	movb	$0x30,RES+7\n"

	"end:\n"
	"	mov	$4,%eax\n"	 // WRITE
	"	mov	$1,%ebx\n"       // ebx  arg1
	"	mov	$RES,%ecx\n"     // ecx  arg2
	"	mov	$9,%edx\n"       // edx  arg3
	"	int	$0x80\n"

	// Error if result is not 0 or 1
	// (result:1 means DF was clobbered)
	"	mov	$1,%eax\n"	 // EXIT
	"	xor	%ebx,%ebx\n"     // ebx  arg1
	"	cmpb	$0x31,RES+7\n"
	"	ja	1f\n"
	"	int	$0x80\n"
	"1:	inc	%ebx\n"
	"	int	$0x80\n"
	);
	return 42; /* not reached */
}