]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/arm/thread-arm.cpp
update
[l4.git] / kernel / fiasco / src / kern / arm / thread-arm.cpp
1 INTERFACE [arm]:
2
3 class Trap_state;
4
5 EXTENSION class Thread
6 {
7 private:
8   bool _in_exception;
9
10 };
11
12 // ------------------------------------------------------------------------
13 IMPLEMENTATION [arm]:
14
15 #include <cassert>
16 #include <cstdio>
17
18 #include "globals.h"
19 #include "kmem_space.h"
20 #include "mem_op.h"
21 #include "static_assert.h"
22 #include "thread_state.h"
23 #include "types.h"
24 #include "vmem_alloc.h"
25
26 enum {
27   FSR_STATUS_MASK = 0x0d,
28   FSR_TRANSL      = 0x05,
29   FSR_DOMAIN      = 0x09,
30   FSR_PERMISSION  = 0x0d,
31 };
32
33 Per_cpu<Thread::Dbg_stack> DEFINE_PER_CPU Thread::dbg_stack;
34
35 PRIVATE static
36 void
37 Thread::print_page_fault_error(Mword e)
38 {
39   char const *const excpts[] =
40     { "reset","undef. insn", "swi", "pref. abort", "data abort",
41       "XXX", "XXX", "XXX" };
42
43   unsigned ex = (e >> 20) & 0x07;
44
45   printf("(%lx) %s, %s(%c)",e & 0xff, excpts[ex],
46          (e & 0x00010000)?"user":"kernel",
47          (e & 0x00020000)?'r':'w');
48 }
49
50 PUBLIC inline
51 void FIASCO_NORETURN
52 Thread::fast_return_to_user(Mword ip, Mword sp, Vcpu_state *arg)
53 {
54   extern char __iret[];
55   assert_kdb((regs()->psr & Proc::Status_mode_mask) == Proc::Status_mode_user);
56
57   regs()->ip(ip);
58   regs()->sp(sp); // user-sp is in lazy user state and thus handled by
59                   // fill_user_state()
60   fill_user_state();
61
62   regs()->psr &= ~Proc::Status_thumb;
63
64     {
65       register Vcpu_state *r0 asm("r0") = arg;
66
67       asm volatile
68         ("mov sp, %0  \t\n"
69          "mov pc, %1  \t\n"
70          :
71          : "r" (nonull_static_cast<Return_frame*>(regs())), "r" (__iret), "r"(r0)
72         );
73     }
74   panic("__builtin_trap()");
75 }
76
77 //
78 // Public services
79 //
80
81 IMPLEMENT
82 void
83 Thread::user_invoke()
84 {
85   user_invoke_generic();
86   assert (current()->state() & Thread_ready);
87
88   Trap_state *ts = nonull_static_cast<Trap_state*>
89     (nonull_static_cast<Return_frame*>(current()->regs()));
90
91   static_assert(sizeof(ts->r[0]) == sizeof(Mword), "Size mismatch");
92   Mem::memset_mwords(&ts->r[0], 0, sizeof(ts->r) / sizeof(ts->r[0]));
93
94   if (current()->space() == sigma0_task)
95     ts->r[0] = Kmem_space::kdir()->walk(Kip::k(), 0, false, 0).phys(Kip::k());
96
97   extern char __return_from_exception;
98
99   asm volatile
100     ("  mov sp, %[stack_p]    \n"    // set stack pointer to regs structure
101      "  mov pc, %[rfe]        \n"
102      :
103      :
104      [stack_p] "r" (ts),
105      [rfe]     "r" (&__return_from_exception)
106      );
107
108   panic("should never be reached");
109   while (1)
110     {
111       current()->state_del(Thread_ready);
112       current()->schedule();
113     };
114
115   // never returns here
116 }
117
118 IMPLEMENT inline NEEDS["space.h", <cstdio>, "types.h" ,"config.h"]
119 bool Thread::handle_sigma0_page_fault( Address pfa )
120 {
121   return (mem_space()->v_insert(
122         Mem_space::Phys_addr::create((pfa & Config::SUPERPAGE_MASK)),
123         Mem_space::Addr::create(pfa & Config::SUPERPAGE_MASK),
124         Mem_space::Size(Config::SUPERPAGE_SIZE),
125         Mem_space::Page_writable | Mem_space::Page_user_accessible
126         | Mem_space::Page_cacheable)
127       != Mem_space::Insert_err_nomem);
128 }
129
130
131 extern "C" {
132
133   /**
134    * The low-level page fault handler called from entry.S.  We're invoked with
135    * interrupts turned off.  Apart from turning on interrupts in almost
136    * all cases (except for kernel page faults in TCB area), just forwards
137    * the call to Thread::handle_page_fault().
138    * @param pfa page-fault virtual address
139    * @param error_code CPU error code
140    * @return true if page fault could be resolved, false otherwise
141    */
142   Mword pagefault_entry(const Mword pfa, Mword error_code,
143                         const Mword pc, Return_frame *ret_frame)
144   {
145 #if 0 // Double PF detect
146     static unsigned long last_pfa = ~0UL;
147     LOG_MSG_3VAL(current(),"PF", pfa, error_code, pc);
148     if (last_pfa == pfa || pfa == 0)
149       kdb_ke("DBF");
150     last_pfa = pfa;
151 #endif
152     if (EXPECT_FALSE(PF::is_alignment_error(error_code)))
153       {
154         printf("KERNEL%d: alignment error at %08lx (PC: %08lx, SP: %08lx, FSR: %lx, PSR: %lx)\n",
155                current_cpu(), pfa, pc, ret_frame->usp, error_code, ret_frame->psr);
156         return false;
157       }
158
159     Thread *t = current_thread();
160
161     // Pagefault in user mode
162     if (PF::is_usermode_error(error_code))
163       {
164         if (t->vcpu_pagefault(pfa, error_code, pc))
165           return 1;
166         t->state_del(Thread_cancel);
167         Proc::sti();
168       }
169     // or interrupts were enabled
170     else if (!(ret_frame->psr & Proc::Status_IRQ_disabled))
171       Proc::sti();
172
173       // Pagefault in kernel mode and interrupts were disabled
174     else
175       {
176         // page fault in kernel memory region, not present, but mapping exists
177         if (Kmem::is_kmem_page_fault (pfa, error_code))
178           {
179             // We've interrupted a context in the kernel with disabled interrupts,
180             // the page fault address is in the kernel region, the error code is
181             // "not mapped" (as opposed to "access error"), and the region is
182             // actually valid (that is, mapped in Kmem's shared page directory,
183             // just not in the currently active page directory)
184             // Remain cli'd !!!
185           }
186         else if (!Config::conservative &&
187             !Kmem::is_kmem_page_fault (pfa, error_code))
188           {
189             // No error -- just enable interrupts.
190             Proc::sti();
191           }
192         else
193           {
194             // Error: We interrupted a cli'd kernel context touching kernel space
195             if (!Thread::log_page_fault())
196               printf("*P[%lx,%lx,%lx] ", pfa, error_code, pc);
197
198             kdb_ke ("page fault in cli mode");
199           }
200
201       }
202
203     // cache operations we carry out for user space might cause PFs, we just
204     // ignore those
205     if (EXPECT_FALSE(t->is_ignore_mem_op_in_progress()))
206       {
207         ret_frame->pc += 4;
208         return 1;
209       }
210
211     // PFs in the kern_lib_page are always write PFs due to rollbacks and
212     // insn decoding
213     if (EXPECT_FALSE((pc & Kmem::Kern_lib_base) == Kmem::Kern_lib_base))
214       error_code |= (1UL << 11);
215
216     return t->handle_page_fault(pfa, error_code, pc, ret_frame);
217   }
218
219   void slowtrap_entry(Trap_state *ts)
220   {
221     Thread *t = current_thread();
222
223     LOG_TRAP;
224
225     if (Config::Support_arm_linux_cache_API)
226       {
227         if (   ts->error_code == 0x00200000
228             && ts->r[7] == 0xf0002)
229           {
230             if (ts->r[2] == 0)
231               Mem_op::arm_mem_cache_maint(Mem_op::Op_cache_coherent,
232                                           (void *)ts->r[0], (void *)ts->r[1]);
233             return;
234           }
235       }
236
237     if (ts->exception_is_undef_insn())
238       {
239         switch (Fpu::handle_fpu_trap(ts))
240           {
241             case Fpu::Fpu_except_emulated: return;
242             case Fpu::Fpu_except_fault:
243 #ifndef NDEBUG
244                   if (!(current_thread()->state() & Thread_vcpu_enabled)
245                       && Fpu::is_enabled() && Fpu::owner(t->cpu()) == t)
246                     printf("KERNEL: FPU doesn't like us?\n");
247                   else
248 #endif
249                   if (t->switchin_fpu())
250                     {
251                       ts->pc -= (ts->psr & Proc::Status_thumb) ? 2 : 4;
252                       return;
253                     }
254
255                   ts->error_code |= 0x01000000; // tag fpu undef insn
256                   if (Fpu::exc_pending())
257                     ts->error_code |= 0x02000000; // fpinst and fpinst2 in utcb will be valid
258                   break;
259             case Fpu::Fpu_except_none: break;
260           }
261       }
262
263     // send exception IPC if requested
264     if (t->send_exception(ts))
265       return;
266
267     // exception handling failed
268     if (Config::conservative)
269       kdb_ke ("thread killed");
270
271     t->halt();
272
273   }
274
275 };
276
277 IMPLEMENT inline
278 bool
279 Thread::pagein_tcb_request(Return_frame *regs)
280 {
281   //if ((*(Mword*)regs->pc & 0xfff00fff ) == 0xe5900000)
282   if (*(Mword*)regs->pc == 0xe59ee000)
283     {
284       // printf("TCBR: %08lx\n", *(Mword*)regs->pc);
285       // skip faulting instruction
286       regs->pc += 4;
287       // tell program that a pagefault occured we cannot handle
288       regs->psr |= 0x40000000;  // set zero flag in psr
289       regs->km_lr = 0;
290
291       return true;
292     }
293   return false;
294 }
295
296 //---------------------------------------------------------------------------
297 IMPLEMENTATION [arm]:
298
299 #include "trap_state.h"
300
301
302 /** Constructor.
303     @param space the address space
304     @param id user-visible thread ID of the sender
305     @param init_prio initial priority
306     @param mcp thread's maximum controlled priority
307     @post state() != Thread_invalid
308  */
309 IMPLEMENT
310 Thread::Thread()
311   : Sender(0),  // select optimized version of constructor
312     _pager(Thread_ptr::Invalid),
313     _exc_handler(Thread_ptr::Invalid),
314     _del_observer(0)
315 {
316   assert (state() == Thread_invalid);
317
318   inc_ref();
319
320   if (Config::stack_depth)
321     std::memset((char*)this + sizeof(Thread), '5',
322                 Config::thread_block_size-sizeof(Thread)-64);
323
324   // set a magic value -- we use it later to verify the stack hasn't
325   // been overrun
326   _magic = magic;
327   _recover_jmpbuf = 0;
328   _timeout = 0;
329   _in_exception = false;
330
331   *reinterpret_cast<void(**)()> (--_kernel_sp) = user_invoke;
332
333   // clear out user regs that can be returned from the thread_ex_regs
334   // system call to prevent covert channel
335   Entry_frame *r = regs();
336   r->sp(0);
337   r->ip(0);
338   r->psr = Proc::Status_mode_user;
339
340   state_add(Thread_dead | Thread_suspended);
341
342   // ok, we're ready to go!
343 }
344
345 IMPLEMENT inline
346 Mword
347 Thread::user_sp() const
348 { return regs()->sp(); }
349
350 IMPLEMENT inline
351 void
352 Thread::user_sp(Mword sp)
353 { return regs()->sp(sp); }
354
355 IMPLEMENT inline NEEDS[Thread::exception_triggered]
356 Mword
357 Thread::user_ip() const
358 { return exception_triggered() ? _exc_cont.ip() : regs()->ip(); }
359
360 IMPLEMENT inline
361 Mword
362 Thread::user_flags() const
363 { return 0; }
364
365 IMPLEMENT inline NEEDS[Thread::exception_triggered]
366 void
367 Thread::user_ip(Mword ip)
368 {
369   if (exception_triggered())
370     _exc_cont.ip(ip);
371   else
372     {
373       Entry_frame *r = regs();
374       r->ip(ip);
375       r->psr = (r->psr & ~Proc::Status_mode_mask) | Proc::Status_mode_user;
376     }
377 }
378
379
380 PUBLIC inline NEEDS ["trap_state.h"]
381 int
382 Thread::send_exception_arch(Trap_state *)
383 {
384   // nothing to tweak on ARM
385   return 1;
386 }
387
388 PRIVATE static inline
389 void
390 Thread::save_fpu_state_to_utcb(Trap_state *ts, Utcb *u)
391 {
392   char *esu = (char *)&u->values[21];
393   Fpu::save_user_exception_state(ts, (Fpu::Exception_state_user *)esu);
394 }
395
396 PRIVATE inline
397 bool
398 Thread::invalid_ipc_buffer(void const *a)
399 {
400   if (!_in_exception)
401     return Mem_layout::in_kernel(((Address)a & Config::SUPERPAGE_MASK)
402                                  + Config::SUPERPAGE_SIZE - 1);
403
404   return false;
405 }
406
407 PROTECTED inline
408 int
409 Thread::do_trigger_exception(Entry_frame *r, void *ret_handler)
410 {
411   if (!_exc_cont.valid())
412     {
413       _exc_cont.activate(r, ret_handler);
414       return 1;
415     }
416   return 0;
417 }
418
419
420 PRIVATE static inline
421 bool FIASCO_WARN_RESULT
422 Thread::copy_utcb_to_ts(L4_msg_tag const &tag, Thread *snd, Thread *rcv,
423                         unsigned char rights)
424 {
425   Trap_state *ts = (Trap_state*)rcv->_utcb_handler;
426   Utcb *snd_utcb = snd->utcb().access();
427   Mword       s  = tag.words();
428
429   if (EXPECT_FALSE(rcv->exception_triggered()))
430     {
431       // triggered exception pending
432       Mem::memcpy_mwords (ts, snd_utcb->values, s > 15 ? 15 : s);
433       if (EXPECT_TRUE(s > 19))
434         {
435           // sanitize processor mode
436           // XXX: fix race
437           snd_utcb->values[19] &= ~Proc::Status_mode_mask; // clear mode
438           snd_utcb->values[19] |=  Proc::Status_mode_supervisor
439             | Proc::Status_interrupts_disabled;
440
441           Continuation::User_return_frame const *s
442             = reinterpret_cast<Continuation::User_return_frame const *>((char*)&snd_utcb->values[15]);
443
444           rcv->_exc_cont.set(ts, s);
445         }
446     }
447   else
448     {
449       Mem::memcpy_mwords (ts, snd_utcb->values, s > 18 ? 18 : s);
450       if (EXPECT_TRUE(s > 18))
451         ts->pc = snd_utcb->values[18];
452       if (EXPECT_TRUE(s > 19))
453         {
454           // sanitize processor mode
455           Mword p = snd_utcb->values[19];
456           p &= ~(Proc::Status_mode_mask | Proc::Status_interrupts_mask); // clear mode & irqs
457           p |=  Proc::Status_mode_user;
458           ts->psr = p;
459         }
460     }
461
462   if (tag.transfer_fpu() && (rights & L4_fpage::W))
463     snd->transfer_fpu(rcv);
464
465   if ((tag.flags() & 0x8000) && (rights & L4_fpage::W))
466     rcv->utcb().access()->user[2] = snd_utcb->values[25];
467
468   bool ret = transfer_msg_items(tag, snd, snd_utcb,
469                                 rcv, rcv->utcb().access(), rights);
470
471   rcv->state_del(Thread_in_exception);
472   return ret;
473 }
474
475
476 PRIVATE static inline NEEDS[Thread::save_fpu_state_to_utcb]
477 bool FIASCO_WARN_RESULT
478 Thread::copy_ts_to_utcb(L4_msg_tag const &, Thread *snd, Thread *rcv,
479                         unsigned char rights)
480 {
481   Trap_state *ts = (Trap_state*)snd->_utcb_handler;
482
483   {
484     Lock_guard <Cpu_lock> guard (&cpu_lock);
485     Utcb *rcv_utcb = rcv->utcb().access();
486
487     Mem::memcpy_mwords (rcv_utcb->values, ts, 15);
488     Continuation::User_return_frame *d
489       = reinterpret_cast<Continuation::User_return_frame *>((char*)&rcv_utcb->values[15]);
490
491     snd->_exc_cont.get(d, ts);
492
493
494     if (EXPECT_TRUE(!snd->exception_triggered()))
495       {
496         rcv_utcb->values[18] = ts->pc;
497         rcv_utcb->values[19] = ts->psr;
498       }
499
500     if (rcv_utcb->inherit_fpu() && (rights & L4_fpage::W))
501       snd->transfer_fpu(rcv);
502
503     save_fpu_state_to_utcb(ts, rcv_utcb);
504   }
505   return true;
506 }
507
508 PROTECTED inline
509 bool
510 Thread::invoke_arch(L4_msg_tag & /*tag*/, Utcb * /*utcb*/)
511 {
512   return false;
513 }
514
515 PROTECTED inline
516 int
517 Thread::sys_control_arch(Utcb *)
518 {
519   return 0;
520 }
521
522 // ------------------------------------------------------------------------
523 IMPLEMENTATION [arm && armv6plus]:
524
525 PROTECTED inline
526 void
527 Thread::vcpu_resume_user_arch()
528 {
529   // just an experiment for now, we cannot really take the
530   // user-writable register because user-land might already use it
531   asm volatile("mcr p15, 0, %0, c13, c0, 2"
532                : : "r" (utcb().access(true)->values[25]) : "memory");
533 }
534
535 // ------------------------------------------------------------------------
536 IMPLEMENTATION [arm && !armv6plus]:
537
538 PROTECTED inline
539 void
540 Thread::vcpu_resume_user_arch()
541 {}
542
543 //-----------------------------------------------------------------------------
544 IMPLEMENTATION [mp]:
545
546 #include "ipi.h"
547
548 EXTENSION class Thread
549 {
550 private:
551   static void kern_kdebug_ipi_entry() asm("kern_kdebug_ipi_entry");
552 };
553
554 PUBLIC static inline NEEDS["ipi.h"]
555 bool
556 Thread::check_for_ipi(unsigned irq)
557 {
558   if (Ipi::is_ipi(irq))
559     {
560       switch (irq)
561         {
562         case Ipi::Request:
563           Thread::handle_remote_requests_irq();
564           break;
565         case Ipi::Debug:
566           Ipi::eoi(Ipi::Debug);
567           kern_kdebug_ipi_entry();
568           break;
569         case Ipi::Global_request:
570           handle_global_remote_requests_irq();
571           break;
572         };
573       return true;
574     }
575
576   return false;
577 }
578
579 //-----------------------------------------------------------------------------
580 IMPLEMENTATION [!mp]:
581
582 PUBLIC static inline
583 bool
584 Thread::check_for_ipi(unsigned)
585 { return false; }