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