]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/plr/server/src/fault_handlers/syscalls.cc
e59714175f413250440d748e595e854f08bbb3f7
[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                 t->vcpu()->r()->ax = 0;
271         }
272
273         //t->print_vcpu_state();
274         //Romain::dump_mem((unsigned*)utcb, 40);
275
276         return Romain::Observer::Replicatable;
277 }
278
279
280 Romain::Observer::ObserverReturnVal
281 Romain::ThreadHandler::handle(Romain::App_instance *,
282                               Romain::App_thread* t,
283                               Romain::Thread_group * tg,
284                               Romain::App_model *am)
285 {
286         MSGt(t) << "Thread system call";
287         l4_utcb_t *utcb = reinterpret_cast<l4_utcb_t*>(t->remote_utcb());
288         MSGt(t) << "UTCB @ " << std::hex << (unsigned)utcb;
289         _check((l4_addr_t)utcb == ~0UL, "remote utcb ptr??");
290
291         //t->print_vcpu_state();
292         //Romain::dump_mem((unsigned*)utcb, 40);
293
294         l4_umword_t op   = l4_utcb_mr_u(utcb)->mr[0] & L4_THREAD_OPCODE_MASK;
295         l4_umword_t dest = t->vcpu()->r()->dx & L4_CAP_MASK;
296
297         DEBUG() << "dest cap " << std::hex << dest << " " << L4_INVALID_CAP;
298         Romain::Thread_group* group = (Romain::Thread_group*)0xdeadbeef;
299         if (dest == L4_INVALID_CAP) {
300                 group = tg;
301         } else {
302                 group = theObjectFactory.thread_for_cap(dest >> L4_CAP_SHIFT);
303         }
304         DEBUG() << "tgroup " << group;
305
306         switch(op) {
307                 case L4_THREAD_CONTROL_OP:
308                         group->control(t, utcb, am);
309                         return Romain::Observer::Replicatable;
310                 case L4_THREAD_EX_REGS_OP:
311                         group->ex_regs(t);
312                         break;
313                 case L4_THREAD_SWITCH_OP:
314                         enter_kdebug("THREAD: switch");
315                         break;
316                 case L4_THREAD_STATS_OP:
317                         enter_kdebug("THREAD: stats");
318                         break;
319                 case L4_THREAD_VCPU_RESUME_OP:
320                         enter_kdebug("THREAD: vcpu_resume");
321                         break;
322                 case L4_THREAD_REGISTER_DELETE_IRQ_OP:
323                         enter_kdebug("THREAD: irq");
324                         break;
325                 case L4_THREAD_MODIFY_SENDER_OP:
326                         enter_kdebug("THREAD: modify sender");
327                         break;
328                 case L4_THREAD_VCPU_CONTROL_OP:
329                         enter_kdebug("THREAD: vcpu control");
330                         break;
331                 case L4_THREAD_VCPU_CONTROL_EXT_OP:
332                         enter_kdebug("THREAD: vcpu control ext");
333                         break;
334                 case L4_THREAD_GDT_X86_OP:
335                         group->gdt(t, utcb);
336                         break;
337                 case L4_THREAD_SET_FS_AMD64_OP:
338                         enter_kdebug("THREAD: set fs amd64");
339                         break;
340                 default:
341                         ERROR() << "unknown thread op: " << std::hex << op;
342                         break;
343         }
344
345         return Romain::Observer::Replicatable;
346         //enter_kdebug("thread");
347 }
348
349
350 void Romain::SyscallObserver::handle_task(Romain::App_instance* i,
351                                           Romain::App_thread*   t,
352                                           Romain::App_model*    a)
353 {
354         l4_utcb_t   *utcb = reinterpret_cast<l4_utcb_t*>(t->remote_utcb());
355         l4_umword_t    op = l4_utcb_mr_u(utcb)->mr[0] & L4_THREAD_OPCODE_MASK;
356         switch(op) {
357                 case L4_TASK_UNMAP_OP:
358 #if 0
359                         MSGt(t) << "unmap";
360                         i->unmap(l4_utcb_mr_u(utcb)->mr[2]);
361 #endif
362                         break;
363                 case L4_TASK_CAP_INFO_OP:
364                         nullhandler.proxy_syscall(i,t,0,a);
365                         break;
366                 default:
367                         MSGt(t) << "Task system call";
368                         MSGt(t) << "UTCB @ " << std::hex << (unsigned)utcb << " op: " << op
369                                   << " cap " << (t->vcpu()->r()->dx & ~0xF) << " " << L4RE_THIS_TASK_CAP;
370                         t->print_vcpu_state();
371                         enter_kdebug("unknown task op?");
372                         break;
373         }
374 }
375
376
377 Romain::Observer::ObserverReturnVal
378 Romain::Factory::handle(Romain::App_instance* inst,
379                         Romain::App_thread* t,
380                         Romain::Thread_group* tg,
381                         Romain::App_model* am)
382 {
383         MSGt(t) << "Factory system call";
384         l4_utcb_t *utcb = reinterpret_cast<l4_utcb_t*>(t->remote_utcb());
385         MSGt(t) << "UTCB @ " << std::hex << (unsigned)utcb;
386         _check((l4_addr_t)utcb == ~0UL, "remote utcb ptr??");
387
388         l4_umword_t obj = l4_utcb_mr_u(utcb)->mr[0];
389         l4_umword_t cap = l4_utcb_br_u(utcb)->br[0] & ~L4_RCV_ITEM_SINGLE_CAP;
390         MSGt(t) << std::hex << L4_PROTO_THREAD;
391         MSGt(t) << "object type: " << std::hex << obj
392               << " cap: " << cap;
393
394         switch(obj) {
395                 case L4_PROTO_THREAD:
396                         create_thread(inst, t, tg, am, cap);
397                         return Romain::Observer::Replicatable;
398
399                 case L4_PROTO_IRQ:
400                         create_irq(inst, t, tg, am, cap);
401                         return Romain::Observer::Replicatable;
402
403                 case L4Re::Protocol::Dataspace:
404                         SyscallHandler::proxy_syscall(inst, t, tg, am);
405                         return Romain::Observer::Replicatable;
406
407                 default:
408                         break;
409         }
410
411         enter_kdebug("theObjectFactory");
412         return Romain::Observer::Finished;
413 }
414
415
416 Romain::Observer::ObserverReturnVal
417 Romain::Scheduling::handle(Romain::App_instance* inst,
418                            Romain::App_thread* t,
419                            Romain::Thread_group* tg,
420                            Romain::App_model* am)
421 {
422         l4_utcb_t *utcb = reinterpret_cast<l4_utcb_t*>(t->remote_utcb());
423         l4_umword_t op  = l4_utcb_mr_u(utcb)->mr[0];
424
425         MSGt(t) << "\033[32mschedule(" << std::hex << op << ")\033[0m";
426         if (op == L4_SCHEDULER_RUN_THREAD_OP) {
427                 l4_umword_t cap             = l4_utcb_mr_u(utcb)->mr[6] >> L4_CAP_SHIFT;
428                 Romain::Thread_group* group = theObjectFactory.thread_for_cap(cap);
429                 group->scheduler_run(t);
430         } else {
431                 //enter_kdebug("run_thread != 1");
432                 SyscallHandler::proxy_syscall(inst, t, tg, am);
433         }
434
435         return Romain::Observer::Replicatable;
436 }
437
438
439 Romain::Observer::ObserverReturnVal
440 Romain::IrqHandler::handle(Romain::App_instance* inst,
441                            Romain::App_thread* t,
442                            Romain::Thread_group* tg,
443                            Romain::App_model* am)
444 {
445         l4_utcb_t *utcb = reinterpret_cast<l4_utcb_t*>(t->remote_utcb());
446         unsigned op     = l4_utcb_mr_u(utcb)->mr[0];
447         unsigned label  = l4_utcb_mr_u(utcb)->mr[1];
448         unsigned cap    = t->vcpu()->r()->dx & L4_CAP_MASK;
449
450         L4::Cap<L4::Irq> irq(cap);
451
452         DEBUGt(t) << "IRQ: cap = " << std::hex << cap << " op = " << op; 
453
454         if (!theObjectFactory.is_irq(cap)) {
455                 SyscallHandler::proxy_syscall(inst, t, tg, am);
456                 return Romain::Observer::Replicatable;
457         }
458
459         switch(op) {
460                 /*
461                  * For attach(), we cannot simply redirect to the gate
462                  * agent, because we need to modify the thread that is
463                  * attached to the IRQ
464                  */
465                 case L4_IRQ_OP_ATTACH:
466                         {
467                                 l4_umword_t attach_cap      = l4_utcb_mr_u(utcb)->mr[3] & L4_FPAGE_ADDR_MASK;
468                                 DEBUG() << "attach " << std::hex << (attach_cap >> L4_CAP_SHIFT);
469                                 Romain::Thread_group *group = theObjectFactory.thread_for_cap(attach_cap >> L4_CAP_SHIFT);
470                                 l4_msgtag_t ret;
471
472                                 if (!group) {
473                                         ERROR() << "Unimplemented: Attaching someone else but myself!";
474                                         enter_kdebug();
475                                 }
476
477                                 ret = irq->attach(label, group->gateagent->listener_cap);
478
479                                 t->vcpu()->r()->ax = ret.raw;
480                                 DEBUG() << std::hex << ret.raw ;
481                                 return Romain::Observer::Replicatable;
482                         }
483                         break;
484
485                 case L4_IRQ_OP_TRIGGER:
486                         DEBUGt(t) << ":: trigger";
487                         //enter_kdebug("trigger");
488                         irq->trigger();
489                         break;
490                 case L4_IRQ_OP_EOI:
491                         DEBUGt(t) << ":: eoi";
492                         tg->gateagent->trigger_agent(t);
493                         break;
494
495                 case L4_IRQ_OP_CHAIN:
496                         enter_kdebug("irq::chain?");
497         }
498
499         return Romain::Observer::Replicatable;
500 }