xref: /netbsd-src/sys/arch/x86/x86/linux_trap.c (revision 76646d9e03c9615328f50b26f06c9b57e12e065b)
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