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