]> 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() << "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                                                << "\033[33;1m" << tv.tv_sec << "." << tv.tv_usec
148                                                << "\033[0m";
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->query_observer_status();
156
157                                         Romain::_the_instance_manager->logdump();
158                                         
159                                         if (1) enter_kdebug("*#^");
160
161                                         nullhandler.proxy_syscall(i, t, tg, a);
162                                         retval = Romain::Observer::Replicatable;
163                                 }
164                                 break;
165
166                         case L4_PROTO_SCHEDULER:
167                                 retval = sched.handle(i, t, tg, a);
168                                 break;
169
170                         default:
171                                 /*
172                                  * Proxied syscalls are always only executed
173                                  * once because for the outside world it must
174                                  * look as if the master server _is_ the one
175                                  * application everyone is talking to.
176                                  */
177                                 nullhandler.proxy_syscall(i, t, tg, a);
178                                 retval = Romain::Observer::Replicatable;
179                                 break;
180                 
181                 }
182
183                 t->vcpu()->r()->ip = ebx_pre;
184                 t->vcpu()->r()->sp = ebp_pre;
185
186         } else {
187                 MSG() << "GPF";
188                 enter_kdebug("GPF in replica");
189         }
190
191         return retval;
192 }
193
194
195 void Romain::SyscallHandler::proxy_syscall(Romain::App_instance *,
196                                            Romain::App_thread* t,
197                                            Romain::Thread_group* tg,
198                                            Romain::App_model *)
199 {
200         char backup_utcb[L4_UTCB_OFFSET]; // for storing local UTCB content
201
202         l4_utcb_t *addr = reinterpret_cast<l4_utcb_t*>(t->remote_utcb());
203         l4_utcb_t *cur_utcb = l4_utcb();
204         MSGt(t) << "UTCB @ " << std::hex << (unsigned)addr;
205         _check((l4_addr_t)addr == ~0UL, "remote utcb ptr??");
206
207         /*
208          * We are going to perform the system call on behalf of the client. This will
209          * thrash our local UTCB, so we want to store it here.
210          */
211         store_utcb((char*)cur_utcb, backup_utcb);
212         store_utcb((char*)addr, (char*)cur_utcb);
213
214         //t->print_vcpu_state();
215         //Romain::dump_mem((unsigned*)addr, 40);
216
217         /* Perform Fiasco system call */
218         asm volatile (L4_ENTER_KERNEL
219                       : "=a" (t->vcpu()->r()->ax),
220                       "=b" (t->vcpu()->r()->bx),
221                       /* ECX, EDX are overwritten anyway */
222                       "=S" (t->vcpu()->r()->si),
223                       "=D" (t->vcpu()->r()->di)
224                       : "a" (t->vcpu()->r()->ax),
225                       /* EBX and EBP will be overwritten with local
226                        * values in L4_ENTER_KERNEL */
227                         "c" (t->vcpu()->r()->cx),
228                         "d" (t->vcpu()->r()->dx),
229                         "S" (t->vcpu()->r()->si),
230                         "D" (t->vcpu()->r()->di)
231                       : "memory", "cc"
232         );
233
234         /*
235          * Restore my UTCB
236          */
237         store_utcb((char*)cur_utcb, (char*)addr);
238         store_utcb(backup_utcb, (char*)cur_utcb);
239
240         //t->print_vcpu_state();
241         //Romain::dump_mem((unsigned*)addr, 40);
242         //enter_kdebug("done syscall");
243 }
244
245
246 Romain::Observer::ObserverReturnVal
247 Romain::RegionManagingHandler::handle(Romain::App_instance* i,
248                                       Romain::App_thread* t,
249                                       Romain::Thread_group* tg,
250                                       Romain::App_model * a)
251 {
252         MSGt(t) << "RM PROTOCOL";
253
254         l4_utcb_t *utcb = reinterpret_cast<l4_utcb_t*>(t->remote_utcb());
255         MSGt(t) << "UTCB @ " << std::hex << (unsigned)utcb;
256         _check((l4_addr_t)utcb == ~0UL, "remote utcb ptr??");
257
258         //t->print_vcpu_state();
259         //Romain::dump_mem((unsigned*)utcb, 40);
260
261         {
262                 Romain::Rm_guard r(a->rm(), i->id());
263                 L4::Ipc::Iostream ios(utcb);
264                 L4Re::Util::region_map_server<Romain::Region_map_server>(a->rm(), ios);
265         }
266
267         //t->print_vcpu_state();
268         //Romain::dump_mem((unsigned*)utcb, 40);
269
270         return Romain::Observer::Replicatable;
271 }
272
273
274 Romain::Observer::ObserverReturnVal
275 Romain::ThreadHandler::handle(Romain::App_instance *,
276                               Romain::App_thread* t,
277                               Romain::Thread_group * tg,
278                               Romain::App_model *am)
279 {
280         MSGt(t) << "Thread system call";
281         l4_utcb_t *utcb = reinterpret_cast<l4_utcb_t*>(t->remote_utcb());
282         MSGt(t) << "UTCB @ " << std::hex << (unsigned)utcb;
283         _check((l4_addr_t)utcb == ~0UL, "remote utcb ptr??");
284
285         //t->print_vcpu_state();
286         //Romain::dump_mem((unsigned*)utcb, 40);
287
288         l4_umword_t op   = l4_utcb_mr_u(utcb)->mr[0] & L4_THREAD_OPCODE_MASK;
289         l4_umword_t dest = t->vcpu()->r()->dx & L4_CAP_MASK;
290
291         DEBUG() << "dest cap " << std::hex << dest << " " << L4_INVALID_CAP;
292         Romain::Thread_group* group = (Romain::Thread_group*)0xdeadbeef;
293         if (dest == L4_INVALID_CAP) {
294                 group = tg;
295         } else {
296                 group = theObjectFactory.thread_for_cap(dest >> L4_CAP_SHIFT);
297         }
298         DEBUG() << "tgroup " << group;
299
300         switch(op) {
301                 case L4_THREAD_CONTROL_OP:
302                         group->control(t, utcb, am);
303                         return Romain::Observer::Replicatable;
304                 case L4_THREAD_EX_REGS_OP:
305                         group->ex_regs(t);
306                         break;
307                 case L4_THREAD_SWITCH_OP:
308                         enter_kdebug("THREAD: switch");
309                         break;
310                 case L4_THREAD_STATS_OP:
311                         enter_kdebug("THREAD: stats");
312                         break;
313                 case L4_THREAD_VCPU_RESUME_OP:
314                         enter_kdebug("THREAD: vcpu_resume");
315                         break;
316                 case L4_THREAD_REGISTER_DELETE_IRQ_OP:
317                         enter_kdebug("THREAD: irq");
318                         break;
319                 case L4_THREAD_MODIFY_SENDER_OP:
320                         enter_kdebug("THREAD: modify sender");
321                         break;
322                 case L4_THREAD_VCPU_CONTROL_OP:
323                         enter_kdebug("THREAD: vcpu control");
324                         break;
325                 case L4_THREAD_VCPU_CONTROL_EXT_OP:
326                         enter_kdebug("THREAD: vcpu control ext");
327                         break;
328                 case L4_THREAD_GDT_X86_OP:
329                         group->gdt(t, utcb);
330                         break;
331                 case L4_THREAD_SET_FS_AMD64_OP:
332                         enter_kdebug("THREAD: set fs amd64");
333                         break;
334                 default:
335                         ERROR() << "unknown thread op: " << std::hex << op;
336                         break;
337         }
338
339         return Romain::Observer::Replicatable;
340         //enter_kdebug("thread");
341 }
342
343
344 void Romain::SyscallObserver::handle_task(Romain::App_instance* i,
345                                           Romain::App_thread*   t,
346                                           Romain::App_model*    a)
347 {
348         l4_utcb_t   *utcb = reinterpret_cast<l4_utcb_t*>(t->remote_utcb());
349         l4_umword_t    op = l4_utcb_mr_u(utcb)->mr[0] & L4_THREAD_OPCODE_MASK;
350         switch(op) {
351                 case L4_TASK_UNMAP_OP:
352 #if 0
353                         MSGt(t) << "unmap";
354                         i->unmap(l4_utcb_mr_u(utcb)->mr[2]);
355 #endif
356                         break;
357                 case L4_TASK_CAP_INFO_OP:
358                         nullhandler.proxy_syscall(i,t,0,a);
359                         break;
360                 default:
361                         MSGt(t) << "Task system call";
362                         MSGt(t) << "UTCB @ " << std::hex << (unsigned)utcb << " op: " << op
363                                   << " cap " << (t->vcpu()->r()->dx & ~0xF) << " " << L4RE_THIS_TASK_CAP;
364                         t->print_vcpu_state();
365                         enter_kdebug("unknown task op?");
366                         break;
367         }
368 }
369
370
371 Romain::Observer::ObserverReturnVal
372 Romain::Factory::handle(Romain::App_instance* inst,
373                         Romain::App_thread* t,
374                         Romain::Thread_group* tg,
375                         Romain::App_model* am)
376 {
377         MSGt(t) << "Factory system call";
378         l4_utcb_t *utcb = reinterpret_cast<l4_utcb_t*>(t->remote_utcb());
379         MSGt(t) << "UTCB @ " << std::hex << (unsigned)utcb;
380         _check((l4_addr_t)utcb == ~0UL, "remote utcb ptr??");
381
382         l4_umword_t obj = l4_utcb_mr_u(utcb)->mr[0];
383         l4_umword_t cap = l4_utcb_br_u(utcb)->br[0] & ~L4_RCV_ITEM_SINGLE_CAP;
384         MSGt(t) << std::hex << L4_PROTO_THREAD;
385         MSGt(t) << "object type: " << std::hex << obj
386               << " cap: " << cap;
387
388         switch(obj) {
389                 case L4_PROTO_THREAD:
390                         create_thread(inst, t, tg, am, cap);
391                         return Romain::Observer::Replicatable;
392
393                 case L4_PROTO_IRQ:
394                         create_irq(inst, t, tg, am, cap);
395                         return Romain::Observer::Replicatable;
396
397                 case L4Re::Protocol::Dataspace:
398                         SyscallHandler::proxy_syscall(inst, t, tg, am);
399                         return Romain::Observer::Replicatable;
400
401                 default:
402                         break;
403         }
404
405         enter_kdebug("theObjectFactory");
406         return Romain::Observer::Finished;
407 }
408
409
410 Romain::Observer::ObserverReturnVal
411 Romain::Scheduling::handle(Romain::App_instance* inst,
412                            Romain::App_thread* t,
413                            Romain::Thread_group* tg,
414                            Romain::App_model* am)
415 {
416         l4_utcb_t *utcb = reinterpret_cast<l4_utcb_t*>(t->remote_utcb());
417         l4_umword_t op  = l4_utcb_mr_u(utcb)->mr[0];
418
419         MSGt(t) << "\033[32mschedule(" << std::hex << op << ")\033[0m";
420         if (op == L4_SCHEDULER_RUN_THREAD_OP) {
421                 l4_umword_t cap             = l4_utcb_mr_u(utcb)->mr[6] >> L4_CAP_SHIFT;
422                 Romain::Thread_group* group = theObjectFactory.thread_for_cap(cap);
423                 group->scheduler_run(t);
424         } else {
425                 //enter_kdebug("run_thread != 1");
426                 SyscallHandler::proxy_syscall(inst, t, tg, am);
427         }
428
429         return Romain::Observer::Replicatable;
430 }
431
432
433 Romain::Observer::ObserverReturnVal
434 Romain::IrqHandler::handle(Romain::App_instance* inst,
435                            Romain::App_thread* t,
436                            Romain::Thread_group* tg,
437                            Romain::App_model* am)
438 {
439         l4_utcb_t *utcb = reinterpret_cast<l4_utcb_t*>(t->remote_utcb());
440         unsigned op     = l4_utcb_mr_u(utcb)->mr[0];
441         unsigned label  = l4_utcb_mr_u(utcb)->mr[1];
442         unsigned cap    = t->vcpu()->r()->dx & L4_CAP_MASK;
443
444         L4::Cap<L4::Irq> irq(cap);
445
446         DEBUGt(t) << "IRQ: cap = " << std::hex << cap << " op = " << op; 
447
448         if (!theObjectFactory.is_irq(cap)) {
449                 SyscallHandler::proxy_syscall(inst, t, tg, am);
450                 return Romain::Observer::Replicatable;
451         }
452
453         switch(op) {
454                 /*
455                  * For attach(), we cannot simply redirect to the gate
456                  * agent, because we need to modify the thread that is
457                  * attached to the IRQ
458                  */
459                 case L4_IRQ_OP_ATTACH:
460                         {
461                                 l4_umword_t attach_cap      = l4_utcb_mr_u(utcb)->mr[3] & L4_FPAGE_ADDR_MASK;
462                                 DEBUG() << "attach " << std::hex << (attach_cap >> L4_CAP_SHIFT);
463                                 Romain::Thread_group *group = theObjectFactory.thread_for_cap(attach_cap >> L4_CAP_SHIFT);
464                                 l4_msgtag_t ret;
465
466                                 if (!group) {
467                                         ERROR() << "Unimplemented: Attaching someone else but myself!";
468                                         enter_kdebug();
469                                 }
470
471                                 ret = irq->attach(label, group->gateagent->listener_cap);
472
473                                 t->vcpu()->r()->ax = ret.raw;
474                                 DEBUG() << std::hex << ret.raw ;
475                                 return Romain::Observer::Replicatable;
476                         }
477                         break;
478
479                 case L4_IRQ_OP_TRIGGER:
480                         DEBUGt(t) << ":: trigger";
481                         //enter_kdebug("trigger");
482                         irq->trigger();
483                         break;
484                 case L4_IRQ_OP_EOI:
485                         DEBUGt(t) << ":: eoi";
486                         tg->gateagent->trigger_agent(t);
487                         break;
488
489                 case L4_IRQ_OP_CHAIN:
490                         enter_kdebug("irq::chain?");
491         }
492
493         return Romain::Observer::Replicatable;
494 }