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