]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/ia32/32/thread-ia32-32.cpp
1e45f9c684512c76b77379a4c8707961655e7a74
[l4.git] / kernel / fiasco / src / kern / ia32 / 32 / thread-ia32-32.cpp
1 IMPLEMENTATION[ia32 || ux]:
2
3 PUBLIC template<typename T> inline
4 void FIASCO_NORETURN
5 Thread::fast_return_to_user(Mword ip, Mword sp, T arg)
6 {
7   assert_kdb(cpu_lock.test());
8   assert_kdb(current() == this);
9   assert_kdb(Config::Is_ux || (regs()->cs() & 3 == 3));
10
11   regs()->ip(ip);
12   regs()->sp(sp);
13   regs()->flags(EFLAGS_IF);
14   asm volatile
15     ("mov %0, %%esp \t\n"
16      "iret         \t\n"
17      :
18      : "r" (static_cast<Return_frame*>(regs())), "a" (arg)
19     );
20   __builtin_trap();
21 }
22
23 IMPLEMENT inline
24 Mword
25 Thread::user_sp() const
26 { return regs()->sp(); }
27
28 IMPLEMENT inline
29 void
30 Thread::user_sp(Mword sp)
31 { regs()->sp(sp); }
32
33 PROTECTED inline
34 int
35 Thread::do_trigger_exception(Entry_frame *r, void *ret_handler)
36 {
37   if (!exception_triggered())
38     {
39       _exc_cont.activate(r, ret_handler);
40       return 1;
41     }
42   // else ignore change of IP because triggered exception already pending
43   return 0;
44 }
45
46
47 PUBLIC inline
48 void
49 Thread::restore_exc_state()
50 {
51   assert (cpu_lock.test());
52   _exc_cont.restore(regs());
53 #if 0
54
55 #ifdef CONFIG_PF_UX
56   r->cs (exception_cs() & ~1);
57 #else
58   r->cs (exception_cs());
59 #endif
60   r->ip (_exc_ip);
61   r->flags (_exc_flags);
62   _exc_ip = ~0UL;
63 #endif
64 }
65
66 PRIVATE static inline
67 Return_frame *
68 Thread::trap_state_to_rf(Trap_state *ts)
69 {
70   char *im = reinterpret_cast<char*>(ts + 1);
71   return reinterpret_cast<Return_frame*>(im)-1;
72 }
73
74 PRIVATE static inline NEEDS[Thread::trap_state_to_rf]
75 bool FIASCO_WARN_RESULT
76 Thread::copy_utcb_to_ts(L4_msg_tag const &tag, Thread *snd, Thread *rcv,
77                         unsigned char rights)
78 {
79   Trap_state *ts = (Trap_state*)rcv->_utcb_handler;
80   Mword       s  = tag.words();
81   Unsigned32  cs = ts->cs();
82   Utcb *snd_utcb = snd->utcb().access();
83
84   // XXX: check that gs and fs point to valid user_entry only, for gdt and
85   // ldt!
86   if (EXPECT_FALSE(rcv->exception_triggered()))
87     {
88       // triggered exception pending
89       Mem::memcpy_mwords(&ts->_gs, snd_utcb->values, s > 12 ? 12 : s);
90       if (EXPECT_TRUE(s > 15))
91         {
92           Continuation::User_return_frame const *s
93             = reinterpret_cast<Continuation::User_return_frame const *>((char*)&snd_utcb->values[12]);
94
95           rcv->_exc_cont.set(trap_state_to_rf(ts), s);
96         }
97     }
98   else
99     Mem::memcpy_mwords (&ts->_gs, snd_utcb->values, s > 16 ? 16 : s);
100
101   // reset segments
102   rcv->_gs = rcv->_fs = 0;
103
104   if (tag.transfer_fpu() && (rights & L4_fpage::W))
105     snd->transfer_fpu(rcv);
106
107   // sanitize eflags
108   ts->flags((ts->flags() & ~(EFLAGS_IOPL | EFLAGS_NT)) | EFLAGS_IF);
109
110   // don't allow to overwrite the code selector!
111   ts->cs(cs);
112
113   bool ret = transfer_msg_items(tag, snd, snd_utcb,
114                                 rcv, rcv->utcb().access(), rights);
115
116   rcv->state_del(Thread_in_exception);
117   return ret;
118 }
119
120 PRIVATE static inline
121 bool FIASCO_WARN_RESULT
122 Thread::copy_ts_to_utcb(L4_msg_tag const &, Thread *snd, Thread *rcv,
123                         unsigned char rights)
124 {
125   Utcb *rcv_utcb = rcv->utcb().access();
126   Trap_state *ts = (Trap_state*)snd->_utcb_handler;
127   Mword        r = Utcb::Max_words;
128
129   {
130     auto guard = lock_guard(cpu_lock);
131     if (EXPECT_FALSE(snd->exception_triggered()))
132       {
133         Mem::memcpy_mwords (rcv_utcb->values, &ts->_gs, r > 12 ? 12 : r);
134         Continuation::User_return_frame *d
135             = reinterpret_cast<Continuation::User_return_frame *>((char*)&rcv_utcb->values[12]);
136
137         snd->_exc_cont.get(d, trap_state_to_rf(ts));
138       }
139     else
140       Mem::memcpy_mwords (rcv_utcb->values, &ts->_gs, r > 16 ? 16 : r);
141
142     if (rcv_utcb->inherit_fpu() && (rights & L4_fpage::W))
143         snd->transfer_fpu(rcv);
144   }
145   return true;
146 }
147
148
149 //----------------------------------------------------------------------------
150 IMPLEMENTATION [ia32 && !ux]:
151
152 IMPLEMENT inline NEEDS[Thread::exception_triggered]
153 void
154 Thread::user_ip(Mword ip)
155 {
156   if (exception_triggered())
157     _exc_cont.ip(ip);
158   else
159     regs()->ip(ip);
160 }
161
162
163 PRIVATE inline
164 int
165 Thread::check_trap13_kernel(Trap_state *ts)
166 {
167   if (EXPECT_FALSE(ts->_trapno == 13 && (ts->_err & 3) == 0))
168     {
169       // First check if user loaded a segment register with 0 because the
170       // resulting exception #13 can be raised from user _and_ kernel. If
171       // the user tried to load another segment selector, the thread gets
172       // killed.
173       // XXX Should we emulate this too? Michael Hohmuth: Yes, we should.
174       if (EXPECT_FALSE(!(ts->_ds & 0xffff)))
175         {
176           Cpu::set_ds(Gdt::data_segment());
177           return 0;
178         }
179       if (EXPECT_FALSE(!(ts->_es & 0xffff)))
180         {
181           Cpu::set_es(Gdt::data_segment());
182           return 0;
183         }
184       if (EXPECT_FALSE(ts->_ds & 0xfff8) == Gdt::gdt_code_user)
185         {
186           WARN("%p eip=%08lx: code selector ds=%04lx",
187                this, ts->ip(), ts->_ds & 0xffff);
188           Cpu::set_ds(Gdt::data_segment());
189           return 0;
190         }
191       if (EXPECT_FALSE(ts->_es & 0xfff8) == Gdt::gdt_code_user)
192         {
193           WARN("%p eip=%08lx: code selector es=%04lx",
194                this, ts->ip(), ts->_es & 0xffff);
195           Cpu::set_es(Gdt::data_segment());
196           return 0;
197         }
198       if (EXPECT_FALSE(ts->_fs & 0xfff8) == Gdt::gdt_code_user)
199         {
200           WARN("%p eip=%08lx: code selector fs=%04lx",
201                this, ts->ip(), ts->_fs & 0xffff);
202           ts->_fs = 0;
203           return 0;
204         }
205       if (EXPECT_FALSE(ts->_gs & 0xfff8) == Gdt::gdt_code_user)
206         {
207           WARN("%p eip=%08lx: code selector gs=%04lx",
208                this, ts->ip(), ts->_gs & 0xffff);
209           ts->_gs = 0;
210           return 0;
211         }
212     }
213
214   return 1;
215 }
216
217
218 IMPLEMENT
219 void
220 Thread::user_invoke()
221 {
222   user_invoke_generic();
223
224   asm volatile
225     ("  movl %%eax,%%esp \n"    // set stack pointer to regs structure
226      "  movl %%ecx,%%es  \n"
227      "  movl %%ecx,%%ds  \n"
228      "  xorl %%eax,%%eax \n"    // clean out user regs
229      "  xorl %%ecx,%%ecx \n"
230      "  xorl %%edx,%%edx \n"
231      "  xorl %%esi,%%esi \n"
232      "  xorl %%edi,%%edi \n"
233      "  xorl %%ebx,%%ebx \n"
234      "  xorl %%ebp,%%ebp \n"
235      "  iret             \n"
236      :                          // no output
237      : "a" (nonull_static_cast<Return_frame*>(current()->regs())),
238        "c" (Gdt::gdt_data_user | Gdt::Selector_user)
239      );
240
241   // never returns here
242 }
243
244 //---------------------------------------------------------------------------
245 IMPLEMENTATION [ia32]:
246
247 #include <feature.h>
248 KIP_KERNEL_FEATURE("segments");
249
250 PROTECTED inline
251 L4_msg_tag
252 Thread::invoke_arch(L4_msg_tag tag, Utcb *utcb)
253 {
254   switch (utcb->values[0] & Opcode_mask)
255     {
256       case Op_gdt_x86:
257
258       // if no words given then return the first gdt entry
259       if (EXPECT_FALSE(tag.words() == 1))
260         {
261           utcb->values[0] = Gdt::gdt_user_entry1 >> 3;
262           return Kobject_iface::commit_result(0, 1);
263         }
264
265         {
266           unsigned entry_number = utcb->values[1];
267           unsigned idx = 2;
268
269           for (; entry_number < Gdt_user_entries
270                  && idx < tag.words()
271                ; idx += 2, ++entry_number)
272             {
273               Gdt_entry *d = (Gdt_entry *)&utcb->values[idx];
274               if (!d->unsafe())
275                 _gdt_user_entries[entry_number] = *d;
276             }
277
278           if (this == current_thread())
279             switch_gdt_user_entries(this);
280
281           return Kobject_iface::commit_result((utcb->values[1] << 3) + Gdt::gdt_user_entry1 + 3);
282         }
283
284     default:
285       return commit_result(-L4_err::ENosys);
286     };
287 }
288
289 //---------------------------------------------------------------------------
290 IMPLEMENTATION [ia32 & (debug | kdb)]:
291
292 #include "kernel_task.h"
293
294 /** Call the nested trap handler (either Jdb::enter_kdebugger() or the
295  * gdb stub. Setup our own stack frame */
296 PRIVATE static
297 int
298 Thread::call_nested_trap_handler(Trap_state *ts)
299 {
300   unsigned log_cpu = dbg_find_cpu();
301   unsigned long &ntr = nested_trap_recover.cpu(log_cpu);
302
303 #if 0
304   printf("%s: lcpu%u sp=%p t=%lu nested_trap_recover=%ld\n",
305          __func__, log_cpu, (void*)Proc::stack_pointer(), ts->_trapno, ntr);
306 #endif
307
308   int ret;
309
310   unsigned dummy1, dummy2, dummy3;
311
312   struct
313   {
314     Mword pdir;
315     FIASCO_FASTCALL int (*handler)(Trap_state*, unsigned);
316     void *stack;
317   } p;
318
319   if (!ntr)
320     p.stack = dbg_stack.cpu(log_cpu).stack_top;
321   else
322     p.stack = 0;
323
324   p.pdir = Kernel_task::kernel_task()->virt_to_phys((Address)Kmem::dir());
325   p.handler = nested_trap_handler;
326
327
328   // don't set %esp if gdb fault recovery to ensure that exceptions inside
329   // kdb/jdb don't overwrite the stack
330   asm volatile
331     ("mov    %%esp,%[d2]        \n\t"
332      "cmpl   $0,(%[ntr])        \n\t"
333      "jne    1f                 \n\t"
334      "mov    8(%[p]),%%esp      \n\t"
335      "1:                        \n\t"
336      "incl   (%[ntr])           \n\t"
337      "mov    %%cr3, %[d1]       \n\t"
338      "push   %[d2]              \n\t"
339      "push   %[p]               \n\t"
340      "push   %[d1]              \n\t"
341      "mov    (%[p]), %[d1]      \n\t"
342      "mov    %[d1], %%cr3       \n\t"
343      "call   *4(%[p])           \n\t"
344      "pop    %[d1]              \n\t"
345      "mov    %[d1], %%cr3       \n\t"
346      "pop    %[p]               \n\t"
347      "pop    %%esp              \n\t"
348      "cmpl   $0,(%[ntr])        \n\t"
349      "je     1f                 \n\t"
350      "decl   (%[ntr])           \n\t"
351      "1:                        \n\t"
352
353      : [ret] "=a"  (ret),
354        [d1]  "=&c" (dummy1),
355        [d2]  "=&r" (dummy2),
356              "=d"  (dummy3)
357      : [ts]      "a" (ts),
358        [cpu]     "d" (log_cpu),
359        [p]       "S" (&p),
360        [ntr]     "D" (&ntr)
361      : "memory");
362
363   return ret == 0 ? 0 : -1;
364 }