1*76646d9eSmsaitoh /* $NetBSD: linux_trap.c,v 1.12 2021/10/07 12:52:27 msaitoh Exp $ */
2631e44f4Sfvdl
3631e44f4Sfvdl /*-
4631e44f4Sfvdl * Copyright (c) 2001 The NetBSD Foundation, Inc.
5631e44f4Sfvdl * All rights reserved.
6631e44f4Sfvdl *
7631e44f4Sfvdl * This code is derived from software contributed to The NetBSD Foundation
8631e44f4Sfvdl * by Christos Zoulas.
9631e44f4Sfvdl *
10631e44f4Sfvdl * Redistribution and use in source and binary forms, with or without
11631e44f4Sfvdl * modification, are permitted provided that the following conditions
12631e44f4Sfvdl * are met:
13631e44f4Sfvdl * 1. Redistributions of source code must retain the above copyright
14631e44f4Sfvdl * notice, this list of conditions and the following disclaimer.
15631e44f4Sfvdl * 2. Redistributions in binary form must reproduce the above copyright
16631e44f4Sfvdl * notice, this list of conditions and the following disclaimer in the
17631e44f4Sfvdl * documentation and/or other materials provided with the distribution.
18631e44f4Sfvdl *
19631e44f4Sfvdl * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20631e44f4Sfvdl * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21631e44f4Sfvdl * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22631e44f4Sfvdl * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23631e44f4Sfvdl * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24631e44f4Sfvdl * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25631e44f4Sfvdl * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26631e44f4Sfvdl * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27631e44f4Sfvdl * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28631e44f4Sfvdl * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29631e44f4Sfvdl * POSSIBILITY OF SUCH DAMAGE.
30631e44f4Sfvdl */
31631e44f4Sfvdl
32631e44f4Sfvdl /*
3333fa5ccbSchs * x86 Trap and System call handling
34631e44f4Sfvdl */
35631e44f4Sfvdl
36631e44f4Sfvdl #include <sys/cdefs.h>
37*76646d9eSmsaitoh __KERNEL_RCSID(0, "$NetBSD: linux_trap.c,v 1.12 2021/10/07 12:52:27 msaitoh Exp $");
38631e44f4Sfvdl
39631e44f4Sfvdl #include <sys/param.h>
40631e44f4Sfvdl #include <sys/systm.h>
41631e44f4Sfvdl #include <sys/proc.h>
42631e44f4Sfvdl #include <sys/acct.h>
43631e44f4Sfvdl #include <sys/kernel.h>
44631e44f4Sfvdl #include <sys/signal.h>
45631e44f4Sfvdl #include <sys/signalvar.h>
46631e44f4Sfvdl #include <sys/syscall.h>
47631e44f4Sfvdl
48631e44f4Sfvdl #include <uvm/uvm_extern.h>
49631e44f4Sfvdl
50631e44f4Sfvdl #include <machine/cpu.h>
51631e44f4Sfvdl #include <machine/cpufunc.h>
52631e44f4Sfvdl #include <machine/psl.h>
53631e44f4Sfvdl #include <machine/reg.h>
54631e44f4Sfvdl #include <machine/trap.h>
55631e44f4Sfvdl #include <machine/userret.h>
56631e44f4Sfvdl
57631e44f4Sfvdl #include <compat/linux/common/linux_exec.h>
58631e44f4Sfvdl
5933fa5ccbSchs #ifndef DEBUG_LINUX
6033fa5ccbSchs #define DPRINTF(a)
6133fa5ccbSchs #else
6233fa5ccbSchs #define DPRINTF(a) uprintf a
6333fa5ccbSchs #endif
6433fa5ccbSchs
6533fa5ccbSchs struct linux_user_desc {
6633fa5ccbSchs unsigned int entry_number;
6733fa5ccbSchs unsigned int base_addr;
6833fa5ccbSchs unsigned int limit;
6933fa5ccbSchs unsigned int seg_32bit:1;
7033fa5ccbSchs unsigned int contents:2;
7133fa5ccbSchs unsigned int read_exec_only:1;
7233fa5ccbSchs unsigned int limit_in_pages:1;
7333fa5ccbSchs unsigned int seg_not_present:1;
7433fa5ccbSchs unsigned int useable:1;
7533fa5ccbSchs };
7633fa5ccbSchs
77631e44f4Sfvdl #define LINUX_T_DIVIDE 0
78631e44f4Sfvdl #define LINUX_T_DEBUG 1
79631e44f4Sfvdl #define LINUX_T_NMI 2
80631e44f4Sfvdl #define LINUX_T_INT3 3
81631e44f4Sfvdl #define LINUX_T_OVERFLOW 4
82631e44f4Sfvdl #define LINUX_T_BOUNDS 5
83631e44f4Sfvdl #define LINUX_T_INVALID_OP 6
84631e44f4Sfvdl #define LINUX_T_DEVICE_NOT_AVAIL 7
85631e44f4Sfvdl #define LINUX_T_DOUBLE_FAULT 8
86631e44f4Sfvdl #define LINUX_T_COPROC_SEG_OVERRUN 9
87631e44f4Sfvdl #define LINUX_T_INVALID_TSS 10
88631e44f4Sfvdl #define LINUX_T_SEG_NOT_PRESENT 11
89631e44f4Sfvdl #define LINUX_T_STACK_SEG_FAULT 12
90631e44f4Sfvdl #define LINUX_T_GENERAL_PROT_FAULT 13
91631e44f4Sfvdl #define LINUX_T_PAGE_FAULT 14
92631e44f4Sfvdl #define LINUX_T_SPURIOUS_INTERRUPT 15
93631e44f4Sfvdl #define LINUX_T_COPROC_ERROR 16
94631e44f4Sfvdl #define LINUX_T_ALIGN_CHECK 17
95631e44f4Sfvdl #define LINUX_T_MACHINE_CHECK 18 /* XXX */
96631e44f4Sfvdl #define LINUX_T_SIMD_COPROC_ERROR 19 /* XXX */
97631e44f4Sfvdl
98631e44f4Sfvdl /* Note 255 is bogus */
99631e44f4Sfvdl static const int trapno_to_x86_vec[] = {
100631e44f4Sfvdl LINUX_T_INVALID_OP, /* 0 T_PRIVINFLT */
101631e44f4Sfvdl LINUX_T_INT3, /* 1 T_BPTFLT */
102631e44f4Sfvdl LINUX_T_COPROC_ERROR, /* 2 T_ARITHTRAP */
103631e44f4Sfvdl LINUX_T_SPURIOUS_INTERRUPT, /* 3 T_ASTFLT XXX: ??? */
104631e44f4Sfvdl LINUX_T_GENERAL_PROT_FAULT, /* 4 T_PROTFLT */
105631e44f4Sfvdl LINUX_T_DEBUG, /* 5 T_TRCTRAP */
106631e44f4Sfvdl LINUX_T_PAGE_FAULT, /* 6 T_PAGEFLT */
107631e44f4Sfvdl LINUX_T_ALIGN_CHECK, /* 7 T_ALIGNFLT */
108631e44f4Sfvdl LINUX_T_DIVIDE, /* 8 T_DIVIDE */
109631e44f4Sfvdl LINUX_T_NMI, /* 9 T_NMI */
110631e44f4Sfvdl LINUX_T_OVERFLOW, /* 10 T_OFLOW */
111631e44f4Sfvdl LINUX_T_BOUNDS, /* 11 T_BOUND */
112631e44f4Sfvdl LINUX_T_DEVICE_NOT_AVAIL, /* 12 T_DNA */
113631e44f4Sfvdl LINUX_T_DOUBLE_FAULT, /* 13 T_DOUBLEFLT */
114631e44f4Sfvdl LINUX_T_COPROC_SEG_OVERRUN, /* 14 T_FPOPFLT */
115631e44f4Sfvdl LINUX_T_INVALID_TSS, /* 15 T_TSSFLT */
116631e44f4Sfvdl LINUX_T_SEG_NOT_PRESENT, /* 16 T_SEGNPFLT */
117631e44f4Sfvdl LINUX_T_STACK_SEG_FAULT, /* 17 T_STKFLT */
118631e44f4Sfvdl LINUX_T_MACHINE_CHECK /* 18 T_RESERVED XXX: ??? */
119631e44f4Sfvdl };
120631e44f4Sfvdl
121631e44f4Sfvdl /* For the nmi and reserved below linux does not post a signal. */
122631e44f4Sfvdl static const int linux_x86_vec_to_sig[] = {
123631e44f4Sfvdl SIGFPE, /* 0 LINUX_T_DIVIDE */
124631e44f4Sfvdl SIGTRAP, /* 1 LINUX_T_DEBUG */
125631e44f4Sfvdl /*nmi*/ SIGSEGV, /* 2 LINUX_T_NMI */
126631e44f4Sfvdl SIGTRAP, /* 3 LINUX_T_INT3 */
127631e44f4Sfvdl SIGSEGV, /* 4 LINUX_T_OVERFLOW */
128631e44f4Sfvdl SIGSEGV, /* 5 LINUX_T_BOUNDS */
129631e44f4Sfvdl SIGILL, /* 6 LINUX_T_INVALIDOP */
130631e44f4Sfvdl SIGSEGV, /* 7 LINUX_T_DEVICE_NOT_AVAIL */
131631e44f4Sfvdl SIGSEGV, /* 8 LINUX_T_DOUBLE_FAULT */
132631e44f4Sfvdl SIGFPE, /* 9 LINUX_T_COPROC_SEG_OVERRUN */
133631e44f4Sfvdl SIGSEGV, /* 10 LINUX_T_INVALID_TSS */
134631e44f4Sfvdl SIGBUS, /* 11 LINUX_T_SEG_NOT_PRESENT */
135631e44f4Sfvdl SIGBUS, /* 12 LINUX_T_STACK_SEG_FAULT */
136631e44f4Sfvdl SIGSEGV, /* 13 LINUX_T_GENERAL_PROT_FAULT */
137631e44f4Sfvdl SIGSEGV, /* 14 LINUX_T_PAGE_FAULT */
138631e44f4Sfvdl /*resv*/SIGSEGV, /* 15 LINUX_T_SPURIOUS_INTERRUPT */
139631e44f4Sfvdl SIGFPE, /* 16 LINUX_T_COPROC_ERROR */
140631e44f4Sfvdl SIGSEGV, /* 17 LINUX_T_ALIGN_CHECK */
141631e44f4Sfvdl SIGSEGV /* 18 LINUX_T_MACHINE_CHECK */
142631e44f4Sfvdl };
143631e44f4Sfvdl
144631e44f4Sfvdl void
linux_trapsignal(struct lwp * l,ksiginfo_t * ksi)145b07ec3fcSad linux_trapsignal(struct lwp *l, ksiginfo_t *ksi)
146631e44f4Sfvdl {
14733fa5ccbSchs ksiginfo_t nksi;
148631e44f4Sfvdl
149631e44f4Sfvdl switch (ksi->ksi_signo) {
150631e44f4Sfvdl case SIGILL:
151631e44f4Sfvdl case SIGTRAP:
152631e44f4Sfvdl case SIGIOT:
153631e44f4Sfvdl case SIGBUS:
154631e44f4Sfvdl case SIGFPE:
155631e44f4Sfvdl case SIGSEGV:
156631e44f4Sfvdl KASSERT(KSI_TRAP_P(ksi));
15756cdb3bbSnjoly if (ksi->ksi_trap < __arraycount(trapno_to_x86_vec)) {
15833fa5ccbSchs nksi = *ksi;
159631e44f4Sfvdl nksi.ksi_trap = trapno_to_x86_vec[ksi->ksi_trap];
16056cdb3bbSnjoly if (nksi.ksi_trap < __arraycount(linux_x86_vec_to_sig)) {
161631e44f4Sfvdl nksi.ksi_signo
162631e44f4Sfvdl = linux_x86_vec_to_sig[nksi.ksi_trap];
163631e44f4Sfvdl } else {
164631e44f4Sfvdl uprintf("Unhandled sig type %d\n",
165631e44f4Sfvdl ksi->ksi_trap);
166631e44f4Sfvdl }
167631e44f4Sfvdl ksi = &nksi;
168631e44f4Sfvdl } else {
169631e44f4Sfvdl uprintf("Unhandled trap type %d\n", ksi->ksi_trap);
170631e44f4Sfvdl }
171631e44f4Sfvdl /*FALLTHROUGH*/
17233fa5ccbSchs
173631e44f4Sfvdl default:
174631e44f4Sfvdl trapsignal(l, ksi);
175631e44f4Sfvdl return;
176631e44f4Sfvdl }
177631e44f4Sfvdl }
17833fa5ccbSchs
17933fa5ccbSchs int
linux_lwp_setprivate(struct lwp * l,void * ptr)18033fa5ccbSchs linux_lwp_setprivate(struct lwp *l, void *ptr)
18133fa5ccbSchs {
18233fa5ccbSchs struct linux_user_desc info;
18333fa5ccbSchs int error;
18433fa5ccbSchs
18533fa5ccbSchs #ifdef __x86_64__
18633fa5ccbSchs if ((l->l_proc->p_flag & PK_32) == 0) {
18733fa5ccbSchs return lwp_setprivate(l, ptr);
18833fa5ccbSchs }
18933fa5ccbSchs #endif
19033fa5ccbSchs error = copyin(ptr, &info, sizeof(info));
19133fa5ccbSchs if (error)
19233fa5ccbSchs return error;
19333fa5ccbSchs
19433fa5ccbSchs DPRINTF(("linux_lwp_setprivate: %i, %x, %x, %i, %i, %i, %i, %i, %i\n",
19533fa5ccbSchs info.entry_number, info.base_addr, info.limit, info.seg_32bit,
19633fa5ccbSchs info.contents, info.read_exec_only, info.limit_in_pages,
19733fa5ccbSchs info.seg_not_present, info.useable));
19833fa5ccbSchs
19933fa5ccbSchs if (info.entry_number != GUGS_SEL) {
20033fa5ccbSchs info.entry_number = GUGS_SEL;
20133fa5ccbSchs error = copyout(&info, ptr, sizeof(info));
20233fa5ccbSchs if (error)
20333fa5ccbSchs return error;
20433fa5ccbSchs }
20533fa5ccbSchs return lwp_setprivate(l, (void *)(uintptr_t)info.base_addr);
20633fa5ccbSchs }
207