]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/plr/server/src/emulation.cc
update
[l4.git] / l4 / pkg / plr / server / src / emulation.cc
1 /*
2  * emulation.cc --
3  *
4  *     Implementation of the write instruction emulator.
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 "exceptions"
15 #include "memory"
16 #include "emulation"
17
18 #include <l4/sys/kdebug.h>
19
20 #define MSG() DEBUGf(Romain::Log::Emulator)
21
22 /*
23  * Debugging: get human-readable operand type
24  */
25 static char const *operand_type_string(ud_operand_t *op)
26 {
27         switch (op->type) {
28                 case UD_OP_REG: return "register";
29                 case UD_OP_MEM: return "memory";
30                 case UD_OP_PTR: return "pointer";
31                 case UD_OP_IMM: return "immediate";
32                 case UD_OP_JIMM: return "immediate jmp target";
33                 case UD_OP_CONST: return "constant";
34                 default: return "invalid";
35         }
36 }
37
38
39 void Romain::Emulator_base::init_ud()
40 {
41         ud_init(&_ud);
42         ud_set_mode(&_ud, 32);
43         ud_set_syntax(&_ud, UD_SYN_INTEL);
44
45         ud_set_pc(&_ud, ip());
46         ud_set_input_buffer(&_ud, (unsigned char*)_local_ip, 32);
47
48         int num_bytes = ud_disassemble(&_ud);
49 #if 0
50         MSG() << "print_instruction "
51                                   << num_bytes << " byte"
52                                   << (num_bytes > 1 ? "s" : "") << ".";
53 #endif
54 }
55
56 /*
57  * Romain::Emulator constructor
58  *
59  * Nothing fancy -- use of udis86 should be hidden behind another
60  * property. XXX
61  */
62 Romain::Emulator_base::Emulator_base(L4vcpu::Vcpu *vcpu,
63                                      Romain::AddressTranslator const *trans)
64         : _vcpu(vcpu), _translator(trans)
65 {
66         _local_ip = _translator->translate(ip());
67         init_ud();
68 }
69
70
71 /*
72  * Get register value from VCPU
73  *
74  * Returns an MWord even if the real operand is only 16 or 8 bit.
75  */
76 l4_umword_t Romain::Emulator_base::register_to_value(ud_type op)
77 {
78         l4_umword_t  val = ~0;
79
80 #define REG(udis_name, vcpu_name, target, ...) \
81         case UD_R_##udis_name: target = _vcpu->r()->vcpu_name __VA_ARGS__; break
82
83         switch(op) {
84                 REG(AL, ax, val, & 0xFF); REG(CL, cx, val, & 0xFF); REG(DL, dx, val, & 0xFF);
85                 REG(BL, bx, val, & 0xFF); REG(SPL, sp, val, & 0xFF); REG(BPL, bp, val, & 0xFF);
86                 REG(SIL, si, val, & 0xFF); REG(DIL, di, val, & 0xFF);
87                 
88 #if 0
89                 REG(AH, ax, val, & 0xFF00); REG(CH, cx, val, & 0xFF00); REG(DH, dx, val, & 0xFF00);
90                 REG(BH, bx, val, & 0xFF00);
91 #endif
92
93                 REG(AX, ax, val, & 0xFFFF); REG(CX, cx, val, & 0xFFFF); REG(DX, dx, val, & 0xFFFF);
94                 REG(BX, bx, val, & 0xFFFF); REG(SP, sp, val, & 0xFFFF); REG(BP, bp, val, & 0xFFFF);
95                 REG(SI, si, val, & 0xFFFF); REG(DI, di, val, & 0xFFFF);
96
97                 REG(EAX, ax, val); REG(ECX, cx, val); REG(EDX, dx, val);
98                 REG(EBX, bx, val); REG(ESP, sp, val); REG(EBP, bp, val);
99                 REG(ESI, si, val); REG(EDI, di, val);
100
101                 default: 
102                         MSG() << "target register: " << std::hex << op;
103                         enter_kdebug("unhandled register target");
104                         break;
105         }
106 #undef REG
107         return val;
108 }
109
110
111 void Romain::Emulator_base::value_to_register(l4_umword_t val, ud_type op)
112 {
113 #define REG(udis_name, vcpu_name, ...) \
114         case UD_R_##udis_name: _vcpu->r()->vcpu_name = val __VA_ARGS__; break;
115
116         switch(op) {
117                 REG(AL, ax, & 0xFF); REG(CL, cx, & 0xFF); REG(DL, dx, & 0xFF);
118                 REG(BL, bx, & 0xFF); REG(SPL, sp, & 0xFF); REG(BPL, bp, & 0xFF);
119                 REG(SIL, si, & 0xFF); REG(DIL, di, & 0xFF);
120
121                 REG(AX, ax, & 0xFFFF); REG(CX, cx, & 0xFFFF); REG(DX, dx, & 0xFFFF);
122                 REG(BX, bx, & 0xFFFF); REG(SP, sp, & 0xFFFF); REG(BP, bp, & 0xFFFF);
123                 REG(SI, si, & 0xFFFF); REG(DI, di, & 0xFFFF);
124
125                 REG(EAX, ax); REG(ECX, cx); REG(EDX, dx);
126                 REG(EBX, bx); REG(ESP, sp); REG(EBP, bp);
127                 REG(ESI, si); REG(EDI, di);
128                 
129                 default:
130                         MSG() << "target register: " << std::hex << op;
131                         enter_kdebug("unhandled register target");
132                         break;
133         }
134
135 #undef REG
136 }
137
138
139 /*
140  * Calculate the value for an operand
141  *
142  * Note, this always returns an MWord. Users need to check op->size
143  * to determine what to do with the value.
144  */
145 l4_umword_t Romain::Emulator_base::operand_to_value(ud_operand_t *op)
146 {
147         bool valid = false;
148         l4_umword_t val = ~0;
149
150         switch(op->type) {
151                 // Operand is a register. The specific register is contained in
152                 // base in the form of an enumerated constant, enum ud_type.
153                 case UD_OP_REG:
154                         {
155                                 char buf[80];
156                                 snprintf(buf, 80, "reg b %02x idx %02x scale %02x offs %02x",
157                                          op->base, op->index, op->scale, op->offset);
158                                 MSG() << buf;
159
160                                 // addr: = base + index * scale + offset
161
162                                 _check(op->scale != 0, "!! implement register scaling");
163                                 _check(op->offset != 0, "!! implement register offset");
164
165                                 l4_umword_t idx = op->index ? register_to_value(op->index) : 0;
166                                 l4_umword_t bas = op->base ? register_to_value(op->base) : 0;
167
168                                 val = bas + idx * op->scale;
169                                 if (op->offset)
170                                         val += op->lval.sdword;
171
172                                 MSG() << "val = " << std::hex << val;
173
174                                 valid = true;
175                         }
176                         break;
177
178                 // Immediate operand. Value available in lval.
179                 case UD_OP_IMM:
180                         {
181                                 MSG() << "op sz " << (int)op->size
182                                         << "op val " << std::hex << op->lval.uqword;
183                                 val = op->lval.udword;
184
185                                 switch (op->size) {
186                                         case 8: val &= 0xFF; break;
187                                         case 16: val &= 0xFFFF; break;
188                                         default: MSG() << "strange op size: " << op->size;
189                                         case 32: break;
190                                 }
191
192                                 valid = true;
193                         }
194                         break;
195
196                 // Memory operand. The intermediate form normalizes all memory
197                 // address equations to the scale-index-base form. The address
198                 // equation is availabe in base, index, and scale. If the offset
199                 // field has a non-zero value (one of 8, 16, 32, and 64), lval
200                 // will contain the memory offset. Note that base and index fields
201                 // contain the base and index register of the address equation,
202                 // in the form of an enumerated constant enum ud_type. scale
203                 // contains an integer value that the index register must be
204                 // scaled by.
205                 case UD_OP_MEM:
206                         {
207                                 char buf[80];
208                                 long long offset = 0;
209                                 snprintf(buf, 80, "mem b %02x idx %02x scale %02x offs %02x",
210                                          op->base, op->index, op->scale, op->offset);
211
212                                 MSG() << buf;
213
214                                 _check(op->scale != 0, "!! implement register scaling");
215
216                                 l4_umword_t addr = register_to_value(op->base);
217                                 MSG() << "    reg " << std::hex << addr;
218
219                                 switch(op->offset) {
220                                         case 0:  offset = 0; break;
221                                         case 8:  offset = op->lval.sbyte; break;
222                                         case 16: offset = op->lval.sword; break;
223                                         case 32: offset = op->lval.sdword; break;
224                                         case 64: offset = op->lval.sqword; enter_kdebug("64bit offset"); break;
225                                         default: enter_kdebug("unknown offset??");
226                                 }
227
228                                 MSG() << std::hex << addr << " + " << offset << " = " << addr + offset;
229                                 addr += offset;
230
231                                 // reading a full mword here is okay, because users of the
232                                 // results returned from this function need to check the real
233                                 // operand size anyway
234                                 val = *(l4_umword_t*)_translator->translate(addr);
235
236                                 MSG() << std::hex << addr << " := " << val;
237
238                                 valid = true;
239                         }
240
241                         break;
242                 default:
243                         MSG() << "Need to handle " << operand_type_string(op);
244                         enter_kdebug("unhandled src operand type");
245                         break;
246         }
247
248         MSG() << std::hex << "v " << val
249                 << (valid ? " (ok)" : " \033[31;1m(INV!)\033[0m")
250                 << " ilen " << ilen();
251
252         if (!valid)
253                 enter_kdebug("unhandled operand type");
254         return val;
255 }
256
257
258 /*
259  * Extract the offset encoded in an operand.
260  *
261  * This incorporates looking at the operand's size to figure out
262  * the right masking. Plus, the result is _SIGNED_!
263  */
264 int Romain::Emulator_base::offset_from_operand(ud_operand_t *op)
265 {
266         uint8_t offs    = op->offset;
267         long long value = 0;
268         bool neg        = false;
269         
270         if (!offs) return op->lval.sword;
271
272         /*
273          * Mask only the lower N bits
274          */
275         value = op->lval.sdword & ((1LL << offs) - 1);
276         neg   = value & (1LL << (offs-1));
277         if (neg) {
278                 value = -((1LL << offs) - value);
279         }
280
281         // XXX: so far, we don't support 64bit offsets...
282         return (int)value;
283 }
284
285
286 /*
287  * Given a value, write it to whatever target is described by the
288  * operand.
289  *
290  * So far, only memory targets are needed as we don't get to see writes to
291  * other stuff, such as registers.
292  */
293 void Romain::Emulator_base::value_to_operand(l4_umword_t val, ud_operand_t *op)
294 {
295         switch(op->type) {
296                 // Memory operand. The intermediate form normalizes all memory
297                 // address equations to the scale-index-base form. The address
298                 // equation is availabe in base, index, and scale. If the offset
299                 // field has a non-zero value (one of 8, 16, 32, and 64), lval
300                 // will contain the memory offset. Note that base and index fields
301                 // contain the base and index register of the address equation,
302                 // in the form of an enumerated constant enum ud_type. scale
303                 // contains an integer value that the index register must be
304                 // scaled by.
305                 //
306                 // addr: = base + index * scale + offset
307                 case UD_OP_MEM:
308                         {
309                                 char buf[80];
310                                 snprintf(buf, 80, "b %02x idx %02x scale %02x offs %02x",
311                                          op->base, op->index, op->scale, op->offset);
312                                 MSG() << buf;
313
314                                 // no base reg, 32 bit size -> this is an address
315                                 if (!op->base && op->offset) {
316                                         l4_addr_t target = _translator->translate(op->lval.sdword);
317                                         MSG() << std::hex
318                                                 << "writing to address: (r " << op->lval.sdword
319                                                 << " l " << target << ") := " << val;
320                                         *(l4_umword_t*)target = val;
321                                 }
322                                 else if (op->base) { // else there must be at least a base addr
323                                         l4_umword_t b_addr = register_to_value(op->base);
324                                         MSG() << "BASE: " << std::hex << b_addr;
325                                         l4_umword_t i_addr = op->index ? register_to_value(op->index) : 0;
326                                         l4_umword_t scale  = op->scale;
327
328                                         MSG() << std::hex << b_addr << " + (" << i_addr << " << " << scale << ") + "
329                                                 << op->lval.sword << " = " << b_addr + (i_addr << scale) + offset_from_operand(op);
330                                         b_addr = b_addr + (i_addr << scale) + offset_from_operand(op);
331
332                                         l4_addr_t target = _translator->translate(b_addr);
333                                         MSG() << "target: " << std::hex << target;
334                                         // XXX: error check
335                                         MSG() << std::hex
336                                                 << "writing to address: (r " << b_addr
337                                                 << " l " << target << ") := " << val;
338                                         write_target(target, val, op->size);
339                                 }
340                                 else {
341                                         MSG() << "strange mem encoding??";
342                                         enter_kdebug("!");
343                                 }
344                         }
345                         break;
346                 default:
347                         MSG() << "Need to handle " << operand_type_string(op);
348                         enter_kdebug("unhandled target operand");
349                         break;
350         }
351 }
352
353
354 /*
355  * Handle PUSH instruction
356  */
357 void Romain::WriteEmulator::handle_push()
358 {
359         l4_umword_t val = ~0;
360         ud_operand_t *op = &_ud.operand[0];
361
362         val = operand_to_value(op);
363
364         Romain::Stack(_translator->translate(_vcpu->r()->sp)).push(val);
365
366         _vcpu->r()->sp -= sizeof(l4_umword_t);
367         _vcpu->r()->ip += ilen();
368 }
369
370
371 /*
372  * Emulate a CALL instruction
373  */
374 void Romain::WriteEmulator::handle_call()
375 {
376         // push return address
377         _vcpu->r()->ip += ilen();
378         Romain::Stack(_translator->translate(_vcpu->r()->sp)).push(ip());
379
380         // adapt EIP
381         ud_operand_t *op = &_ud.operand[0];
382
383         // XXX: check later, if this can be moved into operand_to_value(), too
384         switch(op->type) {
385                 case UD_OP_JIMM:
386                         _check(op->size != 32, "!! immediate jmp offset not an mword");
387                         MSG() << std::hex << op->lval.sdword
388                               << " " << _vcpu->r()->ip + op->lval.sdword;
389                         _vcpu->r()->ip += op->lval.sdword;
390                         break;
391
392                 case UD_OP_MEM: // fallthrough
393                 case UD_OP_REG:
394                         {
395                                 l4_umword_t v = operand_to_value(op);
396                                 MSG() << std::hex << v;
397                                 _vcpu->r()->ip = v;
398                         }
399                         break;
400
401                 default:
402                         MSG() << "Unhandled: " << operand_type_string(op);
403                         enter_kdebug("unhandled target");
404                         break;
405         }
406
407         /* We must not touch the SP _before_ looking at the immediate value,
408          * because otherwise offset calculations might be wrong.
409          */
410         _vcpu->r()->sp -= sizeof(l4_umword_t);
411 }
412
413
414 /*
415  * Handle MOV instruction
416  */
417 void Romain::WriteEmulator::handle_mov()
418 {
419         ud_operand_t *op1 = &_ud.operand[0];
420         ud_operand_t *op2 = &_ud.operand[1];
421
422         l4_umword_t val = operand_to_value(op2);
423         value_to_operand(val, op1);
424
425         _vcpu->r()->ip += ilen();
426 }
427
428
429 /*
430  * Handle (REP:)STOS instruction
431  */
432 void Romain::WriteEmulator::handle_stos()
433 {
434         _check(_ud.mnemonic != UD_Istosd, "non-word string copy");
435
436         unsigned count = _ud.pfx_rep != UD_NONE ? _vcpu->r()->cx : 1;
437
438         MSG() << std::hex << "rep = 0x" << (int)_ud.pfx_rep;
439         MSG() << "iterations: " << count;
440
441         l4_addr_t base = _vcpu->r()->di;
442         base = _translator->translate(base);
443
444         for (unsigned idx = 0; idx < count; ++idx) {
445                 *(l4_umword_t*)base = _vcpu->r()->ax;
446
447                 // XXX: Handle
448                 // 1) other stos-sizes than 4
449                 // 2) direction flag
450                 base += 4;
451         }
452
453         if (_ud.pfx_rep != UD_NONE) // we're done rep'ing
454                 _vcpu->r()->cx = 0;
455
456         _vcpu->r()->di += count * sizeof(l4_umword_t);
457
458         _vcpu->r()->ip += ilen();
459 }
460
461
462 /*
463  * Handle MOVSD instruction
464  */
465 void Romain::WriteEmulator::handle_movsd()
466 {
467         _check(_ud.mnemonic != UD_Imovsd, "non-word memcopy");
468
469         unsigned count = _ud.pfx_rep != UD_NONE ? _vcpu->r()->cx : 1;
470         MSG() << std::hex << "rep = 0x" << (int)_ud.pfx_rep;
471         MSG() << "iterations: " << count;
472
473         l4_addr_t src = _translator->translate(_vcpu->r()->si);
474         l4_addr_t dst = _translator->translate(_vcpu->r()->di);
475
476         for (unsigned idx = 0; idx < count; ++idx) {
477                 *(l4_umword_t*)dst = *(l4_umword_t*)src;
478                 dst += 4;
479                 src += 4;
480         }
481
482         if (_ud.pfx_rep != UD_NONE) // we're done rep'ing
483                 _vcpu->r()->cx = 0;
484         _vcpu->r()->si += count * sizeof(l4_umword_t);
485         _vcpu->r()->di += count * sizeof(l4_umword_t);
486
487         _vcpu->r()->ip += ilen();
488 }
489
490
491 /*
492  * Handle MOVSB instruction
493  */
494 void Romain::WriteEmulator::handle_movsb()
495 {
496         _check(_ud.mnemonic != UD_Imovsb, "non-byte memcopy");
497
498         unsigned count = _ud.pfx_rep != UD_NONE ? _vcpu->r()->cx : 1;
499         MSG() << std::hex << "rep = 0x" << (int)_ud.pfx_rep;
500         MSG() << "iterations: " << count;
501
502         l4_addr_t src = _translator->translate(_vcpu->r()->si);
503         l4_addr_t dst = _translator->translate(_vcpu->r()->di);
504
505         for (unsigned idx = 0; idx < count; ++idx) {
506                 *(unsigned char*)dst = *(unsigned char*)src;
507                 dst++;
508                 src++;
509         }
510
511         if (_ud.pfx_rep != UD_NONE) // we're done rep'ing
512                 _vcpu->r()->cx = 0;
513         _vcpu->r()->si += count * sizeof(unsigned char);
514         _vcpu->r()->di += count * sizeof(unsigned char);
515
516         _vcpu->r()->ip += ilen();
517 }
518
519
520 /*
521  * Handle arithmetic instructions
522  *
523  * Arithmetics modify EFLAGS, too...
524  */
525 void Romain::WriteEmulator::handle_arithmetics(ArithmeticOperations op)
526 {
527         static char const *opstr[] = {"+", "-", "*", "/", "%", "--"};
528
529         ud_operand_t *op1  = &_ud.operand[0];
530         ud_operand_t *op2  = NULL; //  = &_ud.operand[1];
531         l4_umword_t orig   = operand_to_value(op1);
532         l4_umword_t arval  = 0; // operand_to_value(op2);
533         l4_umword_t flags  = 0;
534
535         switch(op) {
536                 case ADD:
537                 case SUB:
538                 case MULT:
539                 case DIV:
540                 case MOD:
541                         op2   = &_ud.operand[1];
542                         arval = operand_to_value(op2);
543                         break;
544                 case DEC:
545                         arval = 1;
546                         op = SUB;
547         }
548
549         MSG() << "value: " << std::hex << orig
550                                   << opstr[op] << arval << " = ";
551
552         switch (op) {
553                 case ADD:  orig += arval; break;
554                 case SUB:  orig -= arval; break;
555                 case MULT: orig *= arval; break;
556                 case DIV:  orig /= arval; break;
557                 case MOD:  orig %= arval; break;
558                 default: enter_kdebug("unknown arith op"); break;
559         }
560
561         /*
562          * Now obtain the flags and insert them into the
563          * already set flags.
564          */
565         asm volatile ("pushf\n\t"
566                       "pop %0"
567                       : "=r" (flags));
568         /* First, we plain copy the lowest 8 bits. */
569         _vcpu->r()->flags = (_vcpu->r()->flags & 0xFFFFFF00) | (flags & 0xFF);
570         /* The next three would be IF, TF, DF, which we don't want to touch.
571          * The remaining OF needs to be put in, though.
572          */
573         if (flags & OverflowFlag) {
574                 _vcpu->r()->flags |= OverflowFlag;
575         } else {
576                 _vcpu->r()->flags &= ~OverflowFlag;
577         }
578
579         MSG() << std::hex << "flags " << flags << " result " << orig;
580
581         value_to_operand(orig, op1);
582
583         _vcpu->r()->ip += ilen();
584         MSG() << std::hex << "vcpu.eflags = " << _vcpu->r()->flags;
585         //enter_kdebug("arith");
586 }
587
588 /*
589  * Emulation entry point
590  */
591 void Romain::WriteEmulator::emulate()
592 {
593         //print_instruction(); // debugging
594 #define HANDLE(val, fn) \
595         case val: fn(); break;
596 #define HANDLE_1(val, fn, arg) \
597         case val: fn(arg); break;
598
599         switch(_ud.mnemonic) {
600                 HANDLE(UD_Icall,  handle_call);
601                 HANDLE(UD_Imov,   handle_mov);
602                 HANDLE(UD_Ipush,  handle_push);
603                 HANDLE(UD_Istosd, handle_stos);
604                 HANDLE(UD_Imovsd, handle_movsd);
605                 HANDLE(UD_Imovsb, handle_movsb); // XXX merge with movsd
606                 HANDLE_1(UD_Isub, handle_arithmetics, SUB);
607                 HANDLE_1(UD_Idec, handle_arithmetics, DEC);
608                 default:
609                         MSG() << _ud.mnemonic;
610                         enter_kdebug("unhandled mnemonic");
611                         break;
612         }
613 #undef HANDLE
614 #undef HANDLE_1
615 }
616
617
618 void Romain::Emulator_base::print_instruction()
619 {
620         INFO() << "INSTR(" << std::setw(16) << ud_insn_hex(&_ud) << ") "
621                << std::setw(20) << ud_insn_asm(&_ud);
622 }