4 * Here's where the real stuff is going on
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.
17 #include "app_loading"
18 #include "fault_handlers/syscalls_handler.h"
22 #include <l4/sys/kdebug.h>
23 #include <l4/util/bitops.h>
24 #include <l4/util/rdtsc.h>
26 #include <pthread-l4.h>
27 #include <l4/sys/segment.h>
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
33 EXTERN_C void *pthread_fn(void *data);
34 EXTERN_C void *pthread_fn(void *data)
36 Romain::App_thread *t = (Romain::App_thread*)data;
38 t->vcpu_cap(Pthread::L4::cap(pthread_self()));
40 DEBUG() << "vcpu @ " << (void*)t->vcpu();
41 DEBUG() << "thread entry: " << (void*)t->thread_entry();
44 * Thread creation, copied from the example again.
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);
51 chksys(t->vcpu_cap()->control(attr), "control");
52 chksys(t->vcpu_cap()->vcpu_control((l4_addr_t)t->vcpu()), "enable VCPU");
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(),
60 MSG() << "!" << std::hex << (void*)t->thread_sp();
61 MSG() << "?" << std::hex << (void*)t->handler_sp();
63 asm volatile("mov %0, %%esp\n\t"
66 : "r" (t->handler_sp()),
67 "r" (t->thread_entry())
70 enter_kdebug("blub?");
78 Romain::InstanceManager *m;
79 Romain::App_instance *i;
80 Romain::App_thread *t;
81 Romain::Thread_group *tg;
86 : m(0), i(0), t(0), tg(0), a(0), cap(L4_INVALID_CAP)
95 l4_cap_idx_t _split_handler;
96 Romain::InstanceManager *_im;
97 Romain::Replicator _replicator;
99 l4_umword_t* _checksums;
101 void wait_for_instances()
103 for (l4_umword_t cnt = 0; cnt < _im->instance_count(); ++cnt) {
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];
110 MSG() << (void*)&_psi[cnt] << " " << _psi[cnt];
112 while (_psi[cnt] == 0) {
113 //MSG() << (void*)_psi[cnt];
117 MSG() << (void*)&_psi[cnt] << " " << _psi[cnt] << " Split handler notified";
119 _checksums[cnt] = _psi[cnt]->t->csum_state();
125 bool validate_instances()
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";
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();
147 L4vcpu::Vcpu *vcpu = _psi[0]->t->vcpu();
148 l4_umword_t trap = _psi[0]->t->vcpu()->r()->trapno;
151 MSGi(_psi[0]->i) << BOLD_YELLOW << "TRAP 0x"
152 << std::hex << vcpu->r()->trapno
153 << " @ 0x" << vcpu->r()->ip << NOCOLOR;
155 Romain::Observer::ObserverReturnVal v
156 = _im->fault_notify(_psi[0]->i, _psi[0]->t, _psi[0]->tg, _psi[0]->a);
159 case Romain::Observer::Finished:
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);
166 case Romain::Observer::Replicatable:
168 _replicator.put(_psi[0]->t);
169 for (l4_umword_t c = 1; c < _im->instance_count(); ++c) {
170 _replicator.get(_psi[c]->t);
175 enter_kdebug("notify?");
179 if ((trap = _psi[0]->t->get_pending_trap()) != 0)
180 _psi[0]->t->vcpu()->r()->trapno = trap;
185 void resume_instances()
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;
190 l4_ipc_send(_psi[c]->cap, l4_utcb(), l4_msgtag(0,0,0,0), L4_IPC_NEVER);
195 MSG() << "... resumed.";
200 static SplitHandler* _handlers[5];
202 static SplitHandler* get(l4_umword_t idx)
204 assert(idx == 0); // for now
205 return _handlers[idx];
208 void notify(Romain::App_instance* i,
209 Romain::App_thread* t,
210 Romain::Thread_group* tg,
211 Romain::App_model* a)
216 //MSGi(i) << "split handler is " << std::hex << SplitHandler::split_handler_cap();
223 si.cap = t->vcpu_cap().cap();
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);
236 MSG() << (void*)&_psi[i->id()] << " " << (void*)&si;
238 while (_psi[i->id()] != 0) {
242 MSGi(i) << "handled.";
245 SplitHandler(Romain::InstanceManager* im)
246 : _split_handler(pthread_l4_cap(pthread_self())),
247 _im(im), _replicator()
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());
258 delete [] _checksums;
264 MSG() << "Instance mgr: " << (void*)_im;
265 MSG() << "Instances: " << _im->instance_count();
268 wait_for_instances();
270 DEBUG() << "received faults from " << _im->instance_count()
273 if (!validate_instances())
274 enter_kdebug("recover");
282 void psi(l4_umword_t idx, SplitInfo* si)
285 l4_cap_idx_t split_handler_cap()
286 { return _split_handler; }
290 SplitHandler* SplitHandler::_handlers[5] = {0};
293 EXTERN_C void *split_handler_fn(void* data)
295 //SplitHandler::split_handler_cap(pthread_l4_cap(pthread_self()));
297 SplitHandler::_handlers[0] = new SplitHandler(reinterpret_cast<Romain::InstanceManager*>(data));
298 SplitHandler::get(0)->run();
300 enter_kdebug("split_handler terminated!");
304 #endif // SPLIT_HANDLING
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)
312 L4vcpu::Vcpu *vcpu = t->vcpu();
313 vcpu->task(i->vcpu_task());
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())
324 if (t->gdt_changed()) {
325 t->commit_client_gdt();
329 gettimeofday(&tv, 0);
330 if (Romain::Log::withtime)
331 INFO() << BOLD_YELLOW << "Starting @ " << tv.tv_sec << "." << tv.tv_usec << NOCOLOR;
333 INFO() << BOLD_YELLOW << "Starting" << NOCOLOR;
335 MSGit(i,tg) << "Resuming instance @ " << (void*)vcpu->r()->ip << " ...";
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;
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();
351 INFO() << "Failed to enable watchdog!";
355 t->ts_user_resume(true);
357 L4::Cap<L4::Thread> cap = t->vcpu_cap();
358 cap->vcpu_resume_commit(cap->vcpu_resume_start());
359 enter_kdebug("startup: after resume");
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)
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.
373 L4vcpu::Vcpu *vcpu = t->vcpu();
374 l4_umword_t trap = t->vcpu()->r()->trapno;
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.
390 INFO() << "first " << i->id();
392 MSGit(i,tg) << BOLD_YELLOW << "TRAP 0x" << std::hex << vcpu->r()->trapno
393 << " @ 0x" << vcpu->r()->ip << NOCOLOR;
395 if (t->vcpu()->r()->ip == 0xA041) {
396 m->fault_notify(i,t,tg,a);
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);
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
418 if (vcpu->r()->ip == 0xA041) {
419 // shortcut for unlock()
420 m->fault_notify(i,t,tg,a);
425 Romain::Observer::ObserverReturnVal v = Romain::Observer::Invalid;
426 Romain::RedundancyCallback::EnterReturnVal rv;
428 * Enter redundancy mode. May cause vCPU to block until leader vCPU executed
432 if (!tg->watchdog->enabled()) {
433 rv = tg->redundancyCB->enter(i,t,tg,a);
435 if (wrv != Romain::RedundancyCallback::Watchdog) {
436 rv = tg->redundancyCB->enter(i,t,tg,a);
437 //MSGi(i) << "red::enter: " << rv;
443 rv = tg->redundancyCB->enter(i,t,tg,a);
449 * Case 1: we are the first to exec this system call.
451 if (rv == Romain::RedundancyCallback::First_syscall) {
452 v = m->fault_notify(i,t,tg,a);
453 MSGit(i,tg) << "fault_notify: " << 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);
462 case Romain::Observer::Replicatable:
463 tg->redundancyCB->leader_replicate(i,t,a);
466 //enter_kdebug("fault was not finished/replicatable");
471 * Case 2: leader told us to do the syscall ourselves
473 else if (rv == Romain::RedundancyCallback::Repeat_syscall) {
474 v = m->fault_notify(i,t,tg,a);
475 MSGit(i,tg) << "fault_notify: " << v;
479 * Special handling for the Finished_{wakeup,wait,step} cases
480 * used by the fault injector.
483 case Romain::Observer::Finished_wait:
484 // go to sleep until woken up
485 tg->redundancyCB->wait(i,t,a);
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);
493 case Romain::Observer::Finished_step:
494 // let other vCPUs step until they go to sleep
495 tg->redundancyCB->silence(i,t,a);
501 if ((trap = t->get_pending_trap()) != 0)
502 t->vcpu()->r()->trapno = trap;
504 t->ts_resume_start();
505 tg->redundancyCB->resume(i, t, tg, a);
508 //print_vcpu_state();
509 MSGit(i, tg) << "Resuming instance @ " << std::hex << t->vcpu()->r()->ip;
510 //t->print_vcpu_state();
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)
522 SplitHandler::get(0)->notify(i,t,a);
524 #endif // SPLIT_HANDLING
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)
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(),
539 local_vCPU_handling(m, i, t, a);
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(),
546 #endif // MIGRATE_VCPU
549 * VCPU fault entry point.
551 * Calls the registered observers and keeps track of any further faults that might get
552 * injected during handler execution.
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)
560 L4vcpu::Vcpu *vcpu = t->vcpu();
561 vcpu->state()->clear(L4_VCPU_F_EXCEPTIONS | L4_VCPU_F_DEBUG_EXC);
563 unsigned long long t1, t2;
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;
576 unsigned long long t1, t2;
582 migrated_vCPU_handling(m, i, t, tg, a);
584 split_vCPU_handling(m, i, t, tg, a);
586 local_vCPU_handling(m, i, t, tg, a);
588 #error No vCPU handling method selected!
592 if (tg->watchdog->enabled())
593 tg->watchdog->reset(i, t, t->watchdog_timeout());
599 t->count_handling(t2-t1);
602 if (t->gdt_changed()) {
603 t->commit_client_gdt();
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;
616 L4::Cap<L4::Thread> self;
617 self->vcpu_resume_commit(self->vcpu_resume_start());
619 enter_kdebug("notify: after resume");