]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/plr/server/src/app_thread.cc
Update
[l4.git] / l4 / pkg / plr / server / src / app_thread.cc
1 /*
2  * app_thread.cc --
3  *
4  *     App_thread functions for creating and preparing a new VCPU
5  *
6  * (c) 2011-2013 Björn Döbel <doebel@os.inf.tu-dresden.de>,
7  *     economic rights: Technische Universität Dresden (Germany)
8  * This file is part of TUD:OS and distributed under the terms of the
9  * GNU General Public License 2.
10  * Please see the COPYING-GPL-2 file for details.
11  */
12
13 #include "app"
14 #include "app_loading"
15 #include "thread_group.h"
16
17 #include <l4/libloader/remote_app_model>
18 #include <pthread-l4.h>
19
20 void Romain::App_thread::alloc_vcpu_mem()
21 {
22         /* Alloc vUTCB */
23         l4_addr_t kumem;
24         L4Re::Util::kumem_alloc(&kumem, 0);
25         _check(kumem == 0, "out of memory in kumem_alloc");
26
27         _vcpu_utcb = (l4_utcb_t *)kumem;
28         _vcpu = L4vcpu::Vcpu::cast(kumem + L4_UTCB_OFFSET);
29
30         /* store segment registers - stolen from the example */
31         //_vcpu->r()->gs = _master_ds;
32         _vcpu->r()->fs = _master_ds & 0xFF;
33         _vcpu->r()->es = _master_ds & 0xFF;
34         _vcpu->r()->ds = _master_ds & 0xFF;
35         _vcpu->r()->ss = _master_ds & 0xFF;
36
37         /* We want to catch ALL exceptions for this vCPU. */
38         _vcpu->saved_state()->set(
39                                   L4_VCPU_F_IRQ
40                                   | L4_VCPU_F_PAGE_FAULTS
41                                   | L4_VCPU_F_EXCEPTIONS
42                                   | L4_VCPU_F_DEBUG_EXC
43                                   | L4_VCPU_F_USER_MODE
44                                   | L4_VCPU_F_FPU_ENABLED
45         );
46
47         DEBUG() << "VCPU: utcb = " << (void*)vcpu_utcb()
48                 << " vcpu @ " << (void*)vcpu();
49 }
50
51
52 void Romain::App_thread::touch_stacks()
53 {
54         /* We need to touch at least the handler stack, because upon entry, the vCPU
55          * still has interrupts enabled and we must not raise one by causing a page
56          * fault on the stack area.
57          */
58         DEBUG() << "Stack info:";
59         DEBUG() << "   handler stack @ " << (void*)_handler_stack
60                 << " - " << (void*)(_handler_stack + sizeof(_handler_stack));
61
62         l4_touch_rw(_handler_stack, sizeof(_handler_stack));
63 }
64
65
66 void Romain::App_thread::alloc_vcpu_cap()
67 {
68 #if 0
69         _vcpu_cap = chkcap(L4Re::Util::cap_alloc.alloc<L4::Thread>(),
70                            "vCPU cap alloc");
71         chksys(L4Re::Env::env()->factory()->create_thread(_vcpu_cap),
72                "create thread");
73         l4_debugger_set_object_name(_vcpu_cap.cap(), "vcpu thread");
74 #endif
75 }
76
77
78 extern "C" void* pthread_fn(void *);
79
80 /*
81  * Create a replica thread
82  */
83 void Romain::App_thread::start()
84 {
85         /*
86          * We only set handler IP and SP here, because beforehand our creator
87          * may have modified them.
88          */
89         _vcpu->entry_sp(handler_sp());
90         _vcpu->entry_ip((l4_umword_t)_handler_fn);
91
92         l4_mword_t err = pthread_create(&_pthread, NULL, pthread_fn, this);
93         _check(err != 0, "pthread_create");
94
95         //_vcpu_cap = Pthread::L4::cap(_pthread);
96 }
97
98
99 /*
100  * Calculate checksum of the replica's state
101  *
102  * This checksum is used to compare replica states.
103  */
104 l4_umword_t
105 Romain::App_thread::csum_state()
106 {
107         // XXX: this should also include the UTCB, which
108         //      means the _used_ part of the UTCB
109         return _vcpu->r()->ip
110              + _vcpu->r()->sp
111              + _vcpu->r()->ax
112              + _vcpu->r()->bx
113              + _vcpu->r()->cx
114              + _vcpu->r()->dx
115              + _vcpu->r()->bp
116              /*+ _vcpu->r()->fs
117              + _vcpu->r()->gs*/
118              ;
119 }
120
121
122 void Romain::App_thread::commit_client_gdt()
123 {
124         vcpu()->r()->fs = fiasco_gdt_set(vcpu_cap().cap(), gdt(), gdt_size(), 1, l4_utcb());
125
126         if (_client_gdt[1].base_low != 0)
127                 vcpu()->r()->gs = vcpu()->r()->fs + 8;
128
129         DEBUG() << "CPU " << cpu() << " FS " << std::hex << vcpu()->r()->fs;
130         DEBUG() << "CPU " << cpu() << " GS " << std::hex << vcpu()->r()->gs;
131         //vcpu()->print_state();
132         _gdt_modified = false;
133 }
134
135
136 void
137 Romain::Thread_group::ex_regs(Romain::App_thread *caller)
138 {
139         /*
140          * Thoughts on ex_regs
141          *
142          * One use case of ex_regs is for an external thread to halt execution
143          * of a thread, store the halted thread's state and later continue from
144          * this point. As replicated threads execute independently, we will not
145          * stop all replicas in the same state. However, we require the replicas
146          * to be in the same state on resumption.
147          *
148          * To make things deterministic, we stop all replicas. As a heuristic, we
149          * then assume that the replicas are all correct (as all state
150          * difference can be attributed to diverging execution). We select the last
151          * stopped replica (potentially being the most advanced) as good copy and
152          * copy its state over to all other replicas, thereby forcing them to the
153          * same state.
154          *
155          * 1) This may shadow certain errors that occurred prior to the ex_regs.
156          *    -> we need to evaluate how often that happens
157          *
158          * 2) HP NonStop instead executes replicas in lock-step until it can
159          *    figure out which is the most advanced. THen they execute everyone
160          *    else up to this point.
161          *    -> higher overhead
162          *    -> no lack in error detection
163          *
164          * 3) Can we detect situations where this happens as hard overwrite of
165          *    the replicated thread's state -> hence we would not need to first
166          *    enforce identical states? -> heuristics only, cannot determine
167          *    upfront if the caller will later reuse the state.
168          *
169          */
170         bool need_to_stop = !this->stopped;
171
172         if (need_to_stop) {
173                 enter_kdebug("stop thread magic goes here");
174         }
175
176         l4_msg_regs_t *buf = l4_utcb_mr_u(reinterpret_cast<l4_utcb_t*>(caller->remote_utcb()));
177         DEBUG() << std::hex << buf->mr[0];
178         DEBUG() << std::hex << buf->mr[1];
179         DEBUG() << std::hex << buf->mr[2];
180
181         Romain::App_thread* thread = threads[0];
182         l4_umword_t eip = thread->vcpu()->r()->ip;
183         l4_umword_t esp = thread->vcpu()->r()->sp;
184
185         DEBUG() << (void*)thread->vcpu();
186         for (l4_umword_t i = 0; i < threads.size(); ++i) {
187                 threads[i]->vcpu()->r()->ip = buf->mr[1];
188                 threads[i]->vcpu()->r()->sp = buf->mr[2];
189         }
190
191         buf->mr[1] = eip;
192         buf->mr[2] = esp;
193
194         caller->vcpu()->r()->ax = l4_msgtag(0,3,0,0).raw;
195
196         if (need_to_stop) {
197                 /*
198                  * If we stopped the threads before,
199                  * we need to reactivate them now.
200                  */
201                 enter_kdebug("reactivation magic goes here");
202         }
203         //enter_kdebug("Thread_group::ex_regs");
204 }
205
206
207 void
208 Romain::Thread_group::scheduler_run(Romain::App_thread *caller)
209 {
210         l4_msg_regs_t *buf = l4_utcb_mr_u(reinterpret_cast<l4_utcb_t*>(caller->remote_utcb()));
211         
212         DEBUG() << "Thread_group::scheduler_run("
213                 << (void*)threads[0]->vcpu()
214                 << ")";
215         DEBUG() << "granularity | affinity.offs : " << std::hex << buf->mr[1];
216         DEBUG() << "affinity.map                : " << std::hex << buf->mr[2];
217         DEBUG() << "prio                        : " << std::hex << buf->mr[3];
218         DEBUG() << "quantum                     : " << std::hex << buf->mr[4];
219         DEBUG() << "obj_control                 : " << std::hex << buf->mr[5];
220         DEBUG() << "thread                      : " << std::hex << buf->mr[6];
221
222         activate();
223
224         //enter_kdebug("Thread_group::schedule()");
225 }
226
227
228 void
229 Romain::Thread_group::sanity_check_control(l4_umword_t flags, l4_utcb_t *utcb)
230 {
231         DEBUG() << "Control flags: " << std::hex << l4_utcb_mr_u(utcb)->mr[L4_THREAD_CONTROL_MR_IDX_FLAGS];
232
233         if ((flags & L4_THREAD_CONTROL_ALIEN) ||
234                 (flags & L4_THREAD_CONTROL_UX_NATIVE)) {
235                 ERROR() << "ux_native and alien not supported yet\n";
236         }
237
238         if (flags & L4_THREAD_CONTROL_BIND_TASK) {
239                 l4_fpage_t fp;
240                 fp.raw = l4_utcb_mr_u(utcb)->mr[L4_THREAD_CONTROL_MR_IDX_BIND_TASK + 1];
241                 DEBUG() << std::hex << "L4_THREAD_CONTROL_BIND_TASK := ("
242                         << l4_utcb_mr_u(utcb)->mr[L4_THREAD_CONTROL_MR_IDX_BIND_UTCB] << ", "
243                         << l4_fpage_page(fp) << ")";
244                 if (l4_fpage_page(fp) != (L4Re::This_task >> L4_CAP_SHIFT)) {
245                         ERROR() << "Binding to different task not supported yet.\n";
246                         enter_kdebug("error");
247                 }
248                 /* Apart from these checks, don't do anything. The replica vCPUs
249                  * are already bound.
250                  */
251         }
252
253         l4_umword_t handler = 0;
254         l4_umword_t pager   = 0;
255
256         if (flags & L4_THREAD_CONTROL_SET_PAGER) {
257                 pager = l4_utcb_mr_u(utcb)->mr[L4_THREAD_CONTROL_MR_IDX_PAGER];
258                 DEBUG() << "pager <- " << std::hex << pager;
259         }
260
261         if (flags & L4_THREAD_CONTROL_SET_EXC_HANDLER) {
262                 handler = l4_utcb_mr_u(utcb)->mr[L4_THREAD_CONTROL_MR_IDX_EXC_HANDLER];
263                 DEBUG() << "exc handler <- " << std::hex << handler;
264         }
265
266         if (handler && pager) {
267                 if ((handler != pager)  || 
268                         (handler != Ldr::Remote_app_std_caps::Rm_thread_cap << L4_CAP_SHIFT) ||
269                         (pager != Ldr::Remote_app_std_caps::Rm_thread_cap << L4_CAP_SHIFT)) {
270                         ERROR() << "setting different pager or exc. handler not supported yet\n";
271                         enter_kdebug("error");
272                 }
273         }
274
275         if (handler && (handler != Ldr::Remote_app_std_caps::Rm_thread_cap << L4_CAP_SHIFT)) {
276                 ERROR() << "setting non-standard pager not supported yet\n";
277         }
278
279         if (pager && (pager != Ldr::Remote_app_std_caps::Rm_thread_cap << L4_CAP_SHIFT)) {
280                 ERROR() << "setting non-standard exc. handler not supported yet\n";
281         }
282 }
283
284
285 void
286 Romain::Thread_group::control(Romain::App_thread *t, l4_utcb_t *utcb, Romain::App_model *am)
287 {
288         l4_umword_t flags = l4_utcb_mr_u(utcb)->mr[L4_THREAD_CONTROL_MR_IDX_FLAGS];
289
290         sanity_check_control(flags, utcb);
291
292         if (flags & L4_THREAD_CONTROL_BIND_TASK) {
293                 l4_addr_t utcb_remote = l4_utcb_mr_u(utcb)->mr[L4_THREAD_CONTROL_MR_IDX_BIND_UTCB];
294                 DEBUG() << "Setting remote UTCB to " << (void*)utcb_remote;
295
296                 L4::Cap<L4Re::Dataspace> mem;
297                 Romain::Region_map::allocate_ds(&mem, L4_PAGESIZE);
298                 l4_addr_t local_addr = Romain::Region_map::allocate_and_attach(&mem, L4_PAGESIZE, 0, 0);
299                 DEBUG() << "Attached TIP to " << (void*)local_addr;
300
301                 /* store UTCB address in TIP */
302                 *reinterpret_cast<l4_umword_t*>(local_addr) = utcb_remote;
303
304                 am->rm()->activate(0);
305                 void* tip_addr = (void*)0x10000;
306                 tip_addr = am->rm()->attach(tip_addr, L4_PAGESIZE,
307                                             Romain::Region_handler(mem, L4_INVALID_CAP, 0,
308                                                                    0, Romain::Region(local_addr, local_addr + L4_PAGESIZE - 1)),
309                                             L4Re::Rm::Search_addr, L4_PAGESHIFT, false);
310                 am->rm()->release();
311
312                 INFO() << "Remote TIP address: " << tip_addr;
313
314                 for (l4_umword_t i = 0; i < threads.size(); ++i) {
315                         threads[i]->setup_utcb_segdesc(reinterpret_cast<l4_addr_t>(tip_addr), 4);
316
317                         l4_addr_t utcb_local = am->rm()->remote_to_local(utcb_remote, i);
318                         threads[i]->remote_utcb(utcb_local);
319                         //threads[i]->commit_client_gdt();
320                 }
321
322                 //enter_kdebug("utcb");
323         }
324
325         /*
326          * Our current assumption is that the replicated app uses only default
327          * threading features, e.g. does not change pager, exception handler or
328          * any other thread features. Therefore, after sanity checking for these
329          * assumptions, we simply pretend everything went alright.
330          */
331         t->vcpu()->r()->ax = l4_msgtag(0, 3, 0, 0).raw;
332 }
333
334 void
335 Romain::Thread_group::gdt(Romain::App_thread* t, l4_utcb_t *utcb)
336 {
337         enum { replica_gs_base = 0x58 };
338
339         l4_msgtag_t *tag = reinterpret_cast<l4_msgtag_t*>(&t->vcpu()->r()->ax);
340         DEBUG() << BOLD_BLUE << "GDT: words" << NOCOLOR << " = " << tag->words();
341
342         // 1 word -> query GDT start
343         if (tag->words() == 1) {
344                 l4_utcb_mr_u(utcb)->mr[0] = replica_gs_base >> 3;
345                 t->vcpu()->r()->ax = l4_msgtag(0, 1, 0, 0).raw;
346                 enter_kdebug("gdt query");
347         } else { // setup new GDT entry
348                 l4_umword_t idx      = l4_utcb_mr_u(utcb)->mr[1];
349                 l4_umword_t numbytes = (tag->words() == 4) ? 8 : 16;
350
351                 for (l4_umword_t i = 0; i < threads.size(); ++i) {
352                         Romain::App_thread* thread = threads[i];
353
354                         if ((idx == 0) and (numbytes == 8)) { // actually, we only support a single entry here
355                                 thread->write_gdt_entry(&l4_utcb_mr_u(utcb)->mr[2], numbytes);
356                                 DEBUG() << "GS: " << std::hex << thread->vcpu()->r()->gs;
357                         } else {
358                                 enter_kdebug("GDT??");
359                         }
360                 }
361
362                 t->vcpu()->r()->ax = l4_msgtag((idx << 3) + replica_gs_base + 3, 0, 0, 0).raw;
363         }
364 }
365
366
367 void* Romain::GateAgent::listener_function(void *gk)
368 {
369         GateAgent *agent = reinterpret_cast<GateAgent*>(gk);
370         static char* utcb_copy[L4_UTCB_OFFSET];
371
372         char namebuf[16];
373         snprintf(namebuf, 16, "GK::%s", agent->owner_group->name.c_str());
374         l4_debugger_set_object_name(pthread_l4_cap(pthread_self()), namebuf);
375
376         sem_wait(&agent->init_sem);
377         sem_destroy(&agent->init_sem);
378         DEBUG() << "starting agent loop";
379
380         l4_utcb_t *my_utcb = l4_utcb();
381         l4_msgtag_t tag;
382
383         while (1) {
384 #if USE_SHMSYNC
385                 while (agent->current_client == 0) {
386                         l4_thread_yield();
387                 }
388 #endif
389                 
390 #if USE_IRQ
391                 tag = agent->gate_irq->receive();
392 #endif
393
394 #if 0
395                 DEBUG() << "Keeper activated by replica.";
396                 DEBUG() << "agent " << (void*)agent;
397                 DEBUG() << "client " << (void*)agent->current_client;
398                 DEBUG() << " CLNT: " << (void*)agent->current_client << " remote_utcb: "
399                         << (void*)agent->current_client->remote_utcb();
400                 if (agent->current_client == 0) {
401                         DEBUG() << "!!!!!" << std::hex << " " << tag.raw;
402                         DEBUG() << l4sys_errtostr(l4_error(tag));
403                         enter_kdebug();
404                         continue;
405                 }
406                 _check(agent->current_client == 0, "agent called with client NULL?");
407 #endif
408                 
409                 memcpy(utcb_copy, my_utcb, L4_UTCB_OFFSET);
410                 memcpy(my_utcb, (void*)agent->current_client->remote_utcb(), L4_UTCB_OFFSET);
411
412                 //outhex32((l4_umword_t)agent->current_client->vcpu()); outstring(" enter kernel\n");
413                 asm volatile( L4_ENTER_KERNEL
414                           : "=a" (agent->current_client->vcpu()->r()->ax),
415                         "=b" (agent->current_client->vcpu()->r()->bx),
416                         /* ECX, EDX are overwritten anyway */
417                         "=S" (agent->current_client->vcpu()->r()->si),
418                         "=D" (agent->current_client->vcpu()->r()->di)
419                           : "a" (agent->current_client->vcpu()->r()->ax),
420                         /* EBX and EBP will be overwritten with local
421                          * values in L4_ENTER_KERNEL */
422                         "c" (agent->current_client->vcpu()->r()->cx),
423                         "d" (agent->current_client->vcpu()->r()->dx),
424                         "S" (agent->current_client->vcpu()->r()->si),
425                         "D" (agent->current_client->vcpu()->r()->di)
426                           : "memory", "cc"
427                 );
428 #if 0
429                 outhex32((l4_umword_t)agent->current_client->vcpu()); outstring(" ret from kernel ");
430                 outhex32((l4_umword_t)agent->current_client->vcpu()->r()->ax); outstring("\n");
431 #endif
432
433                 memcpy((void*)agent->current_client->remote_utcb(), my_utcb, L4_UTCB_OFFSET);
434                 memcpy(my_utcb, utcb_copy, L4_UTCB_OFFSET);
435
436                 l4_cap_idx_t cap = agent->current_client->vcpu_cap().cap();
437                 agent->current_client = 0;
438                 tag = l4_ipc_send(cap, l4_utcb(), l4_msgtag(0,0,0,0), L4_IPC_NEVER);
439         }
440
441         enter_kdebug("gateagent exited");
442
443         return 0;
444 }