1/* $NetBSD: linux_trap.c,v 1.11 2012/02/19 21:06:35 rmind Exp $ */
2
3/*-
4 * Copyright (c) 2001 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Christos Zoulas.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * x86 Trap and System call handling
34 */
35
36#include <sys/cdefs.h>
37__KERNEL_RCSID(0, "$NetBSD: linux_trap.c,v 1.11 2012/02/19 21:06:35 rmind Exp $");
38
39#include <sys/param.h>
40#include <sys/systm.h>
41#include <sys/proc.h>
42#include <sys/acct.h>
43#include <sys/kernel.h>
44#include <sys/signal.h>
45#include <sys/signalvar.h>
46#include <sys/syscall.h>
47
48#include <uvm/uvm_extern.h>
49
50#include <machine/cpu.h>
51#include <machine/cpufunc.h>
52#include <machine/psl.h>
53#include <machine/reg.h>
54#include <machine/trap.h>
55#include <machine/userret.h>
56
57#include <compat/linux/common/linux_exec.h>
58
59#ifndef DEBUG_LINUX
60#define DPRINTF(a)
61#else
62#define DPRINTF(a) uprintf a
63#endif
64
65struct linux_user_desc {
66 unsigned int entry_number;
67 unsigned int base_addr;
68 unsigned int limit;
69 unsigned int seg_32bit:1;
70 unsigned int contents:2;
71 unsigned int read_exec_only:1;
72 unsigned int limit_in_pages:1;
73 unsigned int seg_not_present:1;
74 unsigned int useable:1;
75};
76
77#define LINUX_T_DIVIDE 0
78#define LINUX_T_DEBUG 1
79#define LINUX_T_NMI 2
80#define LINUX_T_INT3 3
81#define LINUX_T_OVERFLOW 4
82#define LINUX_T_BOUNDS 5
83#define LINUX_T_INVALID_OP 6
84#define LINUX_T_DEVICE_NOT_AVAIL 7
85#define LINUX_T_DOUBLE_FAULT 8
86#define LINUX_T_COPROC_SEG_OVERRUN 9
87#define LINUX_T_INVALID_TSS 10
88#define LINUX_T_SEG_NOT_PRESENT 11
89#define LINUX_T_STACK_SEG_FAULT 12
90#define LINUX_T_GENERAL_PROT_FAULT 13
91#define LINUX_T_PAGE_FAULT 14
92#define LINUX_T_SPURIOUS_INTERRUPT 15
93#define LINUX_T_COPROC_ERROR 16
94#define LINUX_T_ALIGN_CHECK 17
95#define LINUX_T_MACHINE_CHECK 18 /* XXX */
96#define LINUX_T_SIMD_COPROC_ERROR 19 /* XXX */
97
98/* Note 255 is bogus */
99static const int trapno_to_x86_vec[] = {
100 LINUX_T_INVALID_OP, /* 0 T_PRIVINFLT */
101 LINUX_T_INT3, /* 1 T_BPTFLT */
102 LINUX_T_COPROC_ERROR, /* 2 T_ARITHTRAP */
103 LINUX_T_SPURIOUS_INTERRUPT, /* 3 T_ASTFLT XXX: ??? */
104 LINUX_T_GENERAL_PROT_FAULT, /* 4 T_PROTFLT */
105 LINUX_T_DEBUG, /* 5 T_TRCTRAP */
106 LINUX_T_PAGE_FAULT, /* 6 T_PAGEFLT */
107 LINUX_T_ALIGN_CHECK, /* 7 T_ALIGNFLT */
108 LINUX_T_DIVIDE, /* 8 T_DIVIDE */
109 LINUX_T_NMI, /* 9 T_NMI */
110 LINUX_T_OVERFLOW, /* 10 T_OFLOW */
111 LINUX_T_BOUNDS, /* 11 T_BOUND */
112 LINUX_T_DEVICE_NOT_AVAIL, /* 12 T_DNA */
113 LINUX_T_DOUBLE_FAULT, /* 13 T_DOUBLEFLT */
114 LINUX_T_COPROC_SEG_OVERRUN, /* 14 T_FPOPFLT */
115 LINUX_T_INVALID_TSS, /* 15 T_TSSFLT */
116 LINUX_T_SEG_NOT_PRESENT, /* 16 T_SEGNPFLT */
117 LINUX_T_STACK_SEG_FAULT, /* 17 T_STKFLT */
118 LINUX_T_MACHINE_CHECK /* 18 T_RESERVED XXX: ??? */
119};
120
121/* For the nmi and reserved below linux does not post a signal. */
122static const int linux_x86_vec_to_sig[] = {
123 SIGFPE, /* 0 LINUX_T_DIVIDE */
124 SIGTRAP, /* 1 LINUX_T_DEBUG */
125/*nmi*/ SIGSEGV, /* 2 LINUX_T_NMI */
126 SIGTRAP, /* 3 LINUX_T_INT3 */
127 SIGSEGV, /* 4 LINUX_T_OVERFLOW */
128 SIGSEGV, /* 5 LINUX_T_BOUNDS */
129 SIGILL, /* 6 LINUX_T_INVALIDOP */
130 SIGSEGV, /* 7 LINUX_T_DEVICE_NOT_AVAIL */
131 SIGSEGV, /* 8 LINUX_T_DOUBLE_FAULT */
132 SIGFPE, /* 9 LINUX_T_COPROC_SEG_OVERRUN */
133 SIGSEGV, /* 10 LINUX_T_INVALID_TSS */
134 SIGBUS, /* 11 LINUX_T_SEG_NOT_PRESENT */
135 SIGBUS, /* 12 LINUX_T_STACK_SEG_FAULT */
136 SIGSEGV, /* 13 LINUX_T_GENERAL_PROT_FAULT */
137 SIGSEGV, /* 14 LINUX_T_PAGE_FAULT */
138/*resv*/SIGSEGV, /* 15 LINUX_T_SPURIOUS_INTERRUPT */
139 SIGFPE, /* 16 LINUX_T_COPROC_ERROR */
140 SIGSEGV, /* 17 LINUX_T_ALIGN_CHECK */
141 SIGSEGV /* 18 LINUX_T_MACHINE_CHECK */
142};
143
144void
145linux_trapsignal(struct lwp *l, ksiginfo_t *ksi)
146{
147 ksiginfo_t nksi;
148
149 switch (ksi->ksi_signo) {
150 case SIGILL:
151 case SIGTRAP:
152 case SIGIOT:
153 case SIGBUS:
154 case SIGFPE:
155 case SIGSEGV:
156 KASSERT(KSI_TRAP_P(ksi));
157 if (ksi->ksi_trap < __arraycount(trapno_to_x86_vec)) {
158 nksi = *ksi;
159 nksi.ksi_trap = trapno_to_x86_vec[ksi->ksi_trap];
160 if (nksi.ksi_trap < __arraycount(linux_x86_vec_to_sig)) {
161 nksi.ksi_signo
162 = linux_x86_vec_to_sig[nksi.ksi_trap];
163 } else {
164 uprintf("Unhandled sig type %d\n",
165 ksi->ksi_trap);
166 }
167 ksi = &nksi;
168 } else {
169 uprintf("Unhandled trap type %d\n", ksi->ksi_trap);
170 }
171 /*FALLTHROUGH*/
172
173 default:
174 trapsignal(l, ksi);
175 return;
176 }
177}
178
179int
180linux_lwp_setprivate(struct lwp *l, void *ptr)
181{
182 struct linux_user_desc info;
183 int error;
184
185#ifdef __x86_64__
186 if ((l->l_proc->p_flag & PK_32) == 0) {
187 return lwp_setprivate(l, ptr);
188 }
189#endif
190 error = copyin(ptr, &info, sizeof(info));
191 if (error)
192 return error;
193
194 DPRINTF(("linux_lwp_setprivate: %i, %x, %x, %i, %i, %i, %i, %i, %i\n",
195 info.entry_number, info.base_addr, info.limit, info.seg_32bit,
196 info.contents, info.read_exec_only, info.limit_in_pages,
197 info.seg_not_present, info.useable));
198
199 if (info.entry_number != GUGS_SEL) {
200 info.entry_number = GUGS_SEL;
201 error = copyout(&info, ptr, sizeof(info));
202 if (error)
203 return error;
204 }
205 return lwp_setprivate(l, (void *)(uintptr_t)info.base_addr);
206}
207