]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/plr/server/src/handler.cc
update
[l4.git] / l4 / pkg / plr / server / src / handler.cc
1 /*
2  * Exception handling
3  *
4  * Here's where the real stuff is going on
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 "manager"
14 #include "log"
15 #include "exceptions"
16 #include "emulation"
17 #include "app_loading"
18 #include "fault_handlers/syscalls_handler.h"
19
20 #include <cassert>
21
22 #include <l4/sys/kdebug.h>
23 #include <l4/util/bitops.h>
24 #include <l4/util/rdtsc.h>
25
26 #include <pthread-l4.h>
27 #include <l4/sys/segment.h>
28
29 #define MSG() DEBUGf(Romain::Log::Faults)
30 #define MSGi(inst) MSG() << "[" << (inst)->id() << "] "
31 #define MSGit(inst,tg) MSG() << "[" << (inst)->id() << "] \033[34;1m{" << tg->name << "}\033[0m "
32
33 #define ____dummy " /* ST2 highlighting fix XXX */
34
35 EXTERN_C void *pthread_fn(void *data);
36 EXTERN_C void *pthread_fn(void *data)
37 {
38         Romain::App_thread *t = (Romain::App_thread*)data;
39
40         l4_cap_idx_t cap = (l4_cap_idx_t)pthread_getl4cap(pthread_self());
41         t->vcpu_cap(L4::Cap<L4::Thread>(cap));
42
43         DEBUG() << "vcpu @ " << (void*)t->vcpu();
44         DEBUG() << "thread entry: " << (void*)t->thread_entry();
45
46         /*
47          * Thread creation, copied from the example again.
48          */
49         L4::Thread::Attr attr;
50         attr.pager(L4::cap_reinterpret_cast<L4::Thread>(L4Re::Env::env()->rm()));
51         attr.exc_handler(L4Re::Env::env()->main_thread());
52 //      attr.bind(t->vcpu_utcb(), L4Re::This_task);
53
54         chksys(t->vcpu_cap()->control(attr), "control");
55         chksys(t->vcpu_cap()->vcpu_control((l4_addr_t)t->vcpu()), "enable VCPU");
56
57         l4_sched_param_t sp = l4_sched_param(2);
58         sp.affinity = l4_sched_cpu_set(t->cpu(), 0);
59         chksys(L4Re::Env::env()->scheduler()->run_thread(t->vcpu_cap(),
60                                                          sp));
61
62 #if 0
63         MSG() << "!" << std::hex << (void*)t->thread_sp();
64         MSG() << "?" << std::hex << (void*)t->handler_sp();
65 #endif
66         asm volatile("mov %0, %%esp\n\t"
67                                  "jmp *%1\n\t"
68                                  :
69                                  : "r" (t->handler_sp()),
70                                    "r" (t->thread_entry())
71                                  );
72
73         enter_kdebug("blub?");
74
75         return 0;
76 }
77
78 #if SPLIT_HANDLING
79
80 struct SplitInfo {
81         Romain::InstanceManager *m;
82         Romain::App_instance    *i;
83         Romain::App_thread      *t;
84         Romain::Thread_group    *tg;
85         Romain::App_model       *a;
86         l4_cap_idx_t             cap;
87
88         SplitInfo()
89                 : m(0), i(0), t(0), tg(0), a(0), cap(L4_INVALID_CAP)
90         { }
91
92 };
93
94 #define SYNC_IPC 1
95
96 class SplitHandler
97 {
98         l4_cap_idx_t             _split_handler;
99         Romain::InstanceManager *_im;
100         Romain::Replicator       _replicator;
101         SplitInfo **             _psi;
102         unsigned long *          _checksums;
103
104         void wait_for_instances()
105         {
106                 for (unsigned cnt = 0; cnt < _im->instance_count(); ++cnt) {
107 #if SYNC_IPC
108                         l4_umword_t label  = 0;
109                         l4_msgtag_t t      = l4_ipc_wait(l4_utcb(), &label, L4_IPC_NEVER);
110                         //MSG() << "Split handler notified: " << std::hex << t.label();
111                         _psi[cnt]          = (SplitInfo*)l4_utcb_mr()->mr[0];
112 #else
113                         MSG() << (void*)&_psi[cnt] << " " << _psi[cnt];
114
115                         while (_psi[cnt] == 0) {
116                                 //MSG() << (void*)_psi[cnt];
117                                 l4_thread_yield();
118                         }
119
120                         MSG() << (void*)&_psi[cnt] << " " << _psi[cnt] << "  Split handler notified";
121 #endif
122                         _checksums[cnt]    = _psi[cnt]->t->csum_state();
123                 }
124
125         }
126
127
128         bool validate_instances()
129         {
130                 for (unsigned cnt = 1; cnt < _im->instance_count(); ++cnt) {
131                         if (_checksums[cnt] != _checksums[cnt-1]) {
132                                 ERROR() << std::hex << _checksums[cnt] << " != " << _checksums[cnt-1];
133                                 ERROR() << "State mismatch detected!";
134                                 ERROR() << "=== vCPU states ===";
135                                 
136                                 for (unsigned i = 0; i < _im->instance_count(); ++i) {
137                                         ERROR() << "Instance " << _psi[i]->i->id() << " "
138                                                 << "csum " << std::hex << _psi[i]->t->csum_state();
139                                         _psi[i]->t->print_vcpu_state();
140                                 }
141
142                                 return false;
143                         }
144                 }
145                 return true;
146         }
147
148         void handle_fault()
149         {
150                 L4vcpu::Vcpu *vcpu = _psi[0]->t->vcpu();
151                 unsigned trap      = _psi[0]->t->vcpu()->r()->trapno;
152
153                 while (trap) {
154                         MSGi(_psi[0]->i) << "\033[33;1mTRAP 0x"
155                                          << std::hex << vcpu->r()->trapno
156                                          << " @ 0x" << vcpu->r()->ip << "\033[0m";
157
158                         Romain::Observer::ObserverReturnVal v 
159                                 = _im->fault_notify(_psi[0]->i, _psi[0]->t, _psi[0]->tg, _psi[0]->a);
160
161                         switch(v) {
162                                 case Romain::Observer::Finished:
163                                         {
164                                                 for (unsigned c = 1; c < _im->instance_count(); ++c) {
165                                                         _im->fault_notify(_psi[c]->i, _psi[c]->t, _psi[c]->tg, _psi[c]->a);
166                                                 }
167                                         }
168                                         break;
169                                 case Romain::Observer::Replicatable:
170                                         {
171                                                 _replicator.put(_psi[0]->t);
172                                                 for (unsigned c = 1; c < _im->instance_count(); ++c) {
173                                                         _replicator.get(_psi[c]->t);
174                                                 }
175                                         }
176                                         break;
177                                 default:
178                                         enter_kdebug("notify?");
179                                         break;
180                         }
181
182                         if ((trap = _psi[0]->t->get_pending_trap()) != 0)
183                                 _psi[0]->t->vcpu()->r()->trapno = trap;
184                 }
185         }
186
187
188         void resume_instances()
189         {
190                 for (unsigned c = 0; c < _im->instance_count(); ++c) {
191                         MSGi(_psi[c]->i) << "Resuming instance @ " << std::hex << _psi[c]->t->vcpu()->r()->ip;
192 #if SYNC_IPC
193                         l4_ipc_send(_psi[c]->cap, l4_utcb(), l4_msgtag(0,0,0,0), L4_IPC_NEVER);
194 #else
195                         _psi[c]    = 0;
196 #endif
197                 }
198                 MSG() << "... resumed.";
199         }
200
201         public:
202
203                 static SplitHandler*     _handlers[5];
204
205                 static SplitHandler* get(unsigned idx)
206                 {
207                         assert(idx == 0); // for now
208                         return _handlers[idx];
209                 }
210
211                 void notify(Romain::App_instance* i,
212                             Romain::App_thread* t,
213                                         Romain::Thread_group* tg,
214                             Romain::App_model* a)
215                 {
216 #if SYNC_IPC
217                         SplitInfo si;
218
219                         //MSGi(i) << "split handler is " << std::hex << SplitHandler::split_handler_cap();
220
221                         //si.m   = _im;
222                         si.i   = i;
223                         si.t   = t;
224                         si.tg  = tg;
225                         si.a   = a;
226                         si.cap = t->vcpu_cap().cap();
227
228                         l4_utcb_mr()->mr[0] = (l4_umword_t)&si;
229                         l4_msgtag_t tag = l4_msgtag(0xF00, 1, 0, 0);
230                         l4_msgtag_t res = l4_ipc_call(SplitHandler::split_handler_cap(),
231                                                       l4_utcb(), tag, L4_IPC_NEVER);
232 #else
233                         SplitInfo si;
234                         si.i = i;
235                         si.t = t;
236                         si.tg = tg;
237                         si.a = a;
238                         psi(i->id(), &si);
239                         MSG() << (void*)&_psi[i->id()] << " " << (void*)&si;
240
241                         while (_psi[i->id()] != 0) {
242                                 l4_thread_yield();
243                         }
244 #endif
245                         MSGi(i) << "handled.";
246                 }
247
248                 SplitHandler(Romain::InstanceManager* im)
249                         : _split_handler(pthread_getl4cap(pthread_self())),
250                       _im(im), _replicator()
251                 {
252                         _psi       = new SplitInfo*[_im->instance_count()];
253                         _checksums = new unsigned long [_im->instance_count()];
254                         memset(_psi, 0, sizeof(SplitInfo*) * _im->instance_count());
255                 }
256
257
258                 ~SplitHandler()
259                 {
260                         delete [] _psi;
261                         delete [] _checksums;
262                 }
263
264
265                 void run()
266                 {
267                         MSG() << "Instance mgr: " << (void*)_im;
268                         MSG() << "Instances: " << _im->instance_count();
269
270                         while (true) {
271                                 wait_for_instances();
272
273                         DEBUG() << "received faults from " << _im->instance_count()
274                                 << " instances...";
275
276                                 if (!validate_instances())
277                                         enter_kdebug("recover");
278
279                                 handle_fault();
280
281                                 resume_instances();
282                         }
283                 }
284
285                 void psi(unsigned idx, SplitInfo* si)
286                 { _psi[idx] = si; }
287
288                 l4_cap_idx_t split_handler_cap()
289                 { return _split_handler; }
290 };
291
292
293 SplitHandler* SplitHandler::_handlers[5] = {0};
294
295
296 EXTERN_C void *split_handler_fn(void* data)
297 {
298         //SplitHandler::split_handler_cap(pthread_getl4cap(pthread_self()));
299
300         SplitHandler::_handlers[0] = new SplitHandler(reinterpret_cast<Romain::InstanceManager*>(data));
301         SplitHandler::get(0)->run();
302
303         enter_kdebug("split_handler terminated!");
304         return 0;
305 }
306
307 #endif // SPLIT_HANDLING
308
309 void __attribute__((noreturn)) Romain::InstanceManager::VCPU_startup(Romain::InstanceManager *m,
310                                                                      Romain::App_instance *i,
311                                                                      Romain::App_thread *t,
312                                                                      Romain::Thread_group *tg,
313                                                                      Romain::App_model*am)
314 {
315         L4vcpu::Vcpu *vcpu = t->vcpu();
316         vcpu->task(i->vcpu_task());
317
318         tg->ready();
319         
320         t->commit_client_gdt();
321         //t->print_vcpu_state();
322
323         char namebuf[16];
324         snprintf(namebuf, 16, "%s.%d", tg->name.c_str(), i->id());
325         l4_debugger_set_object_name(t->vcpu_cap().cap(), namebuf);
326         DEBUG() << std::hex << (unsigned)t->vcpu_cap().cap() << " = "
327                 << l4_debugger_global_id(t->vcpu_cap().cap())
328                 << " ->" << namebuf;
329
330         struct timeval tv;
331         gettimeofday(&tv, 0);
332         if (Romain::Log::withtime)
333                 INFO() << "\033[33;1mStarting @ " << tv.tv_sec << "." << tv.tv_usec << "\033[0m";
334         else
335                 INFO() << "\033[33;1mStarting\033[0m";
336
337         MSGit(i,tg) << "Resuming instance @ " << (void*)vcpu->r()->ip << " ...";
338
339         Measurements::GenericEvent* ev = m->logbuf()->next();
340         ev->header.tsc                 = Romain::_the_instance_manager->logbuf()->getTime(Log::logLocalTSC);
341         ev->header.vcpu                = (l4_uint32_t)vcpu;
342         ev->header.type                = Measurements::Thread_start;
343
344         L4::Cap<L4::Thread> cap = t->vcpu_cap();
345
346         cap->vcpu_resume_commit(cap->vcpu_resume_start());
347
348         enter_kdebug("after resume");
349         l4_sleep_forever();
350 }
351
352
353 static void local_vCPU_handling(Romain::InstanceManager *m,
354                                 Romain::App_instance *i,
355                                 Romain::App_thread *t,
356                                 Romain::Thread_group *tg,
357                                 Romain::App_model *a)
358 {
359         // XXX: At this point we might want to reset the GDT to the view that is
360         //      expected by the master task, which might differ from the client.
361
362         L4vcpu::Vcpu *vcpu = t->vcpu();
363         unsigned trap = t->vcpu()->r()->trapno;
364
365         /*
366          * We potentially handle multiple traps here: As we are emulating a bunch
367          * of instructions (e.g., write PF emulation), some real instructions are never
368          * actually executed and may thus not raise certain exceptions such as the INT1 single
369          * step exception. Observers emulating behavior need to be aware of that and inject
370          * a new exception in such circumstances.
371          */
372         while (trap) {
373                 MSGit(i,tg) << "\033[33;1mTRAP 0x" << std::hex << vcpu->r()->trapno
374                         << " @ 0x" << vcpu->r()->ip << "\033[0m";
375
376                 if (t->vcpu()->r()->ip == 0xA041) {
377                         m->fault_notify(i,t,tg,a);
378                         break;
379                 }
380
381 #if 0
382                 /*
383                  * HACK: In case we are using lock-internal determinism, there's a special
384                  *       entry address in which a single replica will signal us if we need
385                  *       to wake up another thread waiting for a replica. If we encounter
386                  *       this address, we skip redundancy handling and directly notify the
387                  *       fault handler(s).
388                  */
389                 if (vcpu->r()->ip == 0xA041) {
390                         // shortcut for unlock()
391                         m->fault_notify(i,t,tg,a);
392                         break;
393                 }
394 #endif
395
396                 Romain::Observer::ObserverReturnVal v         = Romain::Observer::Invalid;
397
398                 /*
399                  * Enter redundancy mode. May cause vCPU to block until leader vCPU executed
400                  * its handlers.
401                  */
402                 Romain::RedundancyCallback::EnterReturnVal rv = tg->redundancyCB->enter(i,t,a);
403                 //MSGi(i) << "red::enter: " << rv;
404
405                 /*
406                  * Case 1: we are the first to exec this system call.
407                  */
408                 if (rv == Romain::RedundancyCallback::First_syscall) {
409                         v = m->fault_notify(i,t,tg,a);
410                         MSGit(i,tg) << "fault_notify: " << v;
411                         switch(v) {
412                                 case Romain::Observer::Finished:
413                                 case Romain::Observer::Finished_wait:
414                                 case Romain::Observer::Finished_step:
415                                 case Romain::Observer::Finished_wakeup:
416                                         //MSGi(i) << "leader_repeat()";
417                                         tg->redundancyCB->leader_repeat(i,t,a);
418                                         break;
419                                 case Romain::Observer::Replicatable:
420                                         tg->redundancyCB->leader_replicate(i,t,a);
421                                         break;
422                                 default:
423                                         //enter_kdebug("fault was not finished/replicatable");
424                                         break;
425                         }
426                 }
427                 /*
428                  * Case 2: leader told us to do the syscall ourselves
429                  */
430                 else if (rv == Romain::RedundancyCallback::Repeat_syscall) {
431                         v = m->fault_notify(i,t,tg,a);
432                         MSGit(i,tg) << "fault_notify: " << v;
433                 }
434
435                 /*
436                  * Special handling for the Finished_{wakeup,wait,step} cases
437                  * used by the fault injector.
438                  */
439                 switch(v) {
440                         case Romain::Observer::Finished_wait:
441                                 // go to sleep until woken up
442                                 tg->redundancyCB->wait(i,t,a);
443                                 break;
444                         case Romain::Observer::Finished_wakeup:
445                                 // wakeup -> first needs to ensure that all other
446                                 // vCPUs are actually waiting
447                                 tg->redundancyCB->silence(i,t,a);
448                                 tg->redundancyCB->wakeup(i,t,a);
449                                 break;
450                         case Romain::Observer::Finished_step:
451                                 // let other vCPUs step until they go to sleep
452                                 tg->redundancyCB->silence(i,t,a);
453                                 break;
454                         default:
455                                 break;
456                 }
457
458                 if ((trap = t->get_pending_trap()) != 0)
459                         t->vcpu()->r()->trapno = trap;
460
461                 tg->redundancyCB->resume(i, t, a);
462         }
463
464         //print_vcpu_state();
465     MSGit(i, tg) << "Resuming instance @ " << std::hex << t->vcpu()->r()->ip;
466         //t->print_vcpu_state();
467 }
468
469
470 #if SPLIT_HANDLING
471 static void split_vCPU_handling(Romain::InstanceManager *m,
472                                 Romain::App_instance *i,
473                                 Romain::App_thread *t,
474                                 Romain::Thread_group *tg,
475                                 Romain::App_model *a)
476 {
477         MSG() << "here";
478         SplitHandler::get(0)->notify(i,t,a);
479 }
480 #endif // SPLIT_HANDLING
481
482
483 #if MIGRATE_VCPU
484 static void migrated_vCPU_handling(Romain::InstanceManager *m,
485                                    Romain::App_instance *i,
486                                    Romain::App_thread *t,
487                                    Romain::Thread_group *tg,
488                                    Romain::App_model *a)
489 {
490         l4_sched_param_t sp = l4_sched_param(2);
491         sp.affinity = l4_sched_cpu_set(0, 0);
492         chksys(L4Re::Env::env()->scheduler()->run_thread(t->vcpu_cap(),
493                                                          sp));
494
495         local_vCPU_handling(m, i, t, a);
496
497         sp = l4_sched_param(2);
498         sp.affinity = l4_sched_cpu_set(t->cpu(), 0);
499         chksys(L4Re::Env::env()->scheduler()->run_thread(t->vcpu_cap(),
500                                                          sp));
501 }
502 #endif // MIGRATE_VCPU
503
504 /*
505  * VCPU fault entry point.
506  *
507  * Calls the registered observers and keeps track of any further faults that might get
508  * injected during handler execution.
509  */
510 void __attribute__((noreturn)) Romain::InstanceManager::VCPU_handler(Romain::InstanceManager *m,
511                                                                      Romain::App_instance *i,
512                                                                      Romain::App_thread *t,
513                                                                      Romain::Thread_group *tg,
514                                                                      Romain::App_model *a)
515 {
516         L4vcpu::Vcpu *vcpu = t->vcpu();
517         vcpu->state()->clear(L4_VCPU_F_EXCEPTIONS | L4_VCPU_F_DEBUG_EXC);
518         handler_prolog(t);
519
520         Measurements::GenericEvent* ev = m->logbuf()->next();
521         ev->header.tsc     = Romain::_the_instance_manager->logbuf()->getTime(Log::logLocalTSC);
522         ev->header.vcpu        = (l4_uint32_t)vcpu;
523         ev->header.type        = Measurements::Trap;
524         ev->data.trap.start    = 1;
525         ev->data.trap.trapaddr = vcpu->r()->ip;
526         ev->data.trap.trapno   = vcpu->r()->trapno;
527
528 #if MIGRATE_VCPU
529         migrated_vCPU_handling(m, i, t, tg, a);
530 #elif SPLIT_HANDLING
531         split_vCPU_handling(m, i, t, tg, a);
532 #elif LOCAL_HANDLING
533         local_vCPU_handling(m, i, t, tg, a);
534 #else
535 #error No vCPU handling method selected!
536 #endif
537
538         ev = m->logbuf()->next();
539         ev->header.tsc     = Romain::_the_instance_manager->logbuf()->getTime(Log::logLocalTSC);
540         ev->header.vcpu      = (l4_uint32_t)vcpu;
541         ev->header.type      = Measurements::Trap;
542         ev->data.trap.start  = 0;
543         ev->data.trap.trapno = ~0U;
544
545         L4::Cap<L4::Thread> self;
546         self->vcpu_resume_commit(self->vcpu_resume_start());
547
548         enter_kdebug("after resume");
549         l4_sleep_forever();
550 }