]> 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 PROTECTED inline
4 void FIASCO_NORETURN
5 Thread::fast_return_to_user(Mword ip, Mword sp)
6 {
7   assert_kdb(cpu_lock.test());
8   assert_kdb(current() == this);
9
10   regs()->ip(ip);
11   regs()->sp(sp);
12   asm volatile
13     ("mov %0, %%esp \t\n"
14      "iret         \t\n"
15      :
16      : "r" (static_cast<Return_frame*>(regs()))
17     );
18   __builtin_trap();
19 }
20
21 IMPLEMENT inline
22 Mword
23 Thread::user_sp() const
24 { return regs()->sp(); }
25
26 IMPLEMENT inline
27 void
28 Thread::user_sp(Mword sp)
29 { regs()->sp(sp); }
30
31 PROTECTED inline
32 int
33 Thread::do_trigger_exception(Entry_frame *r, void *ret_handler)
34 {
35   if (!exception_triggered())
36     {
37       _exc_cont.activate(r, ret_handler);
38       return 1;
39     }
40   // else ignore change of IP because triggered exception already pending
41   return 0;
42 }
43
44
45 PUBLIC inline
46 void
47 Thread::restore_exc_state()
48 {
49   assert (cpu_lock.test());
50   _exc_cont.restore(regs());
51 #if 0
52
53 #ifdef CONFIG_PF_UX
54   r->cs (exception_cs() & ~1);
55 #else
56   r->cs (exception_cs());
57 #endif
58   r->ip (_exc_ip);
59   r->flags (_exc_flags);
60   _exc_ip = ~0UL;
61 #endif
62 }
63
64 PRIVATE static inline
65 Return_frame *
66 Thread::trap_state_to_rf(Trap_state *ts)
67 {
68   char *im = reinterpret_cast<char*>(ts + 1);
69   return reinterpret_cast<Return_frame*>(im)-1;
70 }
71
72 PRIVATE static inline NEEDS[Thread::trap_is_privileged,
73                             Thread::trap_state_to_rf]
74 bool FIASCO_WARN_RESULT
75 Thread::copy_utcb_to_ts(L4_msg_tag const &tag, Thread *snd, Thread *rcv,
76                         unsigned char rights)
77 {
78   Trap_state *ts = (Trap_state*)rcv->_utcb_handler;
79   Mword       s  = tag.words();
80   Unsigned32  cs = ts->cs();
81   Utcb *snd_utcb = snd->access_utcb();
82
83   // XXX: check that gs and fs point to valid user_entry only, for gdt and
84   // ldt!
85   if (EXPECT_FALSE(rcv->exception_triggered()))
86     {
87       // triggered exception pending
88       Mem::memcpy_mwords(&ts->_gs, snd_utcb->values, s > 12 ? 12 : s);
89       if (EXPECT_TRUE(s > 15))
90         {
91           Continuation::User_return_frame const *s
92             = reinterpret_cast<Continuation::User_return_frame const *>((char*)&snd_utcb->values[12]);
93
94           rcv->_exc_cont.set(trap_state_to_rf(ts), s);
95         }
96     }
97   else
98     Mem::memcpy_mwords (&ts->_gs, snd_utcb->values, s > 16 ? 16 : s);
99
100   // reset segments
101   rcv->_gs = rcv->_fs = 0;
102
103   if (tag.transfer_fpu() && (rights & L4_fpage::W))
104     snd->transfer_fpu(rcv);
105
106   // sanitize eflags
107   if (!rcv->trap_is_privileged(0))
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->access_utcb(), 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();
126   Trap_state *ts = (Trap_state*)snd->_utcb_handler;
127   Mword        r = Utcb::Max_words;
128
129   {
130     Lock_guard <Cpu_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->_fs & 0xffff)))
185         {
186           ts->_fs = Utcb_init::utcb_segment();
187           return 0;
188         }
189       if (EXPECT_FALSE(!(ts->_gs & 0xffff)))
190         {
191           ts->_gs = Utcb_init::utcb_segment();
192           return 0;
193         }
194       if (EXPECT_FALSE(ts->_ds & 0xfff8) == Gdt::gdt_code_user)
195         {
196           WARN("%p eip=%08lx: code selector ds=%04lx",
197                this, ts->ip(), ts->_ds & 0xffff);
198           Cpu::set_ds(Gdt::data_segment());
199           return 0;
200         }
201       if (EXPECT_FALSE(ts->_es & 0xfff8) == Gdt::gdt_code_user)
202         {
203           WARN("%p eip=%08lx: code selector es=%04lx",
204                this, ts->ip(), ts->_es & 0xffff);
205           Cpu::set_es(Gdt::data_segment());
206           return 0;
207         }
208       if (EXPECT_FALSE(ts->_fs & 0xfff8) == Gdt::gdt_code_user)
209         {
210           WARN("%p eip=%08lx: code selector fs=%04lx",
211                this, ts->ip(), ts->_fs & 0xffff);
212           ts->_fs = Utcb_init::utcb_segment();
213           return 0;
214         }
215       if (EXPECT_FALSE(ts->_gs & 0xfff8) == Gdt::gdt_code_user)
216         {
217           WARN("%p eip=%08lx: code selector gs=%04lx",
218                this, ts->ip(), ts->_gs & 0xffff);
219           ts->_gs = Utcb_init::utcb_segment();
220           return 0;
221         }
222     }
223
224   return 1;
225 }
226
227
228 IMPLEMENT
229 void
230 Thread::user_invoke()
231 {
232   user_invoke_generic();
233
234   asm volatile
235     ("  movl %%eax,%%esp \n"    // set stack pointer to regs structure
236      "  movl %%ecx,%%es  \n"
237      "  movl %%ecx,%%ds  \n"
238      "  xorl %%eax,%%eax \n"    // clean out user regs
239      "  xorl %%ecx,%%ecx \n"
240      "  xorl %%edx,%%edx \n"
241      "  xorl %%esi,%%esi \n"
242      "  xorl %%edi,%%edi \n"
243      "  xorl %%ebx,%%ebx \n"
244      "  xorl %%ebp,%%ebp \n"
245      "  iret             \n"
246      :                          // no output
247      : "a" (nonull_static_cast<Return_frame*>(current()->regs())),
248        "c" (Gdt::gdt_data_user | Gdt::Selector_user)
249      );
250
251   // never returns here
252 }
253
254 //---------------------------------------------------------------------------
255 IMPLEMENTATION [ia32]:
256
257 #include <feature.h>
258 KIP_KERNEL_FEATURE("segments");
259
260 PROTECTED inline
261 bool
262 Thread::invoke_arch(L4_msg_tag &tag, Utcb *utcb)
263 {
264   switch (utcb->values[0] & Opcode_mask)
265     {
266       case Op_gdt_x86:
267
268       // if no words given then return the first gdt entry
269       if (EXPECT_FALSE(tag.words() == 1))
270         {
271           utcb->values[0] = Gdt::gdt_user_entry1 >> 3;
272           tag = commit_result(0, 1);
273           return true;
274         }
275
276         {
277           unsigned entry_number = utcb->values[1];
278           unsigned idx = 2;
279
280           for (; entry_number < Gdt_user_entries
281                  && idx < tag.words()
282                ; idx += 2, ++entry_number)
283             {
284               Gdt_entry *d = (Gdt_entry *)&utcb->values[idx];
285               if (!d->unsafe())
286                 _gdt_user_entries[entry_number] = *d;
287             }
288
289           if (this == current_thread())
290             switch_gdt_user_entries(this);
291
292           tag = commit_result(0);
293           return true;
294         }
295
296     default:
297       return false;
298     };
299 }
300
301 //---------------------------------------------------------------------------
302 IMPLEMENTATION [ia32 & (debug | kdb)]:
303
304 #include "ipi.h"
305 #include "kernel_task.h"
306
307 /** Call the nested trap handler (either Jdb::enter_kdebugger() or the
308  * gdb stub. Setup our own stack frame */
309 PRIVATE static
310 int
311 Thread::call_nested_trap_handler(Trap_state *ts)
312 {
313   unsigned long phys_cpu = Cpu::phys_id_direct();
314   unsigned log_cpu = Cpu::p2l(phys_cpu);
315   if (log_cpu == ~0U)
316     {
317       printf("Trap on unknown CPU phys_id=%lx\n", phys_cpu);
318       log_cpu = 0;
319     }
320
321   unsigned long &ntr = nested_trap_recover.cpu(log_cpu);
322
323 #if 0
324   printf("%s: lcpu%u sp=%p t=%lu nested_trap_recover=%ld\n",
325          __func__, log_cpu, (void*)Proc::stack_pointer(), ts->_trapno, ntr);
326 #endif
327
328   int ret;
329
330   unsigned dummy1, dummy2, dummy3;
331
332   struct
333   {
334     Mword pdir;
335     FIASCO_FASTCALL int (*handler)(Trap_state*, unsigned);
336     void *stack;
337   } p;
338
339   if (!ntr)
340     p.stack = dbg_stack.cpu(log_cpu).stack_top;
341   else
342     p.stack = 0;
343
344   p.pdir = Kernel_task::kernel_task()->mem_space()->virt_to_phys((Address)Kmem::dir());
345   p.handler = nested_trap_handler;
346
347
348   // don't set %esp if gdb fault recovery to ensure that exceptions inside
349   // kdb/jdb don't overwrite the stack
350   asm volatile
351     ("mov    %%esp,%[d2]        \n\t"
352      "cmpl   $0,(%[ntr])        \n\t"
353      "jne    1f                 \n\t"
354      "mov    8(%[p]),%%esp      \n\t"
355      "1:                        \n\t"
356      "incl   (%[ntr])           \n\t"
357      "mov    %%cr3, %[d1]       \n\t"
358      "push   %[d2]              \n\t"
359      "push   %[p]               \n\t"
360      "push   %[d1]              \n\t"
361      "mov    (%[p]), %[d1]      \n\t"
362      "mov    %[d1], %%cr3       \n\t"
363      "call   *4(%[p])           \n\t"
364      "pop    %[d1]              \n\t"
365      "mov    %[d1], %%cr3       \n\t"
366      "pop    %[p]               \n\t"
367      "pop    %%esp              \n\t"
368      "cmpl   $0,(%[ntr])        \n\t"
369      "je     1f                 \n\t"
370      "decl   (%[ntr])           \n\t"
371      "1:                        \n\t"
372
373      : [ret] "=a"  (ret),
374        [d1]  "=&c" (dummy1),
375        [d2]  "=&r" (dummy2),
376              "=d"  (dummy3)
377      : [ts]      "a" (ts),
378        [cpu]     "d" (log_cpu),
379        [p]       "r" (&p),
380        [ntr]     "r" (&ntr)
381      : "memory");
382
383   return ret == 0 ? 0 : -1;
384 }