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