]> rtime.felk.cvut.cz Git - l4.git/blobdiff - l4/pkg/plr/server/src/app_thread.cc
update
[l4.git] / l4 / pkg / plr / server / src / app_thread.cc
index deb768410e659f8af69f9a7cfd5e1813fa537094..6ca4c63b6c0701d3c6db5c4a82c6664bee0941f0 100644 (file)
@@ -3,7 +3,7 @@
  *
  *     App_thread functions for creating and preparing a new VCPU
  *
- * (c) 2011 Björn Döbel <doebel@os.inf.tu-dresden.de>,
+ * (c) 2011-2013 Björn Döbel <doebel@os.inf.tu-dresden.de>,
  *     economic rights: Technische Universität Dresden (Germany)
  * This file is part of TUD:OS and distributed under the terms of the
  * GNU General Public License 2.
@@ -12,7 +12,9 @@
 
 #include "app"
 #include "app_loading"
+#include "thread_group.h"
 
+#include <l4/libloader/remote_app_model>
 #include <pthread-l4.h>
 
 void Romain::App_thread::alloc_vcpu_mem()
@@ -26,11 +28,11 @@ void Romain::App_thread::alloc_vcpu_mem()
        _vcpu = L4vcpu::Vcpu::cast(kumem + L4_UTCB_OFFSET);
 
        /* store segment registers - stolen from the example */
-       //_vcpu->r()->gs = _stored_ds;
-       _vcpu->r()->fs = _stored_ds;
-       _vcpu->r()->es = _stored_ds;
-       _vcpu->r()->ds = _stored_ds;
-       _vcpu->r()->ss = _stored_ds;
+       //_vcpu->r()->gs = _master_ds;
+       _vcpu->r()->fs = _master_ds & 0xFF;
+       _vcpu->r()->es = _master_ds & 0xFF;
+       _vcpu->r()->ds = _master_ds & 0xFF;
+       _vcpu->r()->ss = _master_ds & 0xFF;
 
        /* We want to catch ALL exceptions for this vCPU. */
        _vcpu->saved_state()->set(
@@ -87,10 +89,10 @@ void Romain::App_thread::start()
        _vcpu->entry_sp(handler_sp());
        _vcpu->entry_ip((l4_umword_t)_handler_fn);
 
-       int err = pthread_create(&_pthread, NULL, pthread_fn, this);
+       l4_mword_t err = pthread_create(&_pthread, NULL, pthread_fn, this);
        _check(err != 0, "pthread_create");
 
-       _vcpu_cap = L4::Cap<L4::Thread>(pthread_getl4cap(_pthread));
+       //_vcpu_cap = L4::Cap<L4::Thread>(pthread_getl4cap(_pthread));
 }
 
 
@@ -99,7 +101,7 @@ void Romain::App_thread::start()
  *
  * This checksum is used to compare replica states.
  */
