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