]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/plr/server/src/fault_handlers/syscalls.cc
update
[l4.git] / l4 / pkg / plr / server / src / fault_handlers / syscalls.cc
1 /*
2  * syscalls.cc --
3  *
4  *     Implementation of Romain syscall handling.
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 "../log"
14 #include "../app_loading"
15 #include "../locking.h"
16 #include "../manager"
17 #include "../thread_group.h"
18
19 #include "observers.h"
20
21 #include <l4/re/util/region_mapping_svr>
22 #include <l4/sys/segment.h>
23 #include <l4/sys/consts.h>
24
25 #define MSG() DEBUGf(Romain::Log::Faults)
26 #define MSGt(t) DEBUGf(Romain::Log::Faults) << "[" << t->vcpu() << "] "
27 #define DEBUGt(t) DEBUG() <<  "[" << t->vcpu() << "] "
28
29 #include "syscalls_factory.h" // uses MSG() defined above (ugly)
30
31 DEFINE_EMPTY_STARTUP(SyscallObserver)
32
33 static unsigned long num_syscalls;
34
35
36 void Romain::SyscallObserver::status() const
37 {
38         INFO() << "[sys ] System call count: " << num_syscalls;
39 }
40
41
42 static Romain::ThreadHandler          threadsyscall;
43 static Romain::SyscallHandler         nullhandler;
44 static Romain::RegionManagingHandler  rm;
45 static Romain::Scheduling             sched;
46 static Romain::IrqHandler             irq;
47
48 Romain::Factory theObjectFactory;
49
50 Romain::Observer::ObserverReturnVal
51 Romain::SyscallObserver::notify(Romain::App_instance *i,
52                                 Romain::App_thread *t,
53                                 Romain::Thread_group *tg,
54                                 Romain::App_model *a)
55 {
56         enum { Syscall_magic_address = 0xEACFF003, };
57         Romain::Observer::ObserverReturnVal retval = Romain::Observer::Ignored;
58
59         if (t->vcpu()->r()->trapno != 13) {
60                 return retval;
61         }
62
63         /* SYSENTER / INT30 */
64         if (t->vcpu()->r()->ip == Syscall_magic_address) {
65                 ++num_syscalls;
66
67                 l4_msgtag_t *tag = reinterpret_cast<l4_msgtag_t*>(&t->vcpu()->r()->ax);
68                 MSG() << "SYSENTER(" << tg->name << ") tag = " << std::hex << tag->label();
69
70                 Measurements::GenericEvent* ev = Romain::_the_instance_manager->logbuf()->next();
71                 ev->header.tsc     = Romain::_the_instance_manager->logbuf()->getTime(Log::logLocalTSC);
72                 ev->header.vcpu    = (l4_uint32_t)t->vcpu();
73                 ev->header.type    = Measurements::Syscall;
74                 ev->data.sys.eip   = t->vcpu()->r()->bx;
75                 ev->data.sys.label = tag->label();
76
77                 /*
78                  * Fiasco-specific:
79                  *      EBX is return address
80                  *      EBP is return ESP
81                  * -> we need to remember these
82                  */
83                 l4_addr_t ebx_pre = t->vcpu()->r()->bx;
84                 l4_addr_t ebp_pre = t->vcpu()->r()->bp;
85
86                 switch(tag->label()) {
87
88                         case L4_PROTO_NONE:
89                                 /*
90                                  * Catch the open_wait versions of IPC as they require
91                                  * redirection through the gateagent thread. Open wait
92                                  * is defined by the IPC bindings as one where EDX is set
93                                  * to L4_INVALID_CAP | <some_flags>.
94                                  */
95                                 if ((t->vcpu()->r()->dx & L4_INVALID_CAP) == L4_INVALID_CAP) {
96                                         tg->gateagent->trigger_agent(t);
97                                 } else {
98                                         nullhandler.proxy_syscall(i, t, tg, a);
99                                 }
100                                 retval = Romain::Observer::Replicatable;
101                                 break;
102
103                         case L4_PROTO_FACTORY:
104                                 retval = theObjectFactory.handle(i, t, tg, a);
105                                 break;
106
107                         case L4_PROTO_IRQ:
108                                 retval = irq.handle(i, t, tg, a);
109                                 break;
110
111                         case L4_PROTO_THREAD:
112                                 /* 
113                                  * Each instance needs to perform its own
114                                  * thread creation.
115                                  */
116                                 retval = threadsyscall.handle(i, t, tg, a);
117                                 break;
118
119                         case L4_PROTO_TASK:
120                                 if ((t->vcpu()->r()->dx & ~0xF) == L4RE_THIS_TASK_CAP) {
121                                         handle_task(i, t, a);
122                                         retval = Romain::Observer::Finished;
123                                 } else {
124                                         nullhandler.proxy_syscall(i, t, tg, a);
125                                         retval = Romain::Observer::Replicatable;
126                                 }
127                                 break;
128
129                         case L4Re::Protocol::Rm:
130                                 /*
131                                  * Region management is done only once as e.g.,
132                                  * regions for attaching need to be replicated
133                                  * across instances. The real adaptation then
134                                  * happens during page fault handling.
135                                  */
136                                 retval = rm.handle(i, t, tg, a);
137                                 break;
138
139                         case L4Re::Protocol::Parent:
140                                 /*
141                                  * The parent protocol is only used for exitting.
142                                  */
143                                 {
144                                         struct timeval tv;
145                                         gettimeofday(&tv, 0);
146                                         INFO() << "Instance " << i->id() << " exitting. Time "
147                                                << YELLOW << tv.tv_sec << "." << tv.tv_usec
148                                                << NOCOLOR;
149
150                                         Measurements::GenericEvent* ev = Romain::_the_instance_manager->logbuf()->next();
151                                         ev->header.tsc                 = Romain::_the_instance_manager->logbuf()->getTime(Log::logLocalTSC);
152                                         ev->header.vcpu                = (l4_uint32_t)t->vcpu();
153                                         ev->header.type                = Measurements::Thread_stop;
154
155                                         Romain::_the_instance_manager->show_stats();
156                                         
157                                         if (1) enter_kdebug("*#^");
158
159                                         nullhandler.proxy_syscall(i, t, tg, a);
160                                         retval = Romain::Observer::Replicatable;
161                                 }
162                                 break;
163
164                         case L4_PROTO_SCHEDULER:
165                                 retval = sched.handle(i, t, tg, a);
166                                 break;
167
168                         default:
169                                 /*
170                                  * Proxied syscalls are always only executed
171                                  * once because for the outside world it must
172                                  * look as if the master server _is_ the one
173                                  * application everyone is talking to.
174                                  */
175                                 nullhandler.proxy_syscall(i, t, tg, a);
176                                 retval = Romain::Observer::Replicatable;
177                                 break;
178                 
179                 }
180
181                 t->vcpu()->r()->ip = ebx_pre;
182                 t->vcpu()->r()->sp = ebp_pre;
183
184         } else if (t->vcpu()->r()->err == 0x152) { // INT $42
185                 INFO() << "[" << std::hex << (unsigned)t->vcpu() <<  "] INT 42 ("
186                            << t->vcpu()->r()->ip << ")";
187                 t->vcpu()->r()->ip += 2;
188                 retval = Romain::Observer::Replicatable;
189         } else {
190                 t->vcpu()->print_state();
191                 INFO() << "err = " << std::hex << t->vcpu()->r()->err;
192                 MSG() << "GPF";
193                 enter_kdebug("GPF in replica");
194         }
195
196         return retval;
197 }
198
199
200 void Romain::SyscallHandler::proxy_syscall(Romain::App_instance *,
201                                            Romain::App_thread* t,
202                                            Romain::Thread_group* tg,
203                                            Romain::App_model *)
204 {
205         char backup_utcb[L4_UTCB_OFFSET]; // for storing local UTCB content
206
207         l4_utcb_t *addr = reinterpret_cast<l4_utcb_t*>(t->remote_utcb());
208         l4_utcb_t *cur_utcb = l4_utcb();
209         MSGt(t) << "UTCB @ " << std::hex << (unsigned)addr;
210         _check((l4_addr_t)addr == ~0UL, "remote utcb ptr??");
211
212         /*
213          * We are going to perform the system call on behalf of the client. This will
214          * thrash our local UTCB, so we want to store it here.
215          */
216         store_utcb((char*)cur_utcb, backup_utcb);
217         store_utcb((char*)addr, (char*)cur_utcb);
218
219         //t->print_vcpu_state();
220         //Romain::dump_mem((unsigned*)addr, 40);
221
222         /* Perform Fiasco system call */
223         asm volatile (L4_ENTER_KERNEL
224                       : "=a" (t->vcpu()->r()->ax),
225                       "=b" (t->vcpu()->r()->bx),
226                       /* ECX, EDX are overwritten anyway */
227                       "=S" (t->vcpu()->r()->si),
228                       "=D" (t->vcpu()->r()->di)
229                       : "a" (t->vcpu()->r()->ax),
230                       /* EBX and EBP will be overwritten with local
231                        * values in L4_ENTER_KERNEL */
232                         "c" (t->vcpu()->r()->cx),
233                         "d" (t->vcpu()->r()->dx),
234                         "S" (t->vcpu()->r()->si),
235                         "D" (t->vcpu()->r()->di)
236                       : "memory", "cc"
237         );
238
239         /*
240          * Restore my UTCB
241          */
242         store_utcb((char*)cur_utcb, (char*)addr);
243         store_utcb(backup_utcb, (char*)cur_utcb);
244
245         //t->print_vcpu_state();
246         //Romain::dump_mem((unsigned*)addr, 40);
247         //enter_kdebug("done syscall");
248 }
249
250
251 Romain::Observer::ObserverReturnVal
252 Romain::RegionManagingHandler::handle(Romain::App_instance* i,
253                                       Romain::App_thread* t,
254                                       Romain::Thread_group* tg,
255                                       Romain::App_model * a)
256 {
257         MSGt(t) << "RM PROTOCOL";
258
259         l4_utcb_t *utcb = reinterpret_cast<l4_utcb_t*>(t->remote_utcb());
260         MSGt(t) << "UTCB @ " << std::hex << (unsigned)utcb;
261         _check((l4_addr_t)utcb == ~0UL, "remote utcb ptr??");
262
263         //t->print_vcpu_state();
264         //Romain::dump_mem((unsigned*)utcb, 40);
265
266         {
267                 Romain::Rm_guard r(a->rm(), i->id());
268                 L4::Ipc::Iostream ios(utcb);
269                 L4Re::Util::region_map_server<Romain::Region_map_server>(a->rm(), ios);
270         }
271
272         //t->print_vcpu_state();
273         //Romain::dump_mem((unsigned*)utcb, 40);
274
275         return Romain::Observer::Replicatable;
276 }
277
278
279 Romain::Observer::ObserverReturnVal
280 Romain::ThreadHandler::handle(Romain::App_instance *,
281                               Romain::App_thread* t,
282                               Romain::Thread_group * tg,
283                               Romain::App_model *am)
284 {
285         MSGt(t) << "Thread system call";
286         l4_utcb_t *utcb = reinterpret_cast<l4_utcb_t*>(t->remote_utcb());
287         MSGt(t) << "UTCB @ " << std::hex << (unsigned)utcb;
288         _check((l4_addr_t)utcb == ~0UL, "remote utcb ptr??");
289
290         //t->print_vcpu_state();
291         //Romain::dump_mem((unsigned*)utcb, 40);
292
293         l4_umword_t op   = l4_utcb_mr_u(utcb)->mr[0] & L4_THREAD_OPCODE_MASK;
294         l4_umword_t dest = t->vcpu()->r()->dx & L4_CAP_MASK;
295
296         DEBUG() << "dest cap " << std::hex << dest << " " << L4_INVALID_CAP;
297         Romain::Thread_group* group = (Romain::Thread_group*)0xdeadbeef;
298         if (dest == L4_INVALID_CAP) {
299                 group = tg;
300         } else {
301                 group = theObjectFactory.thread_for_cap(dest >> L4_CAP_SHIFT);
302         }
303         DEBUG() << "tgroup " << group;
304
305         switch(op) {
306                 case L4_THREAD_CONTROL_OP:
307                         group->control(t, utcb, am);
308                         return Romain::Observer::Replicatable;
309                 case L4_THREAD_EX_REGS_OP:
310                         group->ex_regs(t);
311                         break;
312                 case L4_THREAD_SWITCH_OP:
313                         enter_kdebug("THREAD: switch");
314                         break;
315                 case L4_THREAD_STATS_OP:
316                         enter_kdebug("THREAD: stats");
317                         break;
318                 case L4_THREAD_VCPU_RESUME_OP:
319                         enter_kdebug("THREAD: vcpu_resume");
320                         break;
321                 case L4_THREAD_REGISTER_DELETE_IRQ_OP:
322                         enter_kdebug("THREAD: irq");
323                         break;
324                 case L4_THREAD_MODIFY_SENDER_OP:
325                         enter_kdebug("THREAD: modify sender");
326                         break;
327                 case L4_THREAD_VCPU_CONTROL_OP:
328                         enter_kdebug("THREAD: vcpu control");
329                         break;
330                 case L4_THREAD_VCPU_CONTROL_EXT_OP:
331                         enter_kdebug("THREAD: vcpu control ext");
332                         break;
333                 case L4_THREAD_GDT_X86_OP:
334                         group->gdt(t, utcb);
335                         break;
336                 case L4_THREAD_SET_FS_AMD64_OP:
337                         enter_kdebug("THREAD: set fs amd64");
338                         break;
339                 default:
340                         ERROR() << "unknown thread op: " << std::hex << op;
341                         break;
342         }
343
344         return Romain::Observer::Replicatable;
345         //enter_kdebug("thread");
346 }
347
348
349 void Romain::SyscallObserver::handle_task(Romain::App_instance* i,
350                                           Romain::App_thread*   t,
351                                           Romain::App_model*    a)
352 {
353         l4_utcb_t   *utcb = reinterpret_cast<l4_utcb_t*>(t->remote_utcb());
354         l4_umword_t    op = l4_utcb_mr_u(utcb)->mr[0] & L4_THREAD_OPCODE_MASK;
355         switch(op) {
356                 case L4_TASK_UNMAP_OP:
357 #if 0
358                         MSGt(t) << "unmap";
359                         i->unmap(l4_utcb_mr_u(utcb)->mr[2]);
360 #endif
361                         break;
362                 case L4_TASK_CAP_INFO_OP:
363                         nullhandler.proxy_syscall(i,t,0,a);
364                         break;
365                 default:
366                         MSGt(t) << "Task system call";
367                         MSGt(t) << "UTCB @ " << std::hex << (unsigned)utcb << " op: " << op
368                                   << " cap " << (t->vcpu()->r()->dx & ~0xF) << " " << L4RE_THIS_TASK_CAP;
369                         t->print_vcpu_state();
370                         enter_kdebug("unknown task op?");
371                         break;
372         }
373 }
374
375
376 Romain::Observer::ObserverReturnVal
377 Romain::Factory::handle(Romain::App_instance* inst,
378                         Romain::App_thread* t,
379                         Romain::Thread_group* tg,
380                         Romain::App_model* am)
381 {
382         MSGt(t) << "Factory system call";
383         l4_utcb_t *utcb = reinterpret_cast<l4_utcb_t*>(t->remote_utcb());
384         MSGt(t) << "UTCB @ " << std::hex << (unsigned)utcb;
385         _check((l4_addr_t)utcb == ~0UL, "remote utcb ptr??");
386
387         l4_umword_t obj = l4_utcb_mr_u(utcb)->mr[0];
388         l4_umword_t cap = l4_utcb_br_u(utcb)->br[0] & ~L4_RCV_ITEM_SINGLE_CAP;
389         MSGt(t) << std::hex << L4_PROTO_THREAD;
390         MSGt(t) << "object type: " << std::hex << obj
391               << " cap: " << cap;
392
393         switch(obj) {
394                 case L4_PROTO_THREAD:
395                         create_thread(inst, t, tg, am, cap);
396                         return Romain::Observer::Replicatable;
397
398                 case L4_PROTO_IRQ:
399                         create_irq(inst, t, tg, am, cap);
400                         return Romain::Observer::Replicatable;
401
402                 case L4Re::Protocol::Dataspace:
403                         SyscallHandler::proxy_syscall(inst, t, tg, am);
404                         return Romain::Observer::Replicatable;
405
406                 default:
407                         break;
408         }
409
410         enter_kdebug("theObjectFactory");
411         return Romain::Observer::Finished;
412 }
413
414
415 Romain::Observer::ObserverReturnVal
416 Romain::Scheduling::handle(Romain::App_instance* inst,
417                            Romain::App_thread* t,
418                            Romain::Thread_group* tg,
419                            Romain::App_model* am)
420 {
421         l4_utcb_t *utcb = reinterpret_cast<l4_utcb_t*>(t->remote_utcb());
422         l4_umword_t op  = l4_utcb_mr_u(utcb)->mr[0];
423
424         MSGt(t) << "\033[32mschedule(" << std::hex << op << ")\033[0m";
425         if (op == L4_SCHEDULER_RUN_THREAD_OP) {
426                 l4_umword_t cap             = l4_utcb_mr_u(utcb)->mr[6] >> L4_CAP_SHIFT;
427                 Romain::Thread_group* group = theObjectFactory.thread_for_cap(cap);
428                 group->scheduler_run(t);
429         } else {
430                 //enter_kdebug("run_thread != 1");
431                 SyscallHandler::proxy_syscall(inst, t, tg, am);
432         }
433
434         return Romain::Observer::Replicatable;
435 }
436
437
438 Romain::Observer::ObserverReturnVal
439 Romain::IrqHandler::handle(Romain::App_instance* inst,
440                            Romain::App_thread* t,
441                            Romain::Thread_group* tg,
442                            Romain::App_model* am)
443 {
444         l4_utcb_t *utcb = reinterpret_cast<l4_utcb_t*>(t->remote_utcb());
445         unsigned op     = l4_utcb_mr_u(utcb)->mr[0];
446         unsigned label  = l4_utcb_mr_u(utcb)->mr[1];
447         unsigned cap    = t->vcpu()->r()->dx & L4_CAP_MASK;
448
449         L4::Cap<L4::Irq> irq(cap);
450
451         DEBUGt(t) << "IRQ: cap = " << std::hex << cap << " op = " << op; 
452
453         if (!theObjectFactory.is_irq(cap)) {
454                 SyscallHandler::proxy_syscall(inst, t, tg, am);
455                 return Romain::Observer::Replicatable;
456         }
457
458         switch(op) {
459                 /*
460                  * For attach(), we cannot simply redirect to the gate
461                  * agent, because we need to modify the thread that is
462                  * attached to the IRQ
463                  */
464                 case L4_IRQ_OP_ATTACH:
465                         {
466                                 l4_umword_t attach_cap      = l4_utcb_mr_u(utcb)->mr[3] & L4_FPAGE_ADDR_MASK;
467                                 DEBUG() << "attach " << std::hex << (attach_cap >> L4_CAP_SHIFT);
468                                 Romain::Thread_group *group = theObjectFactory.thread_for_cap(attach_cap >> L4_CAP_SHIFT);
469                                 l4_msgtag_t ret;
470
471                                 if (!group) {
472                                         ERROR() << "Unimplemented: Attaching someone else but myself!";
473                                         enter_kdebug();
474                                 }
475
476                                 ret = irq->attach(label, group->gateagent->listener_cap);
477
478                                 t->vcpu()->r()->ax = ret.raw;
479                                 DEBUG() << std::hex << ret.raw ;
480                                 return Romain::Observer::Replicatable;
481                         }
482                         break;
483
484                 case L4_IRQ_OP_TRIGGER:
485                         DEBUGt(t) << ":: trigger";
486                         //enter_kdebug("trigger");
487                         irq->trigger();
488                         break;
489                 case L4_IRQ_OP_EOI:
490                         DEBUGt(t) << ":: eoi";
491                         tg->gateagent->trigger_agent(t);
492                         break;
493
494                 case L4_IRQ_OP_CHAIN:
495                         enter_kdebug("irq::chain?");
496         }
497
498         return Romain::Observer::Replicatable;
499 }