1 /*-
2  * Copyright (c) 2015 Nuxi, https://nuxi.nl/
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include <sys/cdefs.h>
27 __FBSDID("$FreeBSD$");
28 
29 #include <sys/param.h>
30 #include <sys/imgact.h>
31 #include <sys/kernel.h>
32 #include <sys/proc.h>
33 #include <sys/sysent.h>
34 
35 #include <vm/vm.h>
36 #include <vm/pmap.h>
37 
38 #include <machine/frame.h>
39 #include <machine/pcb.h>
40 #include <machine/vmparam.h>
41 
42 #include <compat/cloudabi/cloudabi_util.h>
43 
44 #include <compat/cloudabi64/cloudabi64_syscall.h>
45 #include <compat/cloudabi64/cloudabi64_util.h>
46 
47 extern const char *cloudabi64_syscallnames[];
48 extern struct sysent cloudabi64_sysent[];
49 
50 static int
51 cloudabi64_fixup_tcb(register_t **stack_base, struct image_params *imgp)
52 {
53 	int error;
54 	register_t tcbptr;
55 
56 	/* Place auxiliary vector and TCB on the stack. */
57 	error = cloudabi64_fixup(stack_base, imgp);
58 	if (error != 0)
59 		return (error);
60 
61 	/*
62 	 * On x86-64, the TCB is referred to by %fs:0. Take some space
63 	 * from the top of the stack to store a single element array,
64 	 * containing a pointer to the TCB. %fs base will point to this.
65 	 */
66 	tcbptr = (register_t)*stack_base;
67 	return (copyout(&tcbptr, --*stack_base, sizeof(tcbptr)));
68 }
69 
70 static void
71 cloudabi64_proc_setregs(struct thread *td, struct image_params *imgp,
72     unsigned long stack)
73 {
74 	struct trapframe *regs;
75 
76 	exec_setregs(td, imgp, stack);
77 
78 	/*
79 	 * The stack now contains a pointer to the TCB, the TCB itself,
80 	 * and the auxiliary vector. Let %rdx point to the auxiliary
81 	 * vector, and set %fs base to the address of the TCB.
82 	 */
83 	regs = td->td_frame;
84 	regs->tf_rdi = stack + sizeof(register_t) +
85 	    roundup(sizeof(cloudabi64_tcb_t), sizeof(register_t));
86 	(void)cpu_set_user_tls(td, (void *)stack);
87 }
88 
89 static int
90 cloudabi64_fetch_syscall_args(struct thread *td, struct syscall_args *sa)
91 {
92 	struct trapframe *frame = td->td_frame;
93 
94 	/* Obtain system call number. */
95 	sa->code = frame->tf_rax;
96 	if (sa->code >= CLOUDABI64_SYS_MAXSYSCALL)
97 		return (ENOSYS);
98 	sa->callp = &cloudabi64_sysent[sa->code];
99 	sa->narg = sa->callp->sy_narg;
100 
101 	/* Fetch system call arguments. */
102 	sa->args[0] = frame->tf_rdi;
103 	sa->args[1] = frame->tf_rsi;
104 	sa->args[2] = frame->tf_rdx;
105 	sa->args[3] = frame->tf_rcx; /* Actually %r10. */
106 	sa->args[4] = frame->tf_r8;
107 	sa->args[5] = frame->tf_r9;
108 
109 	/* Default system call return values. */
110 	td->td_retval[0] = 0;
111 	td->td_retval[1] = frame->tf_rdx;
112 	return (0);
113 }
114 
115 static void
116 cloudabi64_set_syscall_retval(struct thread *td, int error)
117 {
118 	struct trapframe *frame = td->td_frame;
119 
120 	switch (error) {
121 	case 0:
122 		/* System call succeeded. */
123 		frame->tf_rax = td->td_retval[0];
124 		frame->tf_rdx = td->td_retval[1];
125 		frame->tf_rflags &= ~PSL_C;
126 		break;
127 	case ERESTART:
128 		/* Restart system call. */
129 		frame->tf_rip -= frame->tf_err;
130 		frame->tf_r10 = frame->tf_rcx;
131 		set_pcb_flags(td->td_pcb, PCB_FULL_IRET);
132 		break;
133 	case EJUSTRETURN:
134 		break;
135 	default:
136 		/* System call returned an error. */
137 		frame->tf_rax = cloudabi_convert_errno(error);
138 		frame->tf_rflags |= PSL_C;
139 		break;
140 	}
141 }
142 
143 static void
144 cloudabi64_schedtail(struct thread *td)
145 {
146 	struct trapframe *frame = td->td_frame;
147 
148 	/* Initial register values for processes returning from fork. */
149 	frame->tf_rax = CLOUDABI_PROCESS_CHILD;
150 	frame->tf_rdx = td->td_tid;
151 }
152 
153 int
154 cloudabi64_thread_setregs(struct thread *td,
155     const cloudabi64_threadattr_t *attr, uint64_t tcb)
156 {
157 	struct trapframe *frame;
158 	stack_t stack;
159 	uint64_t tcbptr;
160 	int error;
161 
162 	/*
163 	 * On x86-64, the TCB is referred to by %fs:0. Take some space
164 	 * from the top of the stack to store a single element array,
165 	 * containing a pointer to the TCB. %fs base will point to this.
166 	 */
167 	tcbptr = rounddown(attr->stack + attr->stack_size - sizeof(tcbptr),
168 	    _Alignof(tcbptr));
169 	error = copyout(&tcb, (void *)tcbptr, sizeof(tcb));
170 	if (error != 0)
171 		return (error);
172 
173 	/* Perform standard register initialization. */
174 	stack.ss_sp = TO_PTR(attr->stack);
175 	stack.ss_size = tcbptr - attr->stack;
176 	cpu_set_upcall(td, TO_PTR(attr->entry_point), NULL, &stack);
177 
178 	/*
179 	 * Pass in the thread ID of the new thread and the argument
180 	 * pointer provided by the parent thread in as arguments to the
181 	 * entry point.
182 	 */
183 	frame = td->td_frame;
184 	frame->tf_rdi = td->td_tid;
185 	frame->tf_rsi = attr->argument;
186 
187 	return (cpu_set_user_tls(td, (void *)tcbptr));
188 }
189 
190 static struct sysentvec cloudabi64_elf_sysvec = {
191 	.sv_size		= CLOUDABI64_SYS_MAXSYSCALL,
192 	.sv_table		= cloudabi64_sysent,
193 	.sv_fixup		= cloudabi64_fixup_tcb,
194 	.sv_name		= "CloudABI ELF64",
195 	.sv_coredump		= elf64_coredump,
196 	.sv_pagesize		= PAGE_SIZE,
197 	.sv_minuser		= VM_MIN_ADDRESS,
198 	.sv_maxuser		= VM_MAXUSER_ADDRESS,
199 	.sv_stackprot		= VM_PROT_READ | VM_PROT_WRITE,
200 	.sv_copyout_strings	= cloudabi64_copyout_strings,
201 	.sv_setregs		= cloudabi64_proc_setregs,
202 	.sv_flags		= SV_ABI_CLOUDABI | SV_CAPSICUM | SV_LP64,
203 	.sv_set_syscall_retval	= cloudabi64_set_syscall_retval,
204 	.sv_fetch_syscall_args	= cloudabi64_fetch_syscall_args,
205 	.sv_syscallnames	= cloudabi64_syscallnames,
206 	.sv_schedtail		= cloudabi64_schedtail,
207 };
208 
209 INIT_SYSENTVEC(elf_sysvec, &cloudabi64_elf_sysvec);
210 
211 Elf64_Brandinfo cloudabi64_brand = {
212 	.brand		= ELFOSABI_CLOUDABI,
213 	.machine	= EM_X86_64,
214 	.sysvec		= &cloudabi64_elf_sysvec,
215 	.flags		= BI_CAN_EXEC_DYN,
216 	.compat_3_brand	= "CloudABI",
217 };
218