]> 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 public:
8   static void init_per_cpu(Cpu_number cpu, bool resume);
9 private:
10   bool _in_exception;
11 };
12
13 // ------------------------------------------------------------------------
14 IMPLEMENTATION [arm]:
15
16 #include <cassert>
17 #include <cstdio>
18
19 #include "globals.h"
20 #include "kmem_space.h"
21 #include "mem_op.h"
22 #include "static_assert.h"
23 #include "thread_state.h"
24 #include "types.h"
25
26 enum {
27   FSR_STATUS_MASK = 0x0d,
28   FSR_TRANSL      = 0x05,
29   FSR_DOMAIN      = 0x09,
30   FSR_PERMISSION  = 0x0d,
31 };
32
33 DEFINE_PER_CPU Per_cpu<Thread::Dbg_stack> 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   Entry_frame *r = regs();
56   assert(r->check_valid_user_psr());
57
58   r->ip(ip);
59   r->sp(sp); // user-sp is in lazy user state and thus handled by
60              // fill_user_state()
61   fill_user_state();
62   //load_tpidruro();
63
64   r->psr &= ~Proc::Status_thumb;
65
66   // extended vCPU runs the host code in ARM system mode
67   if (Proc::Is_hyp && (state() & Thread_ext_vcpu_enabled))
68     r->psr_set_mode(Proc::PSR_m_svc);
69
70     {
71       register Vcpu_state *r0 asm("r0") = arg;
72
73       asm volatile
74         ("mov sp, %0  \t\n"
75          "mov pc, %1  \t\n"
76          :
77          : "r" (nonull_static_cast<Return_frame*>(r)), "r" (__iret), "r"(r0)
78         );
79     }
80   panic("__builtin_trap()");
81 }
82
83 IMPLEMENT_DEFAULT inline
84 void
85 Thread::init_per_cpu(Cpu_number, bool)
86 {}
87
88
89 //
90 // Public services
91 //
92
93 IMPLEMENT
94 void
95 Thread::user_invoke()
96 {
97   user_invoke_generic();
98   assert (current()->state() & Thread_ready);
99
100   Trap_state *ts = nonull_static_cast<Trap_state*>
101     (nonull_static_cast<Return_frame*>(current()->regs()));
102
103   assert (((Mword)ts & 7) == 4); // Return_frame has 5 words
104
105   static_assert(sizeof(ts->r[0]) == sizeof(Mword), "Size mismatch");
106   Mem::memset_mwords(&ts->r[0], 0, sizeof(ts->r) / sizeof(ts->r[0]));
107
108   if (current()->space()->is_sigma0())
109     ts->r[0] = Kmem_space::kdir()->virt_to_phys((Address)Kip::k());
110
111   ts->psr |= Proc::Status_always_mask;
112
113   extern char __return_from_user_invoke;
114
115   asm volatile
116     ("  mov sp, %[stack_p]    \n"    // set stack pointer to regs structure
117      "  mov pc, %[rfe]        \n"
118      :
119      :
120      [stack_p] "r" (ts),
121      [rfe]     "r" (&__return_from_user_invoke)
122      );
123
124   panic("should never be reached");
125   while (1)
126     {
127       current()->state_del(Thread_ready);
128       current()->schedule();
129     };
130
131   // never returns here
132 }
133
134 IMPLEMENT inline NEEDS["space.h", "types.h", "config.h"]
135 bool Thread::handle_sigma0_page_fault(Address pfa)
136 {
137   return mem_space()
138     ->v_insert(Mem_space::Phys_addr((pfa & Config::SUPERPAGE_MASK)),
139                Virt_addr(pfa & Config::SUPERPAGE_MASK),
140                Virt_order(Config::SUPERPAGE_SHIFT) /*mem_space()->largest_page_size()*/,
141                Mem_space::Attr(L4_fpage::Rights::URWX()))
142     != Mem_space::Insert_err_nomem;
143 }
144
145
146 PUBLIC static inline
147 bool
148 Thread::check_for_kernel_mem_access_pf(Trap_state *ts, Thread *t)
149 {
150   if (EXPECT_FALSE(t->is_kernel_mem_op_hit_and_clear()))
151     {
152       Mword pc = t->exception_triggered() ? t->_exc_cont.ip() : ts->pc;
153
154       pc -= (ts->psr & Proc::Status_thumb) ? 2 : 4;
155
156       if (t->exception_triggered())
157         t->_exc_cont.ip(pc);
158       else
159         ts->pc = pc;
160
161       return true;
162     }
163
164   return false;
165 }
166
167
168 extern "C" {
169
170   /**
171    * The low-level page fault handler called from entry.S.  We're invoked with
172    * interrupts turned off.  Apart from turning on interrupts in almost
173    * all cases (except for kernel page faults in TCB area), just forwards
174    * the call to Thread::handle_page_fault().
175    * @param pfa page-fault virtual address
176    * @param error_code CPU error code
177    * @return true if page fault could be resolved, false otherwise
178    */
179   Mword pagefault_entry(const Mword pfa, Mword error_code,
180                         const Mword pc, Return_frame *ret_frame)
181   {
182     if (EXPECT_FALSE(PF::is_alignment_error(error_code)))
183       {
184         printf("KERNEL%d: alignment error at %08lx (PC: %08lx, SP: %08lx, FSR: %lx, PSR: %lx)\n",
185                cxx::int_value<Cpu_number>(current_cpu()), pfa, pc,
186                ret_frame->usp, error_code, ret_frame->psr);
187         return false;
188       }
189
190     if (EXPECT_FALSE(Thread::is_debug_exception(error_code, true)))
191       return 0;
192
193     Thread *t = current_thread();
194
195     // cache operations we carry out for user space might cause PFs, we just
196     // ignore those
197     if (EXPECT_FALSE(!PF::is_usermode_error(error_code))
198         && EXPECT_FALSE(t->is_ignore_mem_op_in_progress()))
199       {
200         t->set_kernel_mem_op_hit();
201         ret_frame->pc += 4;
202         return 1;
203       }
204
205     // Pagefault in user mode
206     if (PF::is_usermode_error(error_code))
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 << 6);
212
213         // TODO: Avoid calling Thread::map_fsr_user here everytime!
214         if (t->vcpu_pagefault(pfa, Thread::map_fsr_user(error_code, true), pc))
215           return 1;
216         t->state_del(Thread_cancel);
217       }
218
219     if (EXPECT_TRUE(PF::is_usermode_error(error_code))
220         || !(ret_frame->psr & Proc::Status_preempt_disabled)
221         || !Kmem::is_kmem_page_fault(pfa, error_code))
222       Proc::sti();
223
224     return t->handle_page_fault(pfa, error_code, pc, ret_frame);
225   }
226
227   void slowtrap_entry(Trap_state *ts)
228   {
229     ts->error_code = Thread::map_fsr_user(ts->error_code, false);
230
231     if (0)
232       printf("Trap: pfa=%08lx pc=%08lx err=%08lx psr=%lx\n", ts->pf_address, ts->pc, ts->error_code, ts->psr);
233     Thread *t = current_thread();
234
235     LOG_TRAP;
236
237     if (Config::Support_arm_linux_cache_API)
238       {
239         if (   ts->hsr().ec() == 0x11
240             && ts->r[7] == 0xf0002)
241           {
242             if (ts->r[2] == 0)
243               Mem_op::arm_mem_cache_maint(Mem_op::Op_cache_coherent,
244                                           (void *)ts->r[0], (void *)ts->r[1]);
245             ts->r[0] = 0;
246             return;
247           }
248       }
249
250     if (t->check_and_handle_coproc_faults(ts))
251       return;
252
253     if (Thread::is_debug_exception(ts->error_code))
254       {
255         Thread::handle_debug_exception(ts);
256         return;
257       }
258
259     // send exception IPC if requested
260     if (t->send_exception(ts))
261       return;
262
263     t->halt();
264   }
265 };
266
267 PUBLIC static inline NEEDS[Thread::call_nested_trap_handler]
268 void
269 Thread::handle_debug_exception(Trap_state *ts)
270 {
271   call_nested_trap_handler(ts);
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 && !arm_lpae]:
295
296 PUBLIC static inline
297 Mword
298 Thread::is_debug_exception(Mword error_code, bool just_native_type = false)
299 {
300   if (just_native_type)
301     return (error_code & 0x4f) == 2;
302
303   // LPAE type as already converted
304   return (error_code & 0xc000003f) == 0x80000022;
305 }
306
307 //---------------------------------------------------------------------------
308 IMPLEMENTATION [arm && arm_lpae]:
309
310 PUBLIC static inline
311 Mword
312 Thread::is_debug_exception(Mword error_code, bool just_native_type = false)
313 {
314   if (just_native_type)
315     return ((error_code >> 26) & 0x3f) == 0x22;
316   return (error_code & 0xc000003f) == 0x80000022;
317 }
318
319 //---------------------------------------------------------------------------
320 IMPLEMENTATION [arm]:
321
322 #include "trap_state.h"
323
324
325 /** Constructor.
326     @param space the address space
327     @param id user-visible thread ID of the sender
328     @param init_prio initial priority
329     @param mcp thread's maximum controlled priority
330     @post state() != 0
331  */
332 IMPLEMENT
333 Thread::Thread()
334   : Sender(0),  // select optimized version of constructor
335     _pager(Thread_ptr::Invalid),
336     _exc_handler(Thread_ptr::Invalid),
337     _del_observer(0)
338 {
339   assert (state(false) == 0);
340
341   inc_ref();
342   _space.space(Kernel_task::kernel_task());
343
344   if (Config::Stack_depth)
345     std::memset((char*)this + sizeof(Thread), '5',
346                 Thread::Size-sizeof(Thread)-64);
347
348   // set a magic value -- we use it later to verify the stack hasn't
349   // been overrun
350   _magic = magic;
351   _recover_jmpbuf = 0;
352   _timeout = 0;
353   _in_exception = false;
354
355   *reinterpret_cast<void(**)()> (--_kernel_sp) = user_invoke;
356
357   // clear out user regs that can be returned from the thread_ex_regs
358   // system call to prevent covert channel
359   Entry_frame *r = regs();
360   r->sp(0);
361   r->ip(0);
362   r->psr = Proc::Status_mode_user;
363   //r->psr = 0x1f; //Proc::Status_mode_user;
364
365   state_add_dirty(Thread_dead, false);
366
367   // ok, we're ready to go!
368 }
369
370 IMPLEMENT inline
371 Mword
372 Thread::user_sp() const
373 { return regs()->sp(); }
374
375 IMPLEMENT inline
376 void
377 Thread::user_sp(Mword sp)
378 { return regs()->sp(sp); }
379
380
381 IMPLEMENT inline NEEDS[Thread::exception_triggered]
382 Mword
383 Thread::user_ip() const
384 { return exception_triggered() ? _exc_cont.ip() : regs()->ip(); }
385
386 IMPLEMENT inline
387 Mword
388 Thread::user_flags() const
389 { return 0; }
390
391 IMPLEMENT inline NEEDS[Thread::exception_triggered]
392 void
393 Thread::user_ip(Mword ip)
394 {
395   if (exception_triggered())
396     _exc_cont.ip(ip);
397   else
398     {
399       Entry_frame *r = regs();
400       r->ip(ip);
401     }
402 }
403
404
405 PUBLIC inline NEEDS ["trap_state.h"]
406 int
407 Thread::send_exception_arch(Trap_state *)
408 {
409   // nothing to tweak on ARM
410   return 1;
411 }
412
413 PRIVATE inline
414 void
415 Thread::save_fpu_state_to_utcb(Trap_state *ts, Utcb *u)
416 {
417   char *esu = (char *)&u->values[21];
418   Fpu::save_user_exception_state(state() & Thread_fpu_owner,  fpu_state(),
419                                  ts, (Fpu::Exception_state_user *)esu);
420 }
421
422 PRIVATE inline
423 bool
424 Thread::invalid_ipc_buffer(void const *a)
425 {
426   if (!_in_exception)
427     return Mem_layout::in_kernel(((Address)a & Config::SUPERPAGE_MASK)
428                                  + Config::SUPERPAGE_SIZE - 1);
429
430   return false;
431 }
432
433 PROTECTED inline
434 int
435 Thread::do_trigger_exception(Entry_frame *r, void *ret_handler)
436 {
437   if (!_exc_cont.valid(r))
438     {
439       _exc_cont.activate(r, ret_handler);
440       return 1;
441     }
442   return 0;
443 }
444
445
446 PRIVATE static inline NEEDS[Thread::get_ts_tpidruro]
447 bool FIASCO_WARN_RESULT
448 Thread::copy_utcb_to_ts(L4_msg_tag tag, Thread *snd, Thread *rcv,
449                         L4_fpage::Rights rights)
450 {
451   // if the message is too short just skip the whole copy in
452   if (EXPECT_FALSE(tag.words() < (sizeof(Trap_state) / sizeof(Mword))))
453     return true;
454
455   Trap_state *ts = (Trap_state*)rcv->_utcb_handler;
456   Utcb *snd_utcb = snd->utcb().access();
457
458   if (EXPECT_FALSE(rcv->exception_triggered()))
459     {
460       // triggered exception pending
461       Mem::memcpy_mwords (ts, snd_utcb->values, 16);
462       Return_frame rf = *reinterpret_cast<Return_frame const *>((char const *)&snd_utcb->values[16]);
463       rcv->sanitize_user_state(&rf);
464       rcv->_exc_cont.set(ts, &rf);
465     }
466   else
467     rcv->copy_and_sanitize_trap_state(
468        ts, reinterpret_cast<Trap_state const *>(snd_utcb->values));
469
470   if (tag.transfer_fpu() && (rights & L4_fpage::Rights::W()))
471     snd->transfer_fpu(rcv);
472
473   if ((tag.flags() & 0x8000) && (rights & L4_fpage::Rights::W()))
474     rcv->utcb().access()->user[2] = snd_utcb->values[25];
475
476   rcv->get_ts_tpidruro(ts);
477
478   bool ret = transfer_msg_items(tag, snd, snd_utcb,
479                                 rcv, rcv->utcb().access(), rights);
480
481   rcv->state_del(Thread_in_exception);
482   return ret;
483 }
484
485
486 PRIVATE static inline NEEDS[Thread::save_fpu_state_to_utcb,
487                             Thread::set_ts_tpidruro]
488 bool FIASCO_WARN_RESULT
489 Thread::copy_ts_to_utcb(L4_msg_tag, Thread *snd, Thread *rcv,
490                         L4_fpage::Rights rights)
491 {
492   Trap_state *ts = (Trap_state*)snd->_utcb_handler;
493
494   {
495     auto guard = lock_guard(cpu_lock);
496     Utcb *rcv_utcb = rcv->utcb().access();
497
498     snd->set_ts_tpidruro(ts);
499
500     Mem::memcpy_mwords(rcv_utcb->values, ts, 16);
501     Continuation::User_return_frame *d
502       = reinterpret_cast<Continuation::User_return_frame *>((char*)&rcv_utcb->values[16]);
503
504     snd->_exc_cont.get(d, ts);
505
506
507     if (EXPECT_TRUE(!snd->exception_triggered()))
508       {
509         rcv_utcb->values[19] = ts->pc;
510         rcv_utcb->values[20] = ts->psr;
511       }
512
513     if (rcv_utcb->inherit_fpu() && (rights & L4_fpage::Rights::W()))
514       {
515         snd->save_fpu_state_to_utcb(ts, rcv_utcb);
516         snd->transfer_fpu(rcv);
517       }
518   }
519   return true;
520 }
521
522 PROTECTED inline NEEDS[Thread::set_tpidruro]
523 L4_msg_tag
524 Thread::invoke_arch(L4_msg_tag tag, Utcb *utcb)
525 {
526   switch (utcb->values[0] & Opcode_mask)
527     {
528     case Op_set_tpidruro_arm:
529       return set_tpidruro(tag, utcb);
530     default:
531       return commit_result(-L4_err::ENosys);
532     }
533 }
534
535 PROTECTED inline
536 int
537 Thread::sys_control_arch(Utcb *)
538 {
539   return 0;
540 }
541
542 PUBLIC static inline
543 bool
544 Thread::condition_valid(unsigned char cond, Unsigned32 psr)
545 {
546   // Matrix of instruction conditions and PSR flags,
547   // index into the table is the condition from insn
548   Unsigned16 v[16] =
549   {
550     0xf0f0,
551     0x0f0f,
552     0xcccc,
553     0x3333,
554     0xff00,
555     0x00ff,
556     0xaaaa,
557     0x5555,
558     0x0c0c,
559     0xf3f3,
560     0xaa55,
561     0x55aa,
562     0x0a05,
563     0xf5fa,
564     0xffff,
565     0xffff
566   };
567
568   return (v[cond] >> (psr >> 28)) & 1;
569 }
570
571 // ------------------------------------------------------------------------
572 IMPLEMENTATION [arm && (arm_em_tz || hyp)]:
573
574 IMPLEMENT_OVERRIDE inline
575 bool
576 Thread::arch_ext_vcpu_enabled()
577 { return true; }
578
579 // ------------------------------------------------------------------------
580 IMPLEMENTATION [arm && !arm_lpae]:
581
582 PUBLIC static inline
583 Mword
584 Thread::map_fsr_user(Mword fsr, bool is_only_pf)
585 {
586   static Unsigned16 const pf_map[32] =
587   {
588     /*  0x0 */ 0,
589     /*  0x1 */ 0x21, /* Alignment */
590     /*  0x2 */ 0x22, /* Debug */
591     /*  0x3 */ 0x08, /* Access flag (1st level) */
592     /*  0x4 */ 0x2000, /* Insn cache maint */
593     /*  0x5 */ 0x04, /* Transl (1st level) */
594     /*  0x6 */ 0x09, /* Access flag (2nd level) */
595     /*  0x7 */ 0x05, /* Transl (2nd level) */
596     /*  0x8 */ 0x10, /* Sync ext abort */
597     /*  0x9 */ 0x3c, /* Domain (1st level) */
598     /*  0xa */ 0,
599     /*  0xb */ 0x3d, /* Domain (2nd level) */
600     /*  0xc */ 0x14, /* Sync ext abt on PT walk (1st level) */
601     /*  0xd */ 0x0c, /* Perm (1st level) */
602     /*  0xe */ 0x15, /* Sync ext abt on PT walk (2nd level) */
603     /*  0xf */ 0x0d, /* Perm (2nd level) */
604     /* 0x10 */ 0x30, /* TLB conflict abort */
605     /* 0x11 */ 0,
606     /* 0x12 */ 0,
607     /* 0x13 */ 0,
608     /* 0x14 */ 0x34, /* Lockdown (impl-def) */
609     /* 0x15 */ 0,
610     /* 0x16 */ 0x11, /* Async ext abort */
611     /* 0x17 */ 0,
612     /* 0x18 */ 0x19, /* Async par err on mem access */
613     /* 0x19 */ 0x18, /* Sync par err on mem access */
614     /* 0x1a */ 0x3a, /* Copro abort (impl-def) */
615     /* 0x1b */ 0,
616     /* 0x1c */ 0x14, /* Sync par err on PT walk (1st level) */
617     /* 0x1d */ 0,
618     /* 0x1e */ 0x15, /* Sync par err on PT walk (2nd level) */
619     /* 0x1f */ 0,
620   };
621
622   if (is_only_pf || (fsr & 0xc0000000) == 0x80000000)
623     return pf_map[((fsr >> 10) & 1) | (fsr & 0xf)] | (fsr & ~0x43f);
624
625   return fsr;
626 }
627
628 // ------------------------------------------------------------------------
629 IMPLEMENTATION [arm && arm_lpae]:
630
631 PUBLIC static  inline
632 Mword
633 Thread::map_fsr_user(Mword fsr, bool)
634 { return fsr; }
635
636 // ------------------------------------------------------------------------
637 IMPLEMENTATION [arm && armv6plus]:
638
639 PROTECTED inline
640 void
641 Thread::vcpu_resume_user_arch()
642 {
643   // just an experiment for now, we cannot really take the
644   // user-writable register because user-land might already use it
645   asm volatile("mcr p15, 0, %0, c13, c0, 2"
646                : : "r" (utcb().access(true)->values[25]) : "memory");
647 }
648
649 PRIVATE inline
650 L4_msg_tag
651 Thread::set_tpidruro(L4_msg_tag tag, Utcb *utcb)
652 {
653   if (EXPECT_FALSE(tag.words() < 2))
654     return commit_result(-L4_err::EInval);
655
656   _tpidruro = utcb->values[1];
657   if (EXPECT_FALSE(state() & Thread_vcpu_enabled))
658     arch_update_vcpu_state(vcpu_state().access());
659
660   if (this == current_thread())
661     load_tpidruro();
662
663   return commit_result(0);
664 }
665
666 PRIVATE inline
667 void
668 Thread::get_ts_tpidruro(Trap_state *ts)
669 {
670   _tpidruro = ts->tpidruro;
671   if (this == current_thread())
672     load_tpidruro();
673 }
674
675 PRIVATE inline
676 void
677 Thread::set_ts_tpidruro(Trap_state *ts)
678 {
679   ts->tpidruro = _tpidruro;
680 }
681
682 // ------------------------------------------------------------------------
683 IMPLEMENTATION [arm && !armv6plus]:
684
685 PROTECTED inline
686 void
687 Thread::vcpu_resume_user_arch()
688 {}
689
690 PRIVATE inline
691 L4_msg_tag
692 Thread::set_tpidruro(L4_msg_tag, Utcb *)
693 {
694   return commit_result(-L4_err::EInval);
695 }
696
697 PRIVATE inline
698 void
699 Thread::get_ts_tpidruro(Trap_state *)
700 {}
701
702 PRIVATE inline
703 void
704 Thread::set_ts_tpidruro(Trap_state *)
705 {}
706
707 //-----------------------------------------------------------------------------
708 IMPLEMENTATION [mp]:
709
710 #include "ipi.h"
711 #include "irq_mgr.h"
712
713 EXTENSION class Thread
714 {
715 public:
716   static void kern_kdebug_ipi_entry() asm("kern_kdebug_ipi_entry");
717 };
718
719 class Thread_remote_rq_irq : public Irq_base
720 {
721 public:
722   // we assume IPIs to be top level, no upstream IRQ chips
723   void handle(Upstream_irq const *)
724   { Thread::handle_remote_requests_irq(); }
725
726   Thread_remote_rq_irq()
727   {
728     set_hit(&handler_wrapper<Thread_remote_rq_irq>);
729     unmask();
730   }
731
732   void switch_mode(bool) {}
733 };
734
735 class Thread_glbl_remote_rq_irq : public Irq_base
736 {
737 public:
738   // we assume IPIs to be top level, no upstream IRQ chips
739   void handle(Upstream_irq const *)
740   { Thread::handle_global_remote_requests_irq(); }
741
742   Thread_glbl_remote_rq_irq()
743   {
744     set_hit(&handler_wrapper<Thread_glbl_remote_rq_irq>);
745     unmask();
746   }
747
748   void switch_mode(bool) {}
749 };
750
751 class Thread_debug_ipi : public Irq_base
752 {
753 public:
754   // we assume IPIs to be top level, no upstream IRQ chips
755   void handle(Upstream_irq const *)
756   {
757     Ipi::eoi(Ipi::Debug, current_cpu());
758     Thread::kern_kdebug_ipi_entry();
759   }
760
761   Thread_debug_ipi()
762   {
763     set_hit(&handler_wrapper<Thread_debug_ipi>);
764     unmask();
765   }
766
767   void switch_mode(bool) {}
768 };
769
770 class Thread_timer_tick_ipi : public Irq_base
771 {
772 public:
773   void handle(Upstream_irq const *ui)
774   {
775     //Timer_tick *self = nonull_static_cast<Timer_tick *>(_s);
776     //self->ack();
777     ui->ack();
778     //self->log_timer();
779     current_thread()->handle_timer_interrupt();
780   }
781
782   Thread_timer_tick_ipi()
783   { set_hit(&handler_wrapper<Thread_timer_tick_ipi>); }
784
785   void switch_mode(bool) {}
786 };
787
788
789 //-----------------------------------------------------------------------------
790 IMPLEMENTATION [mp && !irregular_gic]:
791
792 class Arm_ipis
793 {
794 public:
795   Arm_ipis()
796   {
797     check(Irq_mgr::mgr->alloc(&remote_rq_ipi, Ipi::Request));
798     check(Irq_mgr::mgr->alloc(&glbl_remote_rq_ipi, Ipi::Global_request));
799     check(Irq_mgr::mgr->alloc(&debug_ipi, Ipi::Debug));
800     check(Irq_mgr::mgr->alloc(&timer_ipi, Ipi::Timer));
801   }
802
803   Thread_remote_rq_irq remote_rq_ipi;
804   Thread_glbl_remote_rq_irq glbl_remote_rq_ipi;
805   Thread_debug_ipi debug_ipi;
806   Thread_timer_tick_ipi timer_ipi;
807 };
808
809 static Arm_ipis _arm_ipis;
810
811 //-----------------------------------------------------------------------------
812 IMPLEMENTATION [arm && !fpu]:
813
814 PUBLIC inline
815 bool
816 Thread::check_and_handle_coproc_faults(Trap_state *)
817 {
818   return false;
819 }
820
821
822 //-----------------------------------------------------------------------------
823 IMPLEMENTATION [arm && fpu]:
824
825 PUBLIC inline
826 bool
827 Thread::check_and_handle_coproc_faults(Trap_state *ts)
828 {
829   if (!ts->exception_is_undef_insn())
830     return false;
831
832   Unsigned32 opcode;
833
834   if (ts->psr & Proc::Status_thumb)
835     {
836       Unsigned16 v = Thread::peek_user((Unsigned16 *)(ts->pc - 2), this);
837
838       if (EXPECT_FALSE(Thread::check_for_kernel_mem_access_pf(ts, this)))
839         return true;
840
841       if ((v >> 11) <= 0x1c)
842         return false;
843
844       opcode = (v << 16) | Thread::peek_user((Unsigned16 *)ts->pc, this);
845     }
846   else
847     opcode = Thread::peek_user((Unsigned32 *)(ts->pc - 4), this);
848
849   if (EXPECT_FALSE(Thread::check_for_kernel_mem_access_pf(ts, this)))
850     return true;
851
852   if (ts->psr & Proc::Status_thumb)
853     {
854       if (   (opcode & 0xef000000) == 0xef000000 // A6.3.18
855           || (opcode & 0xff100000) == 0xf9000000)
856         return Thread::handle_fpu_trap(opcode, ts);
857     }
858   else
859     {
860       if (   (opcode & 0xfe000000) == 0xf2000000 // A5.7.1
861           || (opcode & 0xff100000) == 0xf4000000)
862         return Thread::handle_fpu_trap(opcode, ts);
863     }
864
865   if ((opcode & 0x0c000e00) == 0x0c000a00)
866     return Thread::handle_fpu_trap(opcode, ts);
867
868   return false;
869 }
870
871 PUBLIC static
872 bool
873 Thread::handle_fpu_trap(Unsigned32 opcode, Trap_state *ts)
874 {
875   if (!condition_valid(opcode >> 28, ts->psr))
876     {
877       // FPU insns are 32bit, even for thumb
878       if (ts->psr & Proc::Status_thumb)
879         ts->pc += 2;
880       return true;
881     }
882
883   if (Fpu::is_enabled())
884     {
885       assert(Fpu::fpu.current().owner() == current());
886       if (Fpu::is_emu_insn(opcode))
887         return Fpu::emulate_insns(opcode, ts);
888
889       ts->hsr().ec() = 0; // tag fpu undef insn
890     }
891   else if (current_thread()->switchin_fpu())
892     {
893       if (Fpu::is_emu_insn(opcode))
894         return Fpu::emulate_insns(opcode, ts);
895       ts->pc -= (ts->psr & Proc::Status_thumb) ? 2 : 4;
896       return true;
897     }
898   else
899     {
900       ts->hsr().ec() = 0x07;
901       ts->hsr().cond() = opcode >> 28;
902       ts->hsr().cv() = 1;
903       ts->hsr().cpt_cpnr() = 10;
904     }
905
906   return false;
907 }
908
909
910 //-----------------------------------------------------------------------------
911 IMPLEMENTATION [arm && !hyp]:
912
913 PUBLIC static inline template<typename T>
914 T Thread::peek_user(T const *adr, Context *c)
915 {
916   T v;
917   c->set_ignore_mem_op_in_progress(true);
918   v = *adr;
919   c->set_ignore_mem_op_in_progress(false);
920   return v;
921 }
922
923