4 * App_thread functions for creating and preparing a new VCPU
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.
14 #include "app_loading"
15 #include "thread_group.h"
17 #include <l4/libloader/remote_app_model>
18 #include <pthread-l4.h>
20 void Romain::App_thread::alloc_vcpu_mem()
24 L4Re::Util::kumem_alloc(&kumem, 0);
25 _check(kumem == 0, "out of memory in kumem_alloc");
27 _vcpu_utcb = (l4_utcb_t *)kumem;
28 _vcpu = L4vcpu::Vcpu::cast(kumem + L4_UTCB_OFFSET);
30 /* store segment registers - stolen from the example */
31 //_vcpu->r()->gs = _master_ds;
32 _vcpu->r()->fs = _master_ds;
33 _vcpu->r()->es = _master_ds;
34 _vcpu->r()->ds = _master_ds;
35 _vcpu->r()->ss = _master_ds;
37 /* We want to catch ALL exceptions for this vCPU. */
38 _vcpu->saved_state()->set(
40 | L4_VCPU_F_PAGE_FAULTS
41 | L4_VCPU_F_EXCEPTIONS
44 | L4_VCPU_F_FPU_ENABLED
47 DEBUG() << "VCPU: utcb = " << (void*)vcpu_utcb()
48 << " vcpu @ " << (void*)vcpu();
52 void Romain::App_thread::touch_stacks()
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.
58 DEBUG() << "Stack info:";
59 DEBUG() << " handler stack @ " << (void*)_handler_stack
60 << " - " << (void*)(_handler_stack + sizeof(_handler_stack));
62 l4_touch_rw(_handler_stack, sizeof(_handler_stack));
66 void Romain::App_thread::alloc_vcpu_cap()
69 _vcpu_cap = chkcap(L4Re::Util::cap_alloc.alloc<L4::Thread>(),
71 chksys(L4Re::Env::env()->factory()->create_thread(_vcpu_cap),
73 l4_debugger_set_object_name(_vcpu_cap.cap(), "vcpu thread");
78 extern "C" void* pthread_fn(void *);
81 * Create a replica thread
83 void Romain::App_thread::start()
86 * We only set handler IP and SP here, because beforehand our creator
87 * may have modified them.
89 _vcpu->entry_sp(handler_sp());
90 _vcpu->entry_ip((l4_umword_t)_handler_fn);
92 int err = pthread_create(&_pthread, NULL, pthread_fn, this);
93 _check(err != 0, "pthread_create");
95 //_vcpu_cap = L4::Cap<L4::Thread>(pthread_getl4cap(_pthread));
100 * Calculate checksum of the replica's state
102 * This checksum is used to compare replica states.
105 Romain::App_thread::csum_state()
107 // XXX: this should also include the UTCB, which
108 // means the _used_ part of the UTCB
109 return _vcpu->r()->ip
123 Romain::Thread_group::ex_regs(Romain::App_thread *caller)
126 * Thoughts on ex_regs
128 * One use case of ex_regs is for an external thread to halt execution
129 * of a thread, store the halted thread's state and later continue from
130 * this point. As replicated threads execute independently, we will not
131 * stop all replicas in the same state. However, we require the replicas
132 * to be in the same state on resumption.
134 * To make things deterministic, we stop all replicas. As a heuristic, we
135 * then assume that the replicas are all correct (as all state
136 * difference can be attributed to diverging execution). We select the last
137 * stopped replica (potentially being the most advanced) as good copy and
138 * copy its state over to all other replicas, thereby forcing them to the
141 * 1) This may shadow certain errors that occurred prior to the ex_regs.
142 * -> we need to evaluate how often that happens
144 * 2) HP NonStop instead executes replicas in lock-step until it can
145 * figure out which is the most advanced. THen they execute everyone
146 * else up to this point.
148 * -> no lack in error detection
150 * 3) Can we detect situations where this happens as hard overwrite of
151 * the replicated thread's state -> hence we would not need to first
152 * enforce identical states? -> heuristics only, cannot determine
153 * upfront if the caller will later reuse the state.
156 bool need_to_stop = !this->stopped;
159 enter_kdebug("stop thread magic goes here");
162 l4_msg_regs_t *buf = l4_utcb_mr_u(reinterpret_cast<l4_utcb_t*>(caller->remote_utcb()));
163 DEBUG() << std::hex << buf->mr[0];
164 DEBUG() << std::hex << buf->mr[1];
165 DEBUG() << std::hex << buf->mr[2];
167 Romain::App_thread* thread = threads[0];
168 l4_umword_t eip = thread->vcpu()->r()->ip;
169 l4_umword_t esp = thread->vcpu()->r()->sp;
171 DEBUG() << (void*)thread->vcpu();
172 for (unsigned i = 0; i < threads.size(); ++i) {
173 threads[i]->vcpu()->r()->ip = buf->mr[1];
174 threads[i]->vcpu()->r()->sp = buf->mr[2];
180 caller->vcpu()->r()->ax = l4_msgtag(0,3,0,0).raw;
184 * If we stopped the threads before,
185 * we need to reactivate them now.
187 enter_kdebug("reactivation magic goes here");
189 //enter_kdebug("Thread_group::ex_regs");
194 Romain::Thread_group::scheduler_run(Romain::App_thread *caller)
196 l4_msg_regs_t *buf = l4_utcb_mr_u(reinterpret_cast<l4_utcb_t*>(caller->remote_utcb()));
198 DEBUG() << "Thread_group::scheduler_run("
199 << (void*)threads[0]->vcpu()
201 DEBUG() << "granularity | affinity.offs : " << std::hex << buf->mr[1];
202 DEBUG() << "affinity.map : " << std::hex << buf->mr[2];
203 DEBUG() << "prio : " << std::hex << buf->mr[3];
204 DEBUG() << "quantum : " << std::hex << buf->mr[4];
205 DEBUG() << "obj_control : " << std::hex << buf->mr[5];
206 DEBUG() << "thread : " << std::hex << buf->mr[6];
210 //enter_kdebug("Thread_group::schedule()");
215 Romain::Thread_group::sanity_check_control(unsigned flags, l4_utcb_t *utcb)
217 DEBUG() << "Control flags: " << std::hex << l4_utcb_mr_u(utcb)->mr[L4_THREAD_CONTROL_MR_IDX_FLAGS];
219 if ((flags & L4_THREAD_CONTROL_ALIEN) ||
220 (flags & L4_THREAD_CONTROL_UX_NATIVE)) {
221 ERROR() << "ux_native and alien not supported yet";
224 if (flags & L4_THREAD_CONTROL_BIND_TASK) {
226 fp.raw = l4_utcb_mr_u(utcb)->mr[L4_THREAD_CONTROL_MR_IDX_BIND_TASK + 1];
227 DEBUG() << std::hex << "L4_THREAD_CONTROL_BIND_TASK := ("
228 << l4_utcb_mr_u(utcb)->mr[L4_THREAD_CONTROL_MR_IDX_BIND_UTCB] << ", "
229 << l4_fpage_page(fp) << ")";
230 if (l4_fpage_page(fp) != (L4Re::This_task >> L4_CAP_SHIFT)) {
231 ERROR() << "Binding to different task not supported yet.";
232 enter_kdebug("error");
234 /* Apart from these checks, don't do anything. The replica vCPUs
239 unsigned handler = 0;
242 if (flags & L4_THREAD_CONTROL_SET_PAGER) {
243 pager = l4_utcb_mr_u(utcb)->mr[L4_THREAD_CONTROL_MR_IDX_PAGER];
244 DEBUG() << "pager <- " << std::hex << pager;
247 if (flags & L4_THREAD_CONTROL_SET_EXC_HANDLER) {
248 handler = l4_utcb_mr_u(utcb)->mr[L4_THREAD_CONTROL_MR_IDX_EXC_HANDLER];
249 DEBUG() << "exc handler <- " << std::hex << handler;
252 if (handler && pager) {
253 if ((handler != pager) ||
254 (handler != Ldr::Remote_app_std_caps::Rm_thread_cap << L4_CAP_SHIFT) ||
255 (pager != Ldr::Remote_app_std_caps::Rm_thread_cap << L4_CAP_SHIFT)) {
256 ERROR() << "setting different pager or exc. handler not supported yet";
257 enter_kdebug("error");
261 if (handler && (handler != Ldr::Remote_app_std_caps::Rm_thread_cap << L4_CAP_SHIFT)) {
262 ERROR() << "setting non-standard pager not supported yet";
265 if (pager && (pager != Ldr::Remote_app_std_caps::Rm_thread_cap << L4_CAP_SHIFT)) {
266 ERROR() << "setting non-standard exc. handler not supported yet";
272 Romain::Thread_group::control(Romain::App_thread *t, l4_utcb_t *utcb, Romain::App_model *am)
274 unsigned flags = l4_utcb_mr_u(utcb)->mr[L4_THREAD_CONTROL_MR_IDX_FLAGS];
276 sanity_check_control(flags, utcb);
278 if (flags & L4_THREAD_CONTROL_BIND_TASK) {
279 l4_addr_t utcb_remote = l4_utcb_mr_u(utcb)->mr[L4_THREAD_CONTROL_MR_IDX_BIND_UTCB];
280 DEBUG() << "Setting remote UTCB to " << (void*)utcb_remote;
282 Romain::App_model::Dataspace ds = am->alloc_ds(L4_PAGESIZE); // thread info page
283 l4_addr_t local_addr = am->local_attach_ds(ds, L4_PAGESIZE, 0);
284 DEBUG() << "Attached TIP to " << (void*)local_addr;
286 *reinterpret_cast<l4_umword_t*>(local_addr) = utcb_remote;
287 void* tip_addr = am->prog_attach_ds(0, L4_PAGESIZE, ds, 0,
288 L4Re::Rm::Search_addr, "thread info page", local_addr);
289 DEBUG() << "Remote TIP address: " << tip_addr;
291 for (unsigned i = 0; i < threads.size(); ++i) {
292 threads[i]->setup_utcb_segdesc(reinterpret_cast<l4_addr_t>(tip_addr), 4);
294 l4_addr_t utcb_local = am->rm()->remote_to_local(utcb_remote, i);
295 threads[i]->remote_utcb(utcb_local);
296 //threads[i]->commit_client_gdt();
299 //enter_kdebug("utcb");
303 * Our current assumption is that the replicated app uses only default
304 * threading features, e.g. does not change pager, exception handler or
305 * any other thread features. Therefore, after sanity checking for these
306 * assumptions, we simply pretend everything went alright.
308 t->vcpu()->r()->ax = l4_msgtag(0, 3, 0, 0).raw;
312 Romain::Thread_group::gdt(Romain::App_thread* t, l4_utcb_t *utcb)
314 enum { replica_gs_base = 0x58 };
316 l4_msgtag_t *tag = reinterpret_cast<l4_msgtag_t*>(&t->vcpu()->r()->ax);
317 DEBUG() << BOLD_BLUE "GDT: words" << NOCOLOR << " = " << tag->words();
319 // 1 word -> query GDT start
320 if (tag->words() == 1) {
321 l4_utcb_mr_u(utcb)->mr[0] = replica_gs_base >> 3;
322 t->vcpu()->r()->ax = l4_msgtag(0, 1, 0, 0).raw;
323 enter_kdebug("gdt query");
324 } else { // setup new GDT entry
325 unsigned idx = l4_utcb_mr_u(utcb)->mr[1];
326 unsigned numbytes = (tag->words() == 4) ? 8 : 16;
328 for (unsigned i = 0; i < threads.size(); ++i) {
329 Romain::App_thread* thread = threads[i];
331 if ((idx == 0) and (numbytes == 8)) { // actually, we only support a single entry here
332 thread->write_gdt_entry(&l4_utcb_mr_u(utcb)->mr[2], numbytes);
333 DEBUG() << "GS: " << std::hex << thread->vcpu()->r()->gs;
335 enter_kdebug("GDT??");
339 t->vcpu()->r()->ax = l4_msgtag((idx << 3) + replica_gs_base + 3, 0, 0, 0).raw;
344 void* Romain::GateAgent::listener_function(void *gk)
346 GateAgent *agent = reinterpret_cast<GateAgent*>(gk);
347 static char* utcb_copy[L4_UTCB_OFFSET];
350 snprintf(namebuf, 16, "GK::%s", agent->owner_group->name.c_str());
351 l4_debugger_set_object_name(pthread_getl4cap(pthread_self()), namebuf);
353 sem_wait(&agent->init_sem);
354 sem_destroy(&agent->init_sem);
355 DEBUG() << "starting agent loop";
357 l4_utcb_t *my_utcb = l4_utcb();
362 while (agent->current_client == 0) {
368 tag = agent->gate_irq->receive();
372 DEBUG() << "Keeper activated by replica.";
373 DEBUG() << "agent " << (void*)agent;
374 DEBUG() << "client " << (void*)agent->current_client;
375 DEBUG() << " CLNT: " << (void*)agent->current_client << " remote_utcb: "
376 << (void*)agent->current_client->remote_utcb();
377 if (agent->current_client == 0) {
378 DEBUG() << "!!!!!" << std::hex << " " << tag.raw;
379 DEBUG() << l4sys_errtostr(l4_error(tag));
383 _check(agent->current_client == 0, "agent called with client NULL?");
386 memcpy(utcb_copy, my_utcb, L4_UTCB_OFFSET);
387 memcpy(my_utcb, (void*)agent->current_client->remote_utcb(), L4_UTCB_OFFSET);
389 //outhex32((unsigned)agent->current_client->vcpu()); outstring(" enter kernel\n");
390 asm volatile( L4_ENTER_KERNEL
391 : "=a" (agent->current_client->vcpu()->r()->ax),
392 "=b" (agent->current_client->vcpu()->r()->bx),
393 /* ECX, EDX are overwritten anyway */
394 "=S" (agent->current_client->vcpu()->r()->si),
395 "=D" (agent->current_client->vcpu()->r()->di)
396 : "a" (agent->current_client->vcpu()->r()->ax),
397 /* EBX and EBP will be overwritten with local
398 * values in L4_ENTER_KERNEL */
399 "c" (agent->current_client->vcpu()->r()->cx),
400 "d" (agent->current_client->vcpu()->r()->dx),
401 "S" (agent->current_client->vcpu()->r()->si),
402 "D" (agent->current_client->vcpu()->r()->di)
406 outhex32((unsigned)agent->current_client->vcpu()); outstring(" ret from kernel ");
407 outhex32((unsigned)agent->current_client->vcpu()->r()->ax); outstring("\n");
410 memcpy((void*)agent->current_client->remote_utcb(), my_utcb, L4_UTCB_OFFSET);
411 memcpy(my_utcb, utcb_copy, L4_UTCB_OFFSET);
413 l4_cap_idx_t cap = agent->current_client->vcpu_cap().cap();
414 agent->current_client = 0;
415 tag = l4_ipc_send(cap, l4_utcb(), l4_msgtag(0,0,0,0), L4_IPC_NEVER);
418 enter_kdebug("gateagent exited");