]> rtime.felk.cvut.cz Git - l4.git/blob - kernel/fiasco/src/kern/task.cpp
update
[l4.git] / kernel / fiasco / src / kern / task.cpp
1 INTERFACE:
2
3 #include "context.h"
4 #include "kobject.h"
5 #include "l4_types.h"
6 #include "rcupdate.h"
7 #include "space.h"
8 #include "spin_lock.h"
9
10 class slab_cache_anon;
11
12 /**
13  * \brief A task is a protection domain.
14  *
15  * A is derived from Space, which aggregates a set of address spaces.
16  * Additionally to a space, a task provides initialization and
17  * destruction functionality for a protection domain.
18  * Task is also derived from Rcu_item to provide RCU shutdown of tasks.
19  */
20 class Task :
21   public Kobject,
22   public Space
23 {
24   FIASCO_DECLARE_KOBJ();
25
26   friend class Jdb_space;
27
28 private:
29   /// \brief Do host (platform) specific initialization.
30   void host_init();
31
32   /// \brief Map the trace buffer to the user address space.
33   void map_tbuf();
34
35 public:
36   enum Operation
37   {
38     Map         = 0,
39     Unmap       = 1,
40     Cap_info    = 2,
41     Add_ku_mem  = 3,
42     Ldt_set_x86 = 0x11,
43     Vm_ops      = 0x20,
44   };
45
46 private:
47   /// map the global utcb pointer page into this task
48   void map_utcb_ptr_page();
49 };
50
51
52 //---------------------------------------------------------------------------
53 IMPLEMENTATION:
54
55 #include "atomic.h"
56 #include "auto_ptr.h"
57 #include "config.h"
58 #include "entry_frame.h"
59 #include "globals.h"
60 #include "kdb_ke.h"
61 #include "kmem.h"
62 #include "kmem_slab_simple.h"
63 #include "l4_types.h"
64 #include "l4_buf_iter.h"
65 #include "logdefs.h"
66 #include "map_util.h"
67 #include "mem_layout.h"
68 #include "ram_quota.h"
69 #include "paging.h"
70 #include "vmem_alloc.h"
71
72 FIASCO_DEFINE_KOBJ(Task);
73
74 static Kmem_slab_t<Task::Ku_mem> _k_u_mem_list_alloc("Ku_mem");
75 slab_cache_anon *Space::Ku_mem::a = &_k_u_mem_list_alloc;
76
77 extern "C" void vcpu_resume(Trap_state *, Return_frame *sp)
78    FIASCO_FASTCALL FIASCO_NORETURN;
79
80 PUBLIC virtual
81 int
82 Task::resume_vcpu(Context *ctxt, Vcpu_state *vcpu, bool user_mode)
83 {
84   Trap_state ts;
85   memcpy(&ts, &vcpu->_ts, sizeof(Trap_state));
86
87   assert_kdb(cpu_lock.test());
88
89   ts.sanitize_user_state();
90
91   // FIXME: UX is currently broken
92   /* UX:ctxt->vcpu_resume_user_arch(); */
93   if (user_mode)
94     vcpu->state |= Vcpu_state::F_traps | Vcpu_state::F_exceptions
95                    | Vcpu_state::F_debug_exc;
96
97   ctxt->space_ref()->user_mode(user_mode);
98   switchin_context(ctxt->space());
99   vcpu_resume(&ts, ctxt->regs());
100 }
101
102 PUBLIC virtual
103 bool
104 Task::put()
105 { return dec_ref() == 0; }
106
107 PRIVATE
108 int
109 Task::alloc_ku_mem_chunk(User<void>::Ptr u_addr, unsigned size, void **k_addr)
110 {
111   assert_kdb ((size & (size - 1)) == 0);
112
113   Mapped_allocator *const alloc = Mapped_allocator::allocator();
114   void *p = alloc->q_unaligned_alloc(ram_quota(), size);
115
116   if (EXPECT_FALSE(!p))
117     return -L4_err::ENomem;
118
119   // clean up utcbs
120   memset(p, 0, size);
121
122   unsigned long page_size = Config::PAGE_SIZE;
123
124   // the following works because the size is a power of two
125   // and once we have size larger than a super page we have
126   // always multiples of superpages
127   if (size >= Config::SUPERPAGE_SIZE)
128     page_size = Config::SUPERPAGE_SIZE;
129
130   for (unsigned long i = 0; i < size; i += page_size)
131     {
132       Address kern_va = (Address)p + i;
133       Address user_va = (Address)u_addr.get() + i;
134       Address pa = mem_space()->pmem_to_phys(kern_va);
135
136       // must be valid physical address
137       assert(pa != ~0UL);
138
139       Mem_space::Status res =
140         mem_space()->v_insert(Mem_space::Phys_addr(pa),
141             Mem_space::Addr(user_va), Mem_space::Size(page_size),
142             Mem_space::Page_writable | Mem_space::Page_user_accessible
143             | Mem_space::Page_cacheable);
144
145       switch (res)
146         {
147         case Mem_space::Insert_ok: break;
148         case Mem_space::Insert_err_nomem:
149           free_ku_mem_chunk(p, u_addr, size, i);
150           return -L4_err::ENomem;
151
152         case Mem_space::Insert_err_exists:
153           free_ku_mem_chunk(p, u_addr, size, i);
154           return -L4_err::EExists;
155
156         default:
157           printf("UTCB mapping failed: va=%p, ph=%p, res=%d\n",
158               (void*)user_va, (void*)kern_va, res);
159           kdb_ke("BUG in utcb allocation");
160           free_ku_mem_chunk(p, u_addr, size, i);
161           return 0;
162         }
163     }
164
165   *k_addr = p;
166   return 0;
167 }
168
169
170 PRIVATE
171 int
172 Task::alloc_ku_mem(L4_fpage ku_area)
173 {
174   if (ku_area.order() < Config::PAGE_SHIFT || ku_area.order() > 20)
175     return -L4_err::EInval;
176
177   Mword sz = 1UL << ku_area.order();
178
179   Ku_mem *m = new (ram_quota()) Ku_mem();
180
181   if (!m)
182     return -L4_err::ENomem;
183
184   User<void>::Ptr u_addr((void*)Virt_addr(ku_area.mem_address()).value());
185
186   void *p;
187   if (int e = alloc_ku_mem_chunk(u_addr, sz, &p))
188     {
189       m->free(ram_quota());
190       return e;
191     }
192
193   m->u_addr = u_addr;
194   m->k_addr = p;
195   m->size = sz;
196
197   // safely add the new Ku_mem object to the list
198   do
199     {
200       m->next = _ku_mem;
201     }
202   while (!mp_cas(&_ku_mem, m->next, m));
203
204   return 0;
205 }
206
207 PRIVATE inline NOEXPORT
208 void
209 Task::free_ku_mem(Ku_mem *m)
210 {
211   free_ku_mem_chunk(m->k_addr, m->u_addr, m->size, m->size);
212   m->free(ram_quota());
213 }
214
215 PRIVATE
216 void
217 Task::free_ku_mem_chunk(void *k_addr, User<void>::Ptr u_addr, unsigned size,
218                         unsigned mapped_size)
219 {
220
221   Mapped_allocator * const alloc = Mapped_allocator::allocator();
222   unsigned long page_size = Config::PAGE_SIZE;
223
224   // the following works because the size is a poer of two
225   // and once we have size larger than a super page we have
226   // always multiples of superpages
227   if (size >= Config::SUPERPAGE_SIZE)
228     page_size = Config::SUPERPAGE_SIZE;
229
230   for (unsigned long i = 0; i < mapped_size; i += page_size)
231     {
232       Address user_va = (Address)u_addr.get() + i;
233       mem_space()->v_delete(Mem_space::Addr(user_va),
234                             Mem_space::Size(page_size));
235     }
236
237   alloc->q_unaligned_free(ram_quota(), size, k_addr);
238 }
239
240 PRIVATE
241 void
242 Task::free_ku_mem()
243 {
244   Ku_mem *m = _ku_mem;
245   _ku_mem = 0;
246
247   while (m)
248     {
249       Ku_mem *d = m;
250       m = m->next;
251
252       free_ku_mem(d);
253     }
254 }
255
256
257 /** Allocate space for the UTCBs of all threads in this task.
258  *  @ return true on success, false if not enough memory for the UTCBs
259  */
260 PUBLIC
261 bool
262 Task::initialize()
263 {
264   // For UX, map the UTCB pointer page. For ia32, do nothing
265   map_utcb_ptr_page();
266
267   return true;
268 }
269
270 /**
271  * \brief Create a normal Task.
272  * \pre \a parent must be valid and exist.
273  */
274 PUBLIC
275 template< typename SPACE_FACTORY >
276 Task::Task(SPACE_FACTORY const &sf, Ram_quota *q)
277   : Space(sf, q)
278 {
279   host_init();
280
281   // increment reference counter from zero
282   inc_ref(true);
283
284   if (mem_space()->is_sigma0())
285     map_tbuf();
286 }
287
288 PROTECTED template<typename SPACE_FACTORY>
289 Task::Task(SPACE_FACTORY const &sf, Ram_quota *q, Mem_space::Dir_type* pdir)
290   : Space(sf, q, pdir)
291 {
292   // increment reference counter from zero
293   inc_ref(true);
294 }
295
296 // The allocator for tasks
297 static Kmem_slab_t<Task> _task_allocator("Task");
298
299 PROTECTED static
300 slab_cache_anon*
301 Task::allocator()
302 { return &_task_allocator; }
303
304
305 PROTECTED inline NEEDS["kmem_slab_simple.h"]
306 void *
307 Task::operator new (size_t size, void *p)
308 {
309   (void)size;
310   assert (size == sizeof (Task));
311   return p;
312 }
313
314
315 PUBLIC //inline NEEDS["kmem_slab_simple.h"]
316 void
317 Task::operator delete (void *ptr)
318 {
319   Task *t = reinterpret_cast<Task*>(ptr);
320   LOG_TRACE("Kobject delete", "del", current(), __fmt_kobj_destroy,
321             Log_destroy *l = tbe->payload<Log_destroy>();
322             l->id = t->dbg_id();
323             l->obj = t;
324             l->type = "Task";
325             l->ram = t->ram_quota()->current());
326
327   allocator()->q_free(t->ram_quota(), ptr);
328 }
329
330
331 PUBLIC template< typename SPACE_FACTORY >
332 static
333 Task *
334 Task::create(SPACE_FACTORY const &sf, Ram_quota *quota,
335              L4_fpage const &utcb_area)
336 {
337   void *t = allocator()->q_alloc(quota);
338   if (!t)
339     return 0;
340
341   auto_ptr<Task> a(new (t) Task(sf, quota));
342   if (!a->valid())
343     return 0;
344
345   if (utcb_area.is_valid())
346     {
347       int e = a->alloc_ku_mem(utcb_area);
348       if (e < 0)
349         return 0;
350     }
351
352   return a.release();
353 }
354
355 PUBLIC inline
356 bool
357 Task::valid() const
358 { return mem_space()->valid(); }
359
360
361 /**
362  * \brief Shutdown the task.
363  *
364  * Currently:
365  * -# Unbind and delete all contexts bound to this task.
366  * -# Unmap everything from all spaces.
367  * -# Delete child tasks.
368  */
369 PUBLIC
370 void
371 Task::destroy(Kobject ***reap_list)
372 {
373   Kobject::destroy(reap_list);
374
375   fpage_unmap(this, L4_fpage::all_spaces(L4_fpage::RWX), L4_map_mask::full(), reap_list);
376 }
377
378 PRIVATE inline NOEXPORT
379 L4_msg_tag
380 Task::sys_map(unsigned char rights, Syscall_frame *f, Utcb *utcb)
381 {
382   LOG_TRACE("Task map", "map", ::current(), __task_unmap_fmt,
383       Log_unmap *lu = tbe->payload<Log_unmap>();
384       lu->id = dbg_id();
385       lu->mask  = utcb->values[1];
386       lu->fpage = utcb->values[2]);
387
388   if (EXPECT_FALSE(!(rights & L4_fpage::W)))
389     return commit_result(-L4_err::EPerm);
390
391   L4_msg_tag const tag = f->tag();
392
393   Obj_space *s = current()->space()->obj_space();
394   L4_snd_item_iter snd_items(utcb, tag.words());
395
396   if (EXPECT_FALSE(!tag.items() || !snd_items.next()))
397     return commit_result(-L4_err::EInval);
398
399   L4_fpage src_task(snd_items.get()->d);
400   if (EXPECT_FALSE(!src_task.is_objpage()))
401     return commit_result(-L4_err::EInval);
402
403   Task *from = Kobject::dcast<Task*>(s->lookup_local(src_task.obj_index()));
404   if (!from)
405     return commit_result(-L4_err::EInval);
406
407   Reap_list rl;
408   L4_error ret;
409
410     {
411       // enforce lock order to prevent deadlocks.
412       // always take lock from task with the lower memory address first
413       Lock_guard_2<Lock> guard;
414
415       // FIXME: avoid locking the current task, it is not needed
416       if (!guard.lock(&existence_lock, &from->existence_lock))
417         return commit_result(-L4_err::EInval);
418
419       cpu_lock.clear();
420
421       ret = fpage_map(from, L4_fpage(utcb->values[2]), this,
422                       L4_fpage::all_spaces(), L4_msg_item(utcb->values[1]), &rl);
423       cpu_lock.lock();
424     }
425
426   cpu_lock.clear();
427   rl.del();
428   cpu_lock.lock();
429
430   // FIXME: treat reaped stuff
431   if (ret.ok())
432     return commit_result(0);
433   else
434     return commit_error(utcb, ret);
435 }
436
437
438 PRIVATE inline NOEXPORT
439 L4_msg_tag
440 Task::sys_unmap(Syscall_frame *f, Utcb *utcb)
441 {
442   Reap_list rl;
443   unsigned words = f->tag().words();
444
445   LOG_TRACE("Task unmap", "unm", ::current(), __task_unmap_fmt,
446             Log_unmap *lu = tbe->payload<Log_unmap>();
447             lu->id = dbg_id();
448             lu->mask  = utcb->values[1];
449             lu->fpage = utcb->values[2]);
450
451     {
452       Lock_guard<Lock> guard;
453
454       // FIXME: avoid locking the current task, it is not needed
455       if (!guard.try_lock(&existence_lock))
456         return commit_error(utcb, L4_error::Not_existent);
457
458       cpu_lock.clear();
459
460       L4_map_mask m(utcb->values[1]);
461
462       for (unsigned i = 2; i < words; ++i)
463         {
464           unsigned const flushed = fpage_unmap(this, L4_fpage(utcb->values[i]), m, rl.list());
465           utcb->values[i] = (utcb->values[i] & ~0xfUL) | flushed;
466         }
467       cpu_lock.lock();
468     }
469
470   cpu_lock.clear();
471   rl.del();
472   cpu_lock.lock();
473
474   return commit_result(0, words);
475 }
476
477 PRIVATE inline NOEXPORT
478 L4_msg_tag
479 Task::sys_cap_valid(Syscall_frame *, Utcb *utcb)
480 {
481   L4_obj_ref obj(utcb->values[1]);
482
483   if (obj.special())
484     return commit_result(0);
485
486   Obj_space::Capability cap = obj_space()->lookup(obj.cap());
487   if (EXPECT_TRUE(cap.valid()))
488     {
489       if (!(utcb->values[1] & 1))
490         return commit_result(1);
491       else
492         return commit_result(cap.obj()->map_root()->cap_ref_cnt());
493     }
494   else
495     return commit_result(0);
496 }
497
498 PRIVATE inline NOEXPORT
499 L4_msg_tag
500 Task::sys_caps_equal(Syscall_frame *, Utcb *utcb)
501 {
502   L4_obj_ref obj_a(utcb->values[1]);
503   L4_obj_ref obj_b(utcb->values[2]);
504
505   if (obj_a == obj_b)
506     return commit_result(1);
507
508   if (obj_a.special() || obj_b.special())
509     return commit_result(obj_a.special_cap() == obj_b.special_cap());
510
511   Obj_space::Capability c_a = obj_space()->lookup(obj_a.cap());
512   Obj_space::Capability c_b = obj_space()->lookup(obj_b.cap());
513
514   return commit_result(c_a == c_b);
515 }
516
517 PRIVATE inline NOEXPORT
518 L4_msg_tag
519 Task::sys_add_ku_mem(Syscall_frame *f, Utcb *utcb)
520 {
521   unsigned const w = f->tag().words();
522   for (unsigned i = 1; i < w; ++i)
523     {
524       L4_fpage ku_fp(utcb->values[i]);
525       if (!ku_fp.is_valid() || !ku_fp.is_mempage())
526         return commit_result(-L4_err::EInval);
527
528       int e = alloc_ku_mem(ku_fp);
529       if (e < 0)
530         return commit_result(e);
531     }
532
533   return commit_result(0);
534 }
535
536 PRIVATE inline NOEXPORT
537 L4_msg_tag
538 Task::sys_cap_info(Syscall_frame *f, Utcb *utcb)
539 {
540   L4_msg_tag const &tag = f->tag();
541
542   switch (tag.words())
543     {
544     default: return commit_result(-L4_err::EInval);
545     case 2:  return sys_cap_valid(f, utcb);
546     case 3:  return sys_caps_equal(f, utcb);
547     }
548 }
549
550
551 PUBLIC
552 void
553 Task::invoke(L4_obj_ref, Mword rights, Syscall_frame *f, Utcb *utcb)
554 {
555   if (EXPECT_FALSE(f->tag().proto() != L4_msg_tag::Label_task))
556     {
557       f->tag(commit_result(-L4_err::EBadproto));
558       return;
559     }
560
561   switch (utcb->values[0])
562     {
563     case Map:
564       f->tag(sys_map(rights, f, utcb));
565       return;
566     case Unmap:
567       f->tag(sys_unmap(f, utcb));
568       return;
569     case Cap_info:
570       f->tag(sys_cap_info(f, utcb));
571       return;
572     case Add_ku_mem:
573       f->tag(sys_add_ku_mem(f, utcb));
574       return;
575     default:
576       L4_msg_tag tag = f->tag();
577       if (invoke_arch(tag, utcb))
578         f->tag(tag);
579       else
580         f->tag(commit_result(-L4_err::ENosys));
581       return;
582     }
583 }
584
585
586 //---------------------------------------------------------------------------
587 IMPLEMENTATION [!ux]:
588
589 IMPLEMENT inline
590 void
591 Task::map_utcb_ptr_page()
592 {}
593
594 IMPLEMENT inline
595 void
596 Task::host_init()
597 {}
598
599 IMPLEMENT inline
600 void
601 Task::map_tbuf()
602 {}
603
604 PUBLIC inline
605 Task::~Task()
606 { free_ku_mem(); }
607
608
609 // ---------------------------------------------------------------------------
610 INTERFACE [debug]:
611
612 EXTENSION class Task
613 {
614 private:
615   struct Log_unmap
616   {
617     Mword id;
618     Mword mask;
619     Mword fpage;
620   } __attribute__((packed));
621
622   static unsigned unmap_fmt(Tb_entry *, int max, char *buf) asm ("__task_unmap_fmt");
623 };
624
625 // ---------------------------------------------------------------------------
626 IMPLEMENTATION [debug]:
627
628 IMPLEMENT
629 unsigned
630 Task::unmap_fmt(Tb_entry *e, int max, char *buf)
631 {
632   Log_unmap *l = e->payload<Log_unmap>();
633   L4_fpage fp(l->fpage);
634   return snprintf(buf, max, "task=[U:%lx] mask=%lx fpage=[%u/%u]%lx",
635                   l->id, l->mask, (unsigned)fp.order(), fp.type(), l->fpage);
636 }
637
638 // ---------------------------------------------------------------------------
639 IMPLEMENTATION[!ia32 || !svm]:
640
641 PRIVATE inline NOEXPORT
642 L4_msg_tag
643 Task::sys_vm_run(Syscall_frame *, Utcb *)
644 {
645   return commit_result(-L4_err::ENosys);
646 }
647