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