-unsigned long
+l4_umword_t
 Romain::App_thread::csum_state()
 {
        // XXX: this should also include the UTCB, which
@@ -111,9 +113,332 @@ Romain::App_thread::csum_state()
             + _vcpu->r()->cx
             + _vcpu->r()->dx
             + _vcpu->r()->bp
+            /*+ _vcpu->r()->fs
+            + _vcpu->r()->gs*/
+            ;
+}
+
+
+void Romain::App_thread::commit_client_gdt()
+{
+       vcpu()->r()->fs = fiasco_gdt_set(vcpu_cap().cap(), gdt(), gdt_size(), 1, l4_utcb());
+
+       if (_client_gdt[1].base_low != 0)
+               vcpu()->r()->gs = vcpu()->r()->fs + 8;
+
+       DEBUG() << "CPU " << cpu() << " FS " << std::hex << vcpu()->r()->fs;
+       DEBUG() << "CPU " << cpu() << " GS " << std::hex << vcpu()->r()->gs;
+       //vcpu()->print_state();
+       _gdt_modified = false;
+}
+
+
+void
+Romain::Thread_group::ex_regs(Romain::App_thread *caller)
+{
+       /*
+        * Thoughts on ex_regs
+        *
+        * One use case of ex_regs is for an external thread to halt execution
+        * of a thread, store the halted thread's state and later continue from
+        * this point. As replicated threads execute independently, we will not
+        * stop all replicas in the same state. However, we require the replicas
+        * to be in the same state on resumption.
+        *
+        * To make things deterministic, we stop all replicas. As a heuristic, we
+        * then assume that the replicas are all correct (as all state
+        * difference can be attributed to diverging execution). We select the last
+        * stopped replica (potentially being the most advanced) as good copy and
+        * copy its state over to all other replicas, thereby forcing them to the
+        * same state.
+        *
+        * 1) This may shadow certain errors that occurred prior to the ex_regs.
+        *    -> we need to evaluate how often that happens
+        *
+        * 2) HP NonStop instead executes replicas in lock-step until it can
+        *    figure out which is the most advanced. THen they execute everyone
+        *    else up to this point.
+        *    -> higher overhead
+        *    -> no lack in error detection
+        *
+        * 3) Can we detect situations where this happens as hard overwrite of
+        *    the replicated thread's state -> hence we would not need to first
+        *    enforce identical states? -> heuristics only, cannot determine
+        *    upfront if the caller will later reuse the state.
+        *
+        */
+       bool need_to_stop = !this->stopped;
+
+       if (need_to_stop) {
+               enter_kdebug("stop thread magic goes here");
+       }
+
+       l4_msg_regs_t *buf = l4_utcb_mr_u(reinterpret_cast<l4_utcb_t*>(caller->remote_utcb()));
+       DEBUG() << std::hex << buf->mr[0];
+       DEBUG() << std::hex << buf->mr[1];
+       DEBUG() << std::hex << buf->mr[2];
+
+       Romain::App_thread* thread = threads[0];
+       l4_umword_t eip = thread->vcpu()->r()->ip;
+       l4_umword_t esp = thread->vcpu()->r()->sp;
+
+       DEBUG() << (void*)thread->vcpu();
+       for (l4_umword_t i = 0; i < threads.size(); ++i) {
+               threads[i]->vcpu()->r()->ip = buf->mr[1];
+               threads[i]->vcpu()->r()->sp = buf->mr[2];
+       }
+
+       buf->mr[1] = eip;
+       buf->mr[2] = esp;
+
+       caller->vcpu()->r()->ax = l4_msgtag(0,3,0,0).raw;
+
+       if (need_to_stop) {
+               /*
+                * If we stopped the threads before,
+                * we need to reactivate them now.
+                */
+               enter_kdebug("reactivation magic goes here");
+       }
+       //enter_kdebug("Thread_group::ex_regs");
+}
+
+
+void
+Romain::Thread_group::scheduler_run(Romain::App_thread *caller)
+{
+       l4_msg_regs_t *buf = l4_utcb_mr_u(reinterpret_cast<l4_utcb_t*>(caller->remote_utcb()));
+       
+       DEBUG() << "Thread_group::scheduler_run("
+               << (void*)threads[0]->vcpu()
+               << ")";
+       DEBUG() << "granularity | affinity.offs : " << std::hex << buf->mr[1];
+       DEBUG() << "affinity.map                : " << std::hex << buf->mr[2];
+       DEBUG() << "prio                        : " << std::hex << buf->mr[3];
+       DEBUG() << "quantum                     : " << std::hex << buf->mr[4];
+       DEBUG() << "obj_control                 : " << std::hex << buf->mr[5];
+       DEBUG() << "thread                      : " << std::hex << buf->mr[6];
+
+       activate();
+
+       //enter_kdebug("Thread_group::schedule()");
+}
+
+
+void
+Romain::Thread_group::sanity_check_control(l4_umword_t flags, l4_utcb_t *utcb)
+{
+       DEBUG() << "Control flags: " << std::hex << l4_utcb_mr_u(utcb)->mr[L4_THREAD_CONTROL_MR_IDX_FLAGS];
+
+       if ((flags & L4_THREAD_CONTROL_ALIEN) ||
+               (flags & L4_THREAD_CONTROL_UX_NATIVE)) {
+               ERROR() << "ux_native and alien not supported yet\n";
+       }
+
+       if (flags & L4_THREAD_CONTROL_BIND_TASK) {
+               l4_fpage_t fp;
+               fp.raw = l4_utcb_mr_u(utcb)->mr[L4_THREAD_CONTROL_MR_IDX_BIND_TASK + 1];
+               DEBUG() << std::hex << "L4_THREAD_CONTROL_BIND_TASK := ("
+                       << l4_utcb_mr_u(utcb)->mr[L4_THREAD_CONTROL_MR_IDX_BIND_UTCB] << ", "
+                       << l4_fpage_page(fp) << ")";
+               if (l4_fpage_page(fp) != (L4Re::This_task >> L4_CAP_SHIFT)) {
+                       ERROR() << "Binding to different task not supported yet.\n";
+                       enter_kdebug("error");
+               }
+               /* Apart from these checks, don't do anything. The replica vCPUs
+                * are already bound.
+                */
+       }
+
+       l4_umword_t handler = 0;
+       l4_umword_t pager   = 0;
+
+       if (flags & L4_THREAD_CONTROL_SET_PAGER) {
+               pager = l4_utcb_mr_u(utcb)->mr[L4_THREAD_CONTROL_MR_IDX_PAGER];
+               DEBUG() << "pager <- " << std::hex << pager;
+       }
+
+       if (flags & L4_THREAD_CONTROL_SET_EXC_HANDLER) {
+               handler = l4_utcb_mr_u(utcb)->mr[L4_THREAD_CONTROL_MR_IDX_EXC_HANDLER];
+               DEBUG() << "exc handler <- " << std::hex << handler;
+       }
+
+       if (handler && pager) {
+               if ((handler != pager)  || 
+                       (handler != Ldr::Remote_app_std_caps::Rm_thread_cap << L4_CAP_SHIFT) ||
+                       (pager != Ldr::Remote_app_std_caps::Rm_thread_cap << L4_CAP_SHIFT)) {
+                       ERROR() << "setting different pager or exc. handler not supported yet\n";
+                       enter_kdebug("error");
+               }
+       }
+
+       if (handler && (handler != Ldr::Remote_app_std_caps::Rm_thread_cap << L4_CAP_SHIFT)) {
+               ERROR() << "setting non-standard pager not supported yet\n";
+       }
+
+       if (pager && (pager != Ldr::Remote_app_std_caps::Rm_thread_cap << L4_CAP_SHIFT)) {
+               ERROR() << "setting non-standard exc. handler not supported yet\n";
+       }
+}
+
+
+void
+Romain::Thread_group::control(Romain::App_thread *t, l4_utcb_t *utcb, Romain::App_model *am)
+{
+       l4_umword_t flags = l4_utcb_mr_u(utcb)->mr[L4_THREAD_CONTROL_MR_IDX_FLAGS];
+
+       sanity_check_control(flags, utcb);
+
+       if (flags & L4_THREAD_CONTROL_BIND_TASK) {
+               l4_addr_t utcb_remote = l4_utcb_mr_u(utcb)->mr[L4_THREAD_CONTROL_MR_IDX_BIND_UTCB];
+               DEBUG() << "Setting remote UTCB to " << (void*)utcb_remote;
+
+               L4::Cap<L4Re::Dataspace> mem;
+               Romain::Region_map::allocate_ds(&mem, L4_PAGESIZE);
+               l4_addr_t local_addr = Romain::Region_map::allocate_and_attach(&mem, L4_PAGESIZE, 0, 0);
+               DEBUG() << "Attached TIP to " << (void*)local_addr;
+
+               /* store UTCB address in TIP */
+               *reinterpret_cast<l4_umword_t*>(local_addr) = utcb_remote;
+
+               am->rm()->activate(0);
+               void* tip_addr = (void*)0x10000;
+               tip_addr = am->rm()->attach(tip_addr, L4_PAGESIZE,
+                                           Romain::Region_handler(mem, L4_INVALID_CAP, 0,
+                                                                  0, Romain::Region(local_addr, local_addr + L4_PAGESIZE - 1)),
+                                           L4Re::Rm::Search_addr, L4_PAGESHIFT, false);
+               am->rm()->release();
+
+               INFO() << "Remote TIP address: " << tip_addr;
+
+               for (l4_umword_t i = 0; i < threads.size(); ++i) {
+                       threads[i]->setup_utcb_segdesc(reinterpret_cast<l4_addr_t>(tip_addr), 4);
+
+                       l4_addr_t utcb_local = am->rm()->remote_to_local(utcb_remote, i);
+                       threads[i]->remote_utcb(utcb_local);
+                       //threads[i]->commit_client_gdt();
+               }
+
+               //enter_kdebug("utcb");
+       }
+
+       /*
+        * Our current assumption is that the replicated app uses only default
+        * threading features, e.g. does not change pager, exception handler or
+        * any other thread features. Therefore, after sanity checking for these
+        * assumptions, we simply pretend everything went alright.
+        */
+       t->vcpu()->r()->ax = l4_msgtag(0, 3, 0, 0).raw;
+}
+
+void
+Romain::Thread_group::gdt(Romain::App_thread* t, l4_utcb_t *utcb)
+{
+       enum { replica_gs_base = 0x58 };
+
+       l4_msgtag_t *tag = reinterpret_cast<l4_msgtag_t*>(&t->vcpu()->r()->ax);
+       DEBUG() << BOLD_BLUE << "GDT: words" << NOCOLOR << " = " << tag->words();
+
+       // 1 word -> query GDT start
+       if (tag->words() == 1) {
+               l4_utcb_mr_u(utcb)->mr[0] = replica_gs_base >> 3;
+               t->vcpu()->r()->ax = l4_msgtag(0, 1, 0, 0).raw;
+               enter_kdebug("gdt query");
+       } else { // setup new GDT entry
+               l4_umword_t idx      = l4_utcb_mr_u(utcb)->mr[1];
+               l4_umword_t numbytes = (tag->words() == 4) ? 8 : 16;
+
+               for (l4_umword_t i = 0; i < threads.size(); ++i) {
+                       Romain::App_thread* thread = threads[i];
+
+                       if ((idx == 0) and (numbytes == 8)) { // actually, we only support a single entry here
+                               thread->write_gdt_entry(&l4_utcb_mr_u(utcb)->mr[2], numbytes);
+                               DEBUG() << "GS: " << std::hex << thread->vcpu()->r()->gs;
+                       } else {
+                               enter_kdebug("GDT??");
+                       }
+               }
+
+               t->vcpu()->r()->ax = l4_msgtag((idx << 3) + replica_gs_base + 3, 0, 0, 0).raw;
+       }
+}
+
+
+void* Romain::GateAgent::listener_function(void *gk)
+{
+       GateAgent *agent = reinterpret_cast<GateAgent*>(gk);
+       static char* utcb_copy[L4_UTCB_OFFSET];
+
+       char namebuf[16];
+       snprintf(namebuf, 16, "GK::%s", agent->owner_group->name.c_str());
+       l4_debugger_set_object_name(pthread_getl4cap(pthread_self()), namebuf);
+
+       sem_wait(&agent->init_sem);
+       sem_destroy(&agent->init_sem);
+       DEBUG() << "starting agent loop";
+
+       l4_utcb_t *my_utcb = l4_utcb();
+       l4_msgtag_t tag;
+
+       while (1) {
+#if USE_SHMSYNC
+               while (agent->current_client == 0) {
+                       l4_thread_yield();
+               }
+#endif
+               
+#if USE_IRQ
+               tag = agent->gate_irq->receive();
+#endif
+
 #if 0
-            + _vcpu->r()->fs
-            + _vcpu->r()->gs
+               DEBUG() << "Keeper activated by replica.";
+               DEBUG() << "agent " << (void*)agent;
+               DEBUG() << "client " << (void*)agent->current_client;
+               DEBUG() << " CLNT: " << (void*)agent->current_client << " remote_utcb: "
+                       << (void*)agent->current_client->remote_utcb();
+               if (agent->current_client == 0) {
+                       DEBUG() << "!!!!!" << std::hex << " " << tag.raw;
+                       DEBUG() << l4sys_errtostr(l4_error(tag));
+                       enter_kdebug();
+                       continue;
+               }
+               _check(agent->current_client == 0, "agent called with client NULL?");
 #endif
-            ;
+               
+               memcpy(utcb_copy, my_utcb, L4_UTCB_OFFSET);
+               memcpy(my_utcb, (void*)agent->current_client->remote_utcb(), L4_UTCB_OFFSET);
+
+               //outhex32((l4_umword_t)agent->current_client->vcpu()); outstring(" enter kernel\n");
+               asm volatile( L4_ENTER_KERNEL
+                         : "=a" (agent->current_client->vcpu()->r()->ax),
+                       "=b" (agent->current_client->vcpu()->r()->bx),
+                       /* ECX, EDX are overwritten anyway */
+                       "=S" (agent->current_client->vcpu()->r()->si),
+                       "=D" (agent->current_client->vcpu()->r()->di)
+                         : "a" (agent->current_client->vcpu()->r()->ax),
+                       /* EBX and EBP will be overwritten with local
+                        * values in L4_ENTER_KERNEL */
+                       "c" (agent->current_client->vcpu()->r()->cx),
+                       "d" (agent->current_client->vcpu()->r()->dx),
+                       "S" (agent->current_client->vcpu()->r()->si),
+                       "D" (agent->current_client->vcpu()->r()->di)
+                         : "memory", "cc"
+               );
+#if 0
+               outhex32((l4_umword_t)agent->current_client->vcpu()); outstring(" ret from kernel ");
+               outhex32((l4_umword_t)agent->current_client->vcpu()->r()->ax); outstring("\n");
+#endif
+
+               memcpy((void*)agent->current_client->remote_utcb(), my_utcb, L4_UTCB_OFFSET);
+               memcpy(my_utcb, utcb_copy, L4_UTCB_OFFSET);
+
+               l4_cap_idx_t cap = agent->current_client->vcpu_cap().cap();
+               agent->current_client = 0;
+               tag = l4_ipc_send(cap, l4_utcb(), l4_msgtag(0,0,0,0), L4_IPC_NEVER);
+       }
+
+       enter_kdebug("gateagent exited");
+
+       return 0;
 }