4 * Implementation of the write instruction emulator.
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.
19 #include <l4/sys/kdebug.h>
21 #define MSG() DEBUGf(Romain::Log::Emulator)
24 * Debugging: get human-readable operand type
26 static char const *operand_type_string(ud_operand_t *op)
29 case UD_OP_REG: return "register";
30 case UD_OP_MEM: return "memory";
31 case UD_OP_PTR: return "pointer";
32 case UD_OP_IMM: return "immediate";
33 case UD_OP_JIMM: return "immediate jmp target";
34 case UD_OP_CONST: return "constant";
35 default: return "invalid";
40 void Romain::Emulator_base::init_ud()
43 ud_set_mode(&_ud, 32);
44 ud_set_syntax(&_ud, UD_SYN_INTEL);
46 ud_set_pc(&_ud, ip());
47 ud_set_input_buffer(&_ud, (l4_uint8_t*)_local_ip, 32);
49 l4_mword_t num_bytes = ud_disassemble(&_ud);
52 MSG() << "print_instruction "
53 << num_bytes << " byte"
54 << (num_bytes > 1 ? "s" : "") << ".";
59 * Romain::Emulator constructor
61 * Nothing fancy -- use of udis86 should be hidden behind another
64 Romain::Emulator_base::Emulator_base(L4vcpu::Vcpu *vcpu,
65 Romain::AddressTranslator const *trans)
66 : _vcpu(vcpu), _translator(trans)
68 _local_ip = _translator->translate(ip());
74 * Get register value from VCPU
76 * Returns an MWord even if the real operand is only 16 or 8 bit.
78 l4_umword_t Romain::Emulator_base::register_to_value(ud_type op)
82 #define REG(udis_name, vcpu_name, target, ...) \
83 case UD_R_##udis_name: target = _vcpu->r()->vcpu_name __VA_ARGS__; break
86 REG(AL, ax, val, & 0xFF); REG(CL, cx, val, & 0xFF); REG(DL, dx, val, & 0xFF);
87 REG(BL, bx, val, & 0xFF); REG(SPL, sp, val, & 0xFF); REG(BPL, bp, val, & 0xFF);
88 REG(SIL, si, val, & 0xFF); REG(DIL, di, val, & 0xFF);
91 REG(AH, ax, val, & 0xFF00); REG(CH, cx, val, & 0xFF00); REG(DH, dx, val, & 0xFF00);
92 REG(BH, bx, val, & 0xFF00);
95 REG(AX, ax, val, & 0xFFFF); REG(CX, cx, val, & 0xFFFF); REG(DX, dx, val, & 0xFFFF);
96 REG(BX, bx, val, & 0xFFFF); REG(SP, sp, val, & 0xFFFF); REG(BP, bp, val, & 0xFFFF);
97 REG(SI, si, val, & 0xFFFF); REG(DI, di, val, & 0xFFFF);
99 REG(EAX, ax, val); REG(ECX, cx, val); REG(EDX, dx, val);
100 REG(EBX, bx, val); REG(ESP, sp, val); REG(EBP, bp, val);
101 REG(ESI, si, val); REG(EDI, di, val);
104 MSG() << "target register: " << std::hex << op;
105 enter_kdebug("unhandled register target");
113 void Romain::Emulator_base::value_to_register(l4_umword_t val, ud_type op)
115 #define REG(udis_name, vcpu_name, ...) \
116 case UD_R_##udis_name: _vcpu->r()->vcpu_name = val __VA_ARGS__; break;
119 REG(AL, ax, & 0xFF); REG(CL, cx, & 0xFF); REG(DL, dx, & 0xFF);
120 REG(BL, bx, & 0xFF); REG(SPL, sp, & 0xFF); REG(BPL, bp, & 0xFF);
121 REG(SIL, si, & 0xFF); REG(DIL, di, & 0xFF);
123 REG(AX, ax, & 0xFFFF); REG(CX, cx, & 0xFFFF); REG(DX, dx, & 0xFFFF);
124 REG(BX, bx, & 0xFFFF); REG(SP, sp, & 0xFFFF); REG(BP, bp, & 0xFFFF);
125 REG(SI, si, & 0xFFFF); REG(DI, di, & 0xFFFF);
127 REG(EAX, ax); REG(ECX, cx); REG(EDX, dx);
128 REG(EBX, bx); REG(ESP, sp); REG(EBP, bp);
129 REG(ESI, si); REG(EDI, di);
132 MSG() << "target register: " << std::hex << op;
133 enter_kdebug("unhandled register target");
142 * Calculate the value for an operand
144 * Note, this always returns an MWord. Users need to check op->size
145 * to determine what to do with the value.
147 l4_umword_t Romain::Emulator_base::operand_to_value(ud_operand_t *op)
150 l4_umword_t val = ~0;
153 // Operand is a register. The specific register is contained in
154 // base in the form of an enumerated constant, enum ud_type.
158 snprintf(buf, 80, "reg b %02x idx %02x scale %02x offs %02x",
159 op->base, op->index, op->scale, op->offset);
162 // addr: = base + index * scale + offset
164 _check(op->scale != 0, "!! implement register scaling");
165 _check(op->offset != 0, "!! implement register offset");
167 l4_umword_t idx = op->index ? register_to_value(op->index) : 0;
168 l4_umword_t bas = op->base ? register_to_value(op->base) : 0;
170 val = bas + idx * op->scale;
172 val += op->lval.sdword;
174 MSG() << "val = " << std::hex << val;
180 // Immediate operand. Value available in lval.
183 MSG() << "op sz " << (int)op->size
184 << "op val " << std::hex << op->lval.uqword;
185 val = op->lval.udword;
188 case 8: val &= 0xFF; break;
189 case 16: val &= 0xFFFF; break;
190 default: MSG() << "strange op size: " << op->size;
198 // Memory operand. The intermediate form normalizes all memory
199 // address equations to the scale-index-base form. The address
200 // equation is availabe in base, index, and scale. If the offset
201 // field has a non-zero value (one of 8, 16, 32, and 64), lval
202 // will contain the memory offset. Note that base and index fields
203 // contain the base and index register of the address equation,
204 // in the form of an enumerated constant enum ud_type. scale
205 // contains an integer value that the index register must be
210 long long offset = 0;
211 snprintf(buf, 80, "mem b %02x idx %02x scale %02x offs %02x",
212 op->base, op->index, op->scale, op->offset);
216 _check(op->scale != 0, "!! implement register scaling");
218 l4_umword_t addr = register_to_value(op->base);
219 MSG() << " reg " << std::hex << addr;
222 case 0: offset = 0; break;
223 case 8: offset = op->lval.sbyte; break;
224 case 16: offset = op->lval.sword; break;
225 case 32: offset = op->lval.sdword; break;
226 case 64: offset = op->lval.sqword; enter_kdebug("64bit offset"); break;
227 default: enter_kdebug("unknown offset??");
230 MSG() << std::hex << addr << " + " << offset << " = " << addr + offset;
233 // reading a full mword here is okay, because users of the
234 // results returned from this function need to check the real
235 // operand size anyway
236 val = *(l4_umword_t*)_translator->translate(addr);
238 MSG() << std::hex << addr << " := " << val;
245 MSG() << "Need to handle " << operand_type_string(op);
246 enter_kdebug("unhandled src operand type");
250 MSG() << std::hex << "v " << val
251 << (valid ? " (ok)" : " \033[31;1m(INV!)\033[0m")
252 << " ilen " << ilen();
255 enter_kdebug("unhandled operand type");
261 * Extract the offset encoded in an operand.
263 * This incorporates looking at the operand's size to figure out
264 * the right masking. Plus, the result is _SIGNED_!
266 l4_mword_t Romain::Emulator_base::offset_from_operand(ud_operand_t *op)
268 uint8_t offs = op->offset;
272 if (!offs) return op->lval.sword;
275 * Mask only the lower N bits
277 value = op->lval.sdword & ((1LL << offs) - 1);
278 neg = value & (1LL << (offs-1));
280 value = -((1LL << offs) - value);
283 // XXX: so far, we don't support 64bit offsets...
289 * Given a value, write it to whatever target is described by the
292 * So far, only memory targets are needed as we don't get to see writes to
293 * other stuff, such as registers.
295 void Romain::Emulator_base::value_to_operand(l4_umword_t val, ud_operand_t *op)
298 // Memory operand. The intermediate form normalizes all memory
299 // address equations to the scale-index-base form. The address
300 // equation is availabe in base, index, and scale. If the offset
301 // field has a non-zero value (one of 8, 16, 32, and 64), lval
302 // will contain the memory offset. Note that base and index fields
303 // contain the base and index register of the address equation,
304 // in the form of an enumerated constant enum ud_type. scale
305 // contains an integer value that the index register must be
308 // addr: = base + index * scale + offset
312 snprintf(buf, 80, "b %02x idx %02x scale %02x offs %02x",
313 op->base, op->index, op->scale, op->offset);
316 // no base reg, 32 bit size -> this is an address
317 if (!op->base && op->offset) {
318 l4_addr_t target = _translator->translate(op->lval.sdword);
320 << "writing to address: (r " << op->lval.sdword
321 << " l " << target << ") := " << val;
322 *(l4_umword_t*)target = val;
324 else if (op->base) { // else there must be at least a base addr
325 l4_umword_t b_addr = register_to_value(op->base);
326 MSG() << "BASE: " << std::hex << b_addr;
327 l4_umword_t i_addr = op->index ? register_to_value(op->index) : 0;
328 l4_umword_t scale = op->scale;
330 MSG() << std::hex << b_addr << " + (" << i_addr << " << " << scale << ") + "
331 << op->lval.sword << " = " << b_addr + (i_addr << scale) + offset_from_operand(op);
332 b_addr = b_addr + (i_addr << scale) + offset_from_operand(op);
334 l4_addr_t target = _translator->translate(b_addr);
335 MSG() << "target: " << std::hex << target;
338 << "writing to address: (r " << b_addr
339 << " l " << target << ") := " << val;
340 write_target(target, val, op->size);
343 MSG() << "strange mem encoding??";
349 MSG() << "Need to handle " << operand_type_string(op);
350 enter_kdebug("unhandled target operand");
357 * Handle PUSH instruction
359 void Romain::WriteEmulator::handle_push()
361 l4_umword_t val = ~0;
362 ud_operand_t *op = &_ud.operand[0];
364 val = operand_to_value(op);
366 Romain::Stack(_translator->translate(_vcpu->r()->sp)).push(val);
368 _vcpu->r()->sp -= sizeof(l4_umword_t);
369 _vcpu->r()->ip += ilen();
374 * Emulate a CALL instruction
376 void Romain::WriteEmulator::handle_call()
378 // push return address
379 _vcpu->r()->ip += ilen();
380 Romain::Stack(_translator->translate(_vcpu->r()->sp)).push(ip());
383 ud_operand_t *op = &_ud.operand[0];
385 // XXX: check later, if this can be moved into operand_to_value(), too
388 _check(op->size != 32, "!! immediate jmp offset not an mword");
389 MSG() << std::hex << op->lval.sdword
390 << " " << _vcpu->r()->ip + op->lval.sdword;
391 _vcpu->r()->ip += op->lval.sdword;
394 case UD_OP_MEM: // fallthrough
397 l4_umword_t v = operand_to_value(op);
398 MSG() << std::hex << v;
404 MSG() << "Unhandled: " << operand_type_string(op);
405 enter_kdebug("unhandled target");
409 /* We must not touch the SP _before_ looking at the immediate value,
410 * because otherwise offset calculations might be wrong.
412 _vcpu->r()->sp -= sizeof(l4_umword_t);
417 * Handle MOV instruction
419 void Romain::WriteEmulator::handle_mov()
421 ud_operand_t *op1 = &_ud.operand[0];
422 ud_operand_t *op2 = &_ud.operand[1];
424 l4_umword_t val = operand_to_value(op2);
425 value_to_operand(val, op1);
427 _vcpu->r()->ip += ilen();
432 * Handle (REP:)STOS instruction
434 void Romain::WriteEmulator::handle_stos()
436 _check(_ud.mnemonic != UD_Istosd, "non-word string copy");
438 l4_umword_t count = _ud.pfx_rep != UD_NONE ? _vcpu->r()->cx : 1;
440 MSG() << std::hex << "rep = 0x" << (int)_ud.pfx_rep;
441 MSG() << "iterations: " << count;
443 l4_addr_t base = _vcpu->r()->di;
444 base = _translator->translate(base);
446 for (l4_umword_t idx = 0; idx < count; ++idx) {
447 *(l4_umword_t*)base = _vcpu->r()->ax;
450 // 1) other stos-sizes than 4
455 if (_ud.pfx_rep != UD_NONE) // we're done rep'ing
458 _vcpu->r()->di += count * sizeof(l4_umword_t);
460 _vcpu->r()->ip += ilen();
465 * Handle MOVSD instruction
467 void Romain::WriteEmulator::handle_movsd()
469 _check(_ud.mnemonic != UD_Imovsd, "non-word memcopy");
471 l4_umword_t count = _ud.pfx_rep != UD_NONE ? _vcpu->r()->cx : 1;
472 MSG() << std::hex << "rep = 0x" << (int)_ud.pfx_rep;
473 MSG() << "iterations: " << count;
475 l4_addr_t src = _translator->translate(_vcpu->r()->si);
476 l4_addr_t dst = _translator->translate(_vcpu->r()->di);
478 for (l4_umword_t idx = 0; idx < count; ++idx) {
479 *(l4_umword_t*)dst = *(l4_umword_t*)src;
484 if (_ud.pfx_rep != UD_NONE) // we're done rep'ing
486 _vcpu->r()->si += count * sizeof(l4_umword_t);
487 _vcpu->r()->di += count * sizeof(l4_umword_t);
489 _vcpu->r()->ip += ilen();
494 * Handle MOVSB instruction
496 void Romain::WriteEmulator::handle_movsb()
498 _check(_ud.mnemonic != UD_Imovsb, "non-byte memcopy");
500 l4_umword_t count = _ud.pfx_rep != UD_NONE ? _vcpu->r()->cx : 1;
501 MSG() << std::hex << "rep = 0x" << (int)_ud.pfx_rep;
502 MSG() << "iterations: " << count;
504 l4_addr_t src = _translator->translate(_vcpu->r()->si);
505 l4_addr_t dst = _translator->translate(_vcpu->r()->di);
507 for (l4_umword_t idx = 0; idx < count; ++idx) {
508 *(l4_uint8_t*)dst = *(l4_uint8_t*)src;
513 if (_ud.pfx_rep != UD_NONE) // we're done rep'ing
515 _vcpu->r()->si += count * sizeof(l4_uint8_t);
516 _vcpu->r()->di += count * sizeof(l4_uint8_t);
518 _vcpu->r()->ip += ilen();
523 * Handle arithmetic instructions
525 * Arithmetics modify EFLAGS, too...
527 void Romain::WriteEmulator::handle_arithmetics(ArithmeticOperations op)
529 static char const *opstr[] = {"+", "-", "*", "/", "%", "--"};
531 ud_operand_t *op1 = &_ud.operand[0];
532 ud_operand_t *op2 = NULL; // = &_ud.operand[1];
533 l4_umword_t orig = operand_to_value(op1);
534 l4_umword_t arval = 0; // operand_to_value(op2);
535 l4_umword_t flags = 0;
543 op2 = &_ud.operand[1];
544 arval = operand_to_value(op2);
551 MSG() << "value: " << std::hex << orig
552 << opstr[op] << arval << " = ";
555 case ADD: orig += arval; break;
556 case SUB: orig -= arval; break;
557 case MULT: orig *= arval; break;
558 case DIV: orig /= arval; break;
559 case MOD: orig %= arval; break;
560 default: enter_kdebug("unknown arith op"); break;
564 * Now obtain the flags and insert them into the
567 asm volatile ("pushf\n\t"
570 /* First, we plain copy the lowest 8 bits. */
571 _vcpu->r()->flags = (_vcpu->r()->flags & 0xFFFFFF00) | (flags & 0xFF);
572 /* The next three would be IF, TF, DF, which we don't want to touch.
573 * The remaining OF needs to be put in, though.
575 if (flags & OverflowFlag) {
576 _vcpu->r()->flags |= OverflowFlag;
578 _vcpu->r()->flags &= ~OverflowFlag;
581 MSG() << std::hex << "flags " << flags << " result " << orig;
583 value_to_operand(orig, op1);
585 _vcpu->r()->ip += ilen();
586 MSG() << std::hex << "vcpu.eflags = " << _vcpu->r()->flags;
587 //enter_kdebug("arith");
591 * Emulation entry point
593 void Romain::WriteEmulator::emulate()
595 //print_instruction(); // debugging
596 #define HANDLE(val, fn) \
597 case val: fn(); break;
598 #define HANDLE_1(val, fn, arg) \
599 case val: fn(arg); break;
601 switch(_ud.mnemonic) {
602 HANDLE(UD_Icall, handle_call);
603 HANDLE(UD_Imov, handle_mov);
604 HANDLE(UD_Ipush, handle_push);
605 HANDLE(UD_Istosd, handle_stos);
606 HANDLE(UD_Imovsd, handle_movsd);
607 HANDLE(UD_Imovsb, handle_movsb); // XXX merge with movsd
608 HANDLE_1(UD_Isub, handle_arithmetics, SUB);
609 HANDLE_1(UD_Idec, handle_arithmetics, DEC);
611 MSG() << _ud.mnemonic;
612 enter_kdebug("unhandled mnemonic");
620 void Romain::Emulator_base::print_instruction()
622 INFO() << "INSTR(" << std::setw(16) << ud_insn_hex(&_ud) << ") "
623 << std::setw(20) << ud_insn_asm(&_ud);
626 static unsigned long long rdtsc1()
628 unsigned long long ret = 0;
629 unsigned long hi, lo;
630 asm volatile ("cpuid\t\n"
636 : "eax", "ebx", "ecx", "edx");
644 static unsigned long long rdtsc2()
646 unsigned long long ret = 0;
647 unsigned long hi, lo;
648 asm volatile ("rdtscp\n\t"
654 : "eax", "ebx", "ecx", "edx");
658 //printf("%lx %lx %llx\n", hi, lo, ret);
664 static unsigned long long t = 0;
665 static unsigned count = 0;
668 #include "instruction_length.h"
670 void Romain::CopyAndExecute::emulate(Romain::AddressTranslator *at)
672 unsigned long long t1, t2;
674 MSG() << "CopyAndExecute::emulate() called @ " << std::hex << _vcpu->r()->ip
675 << "\n local IP @ " << _local_ip << "\n ilen " << _ilen;
678 // XXX: need rewrite support for rep:movs, because this instruction would
679 // potentially use a second address that is not a shared mem address
680 _local_ip = at->translate(_vcpu->r()->ip);
681 _ilen = mlde32((void*)_local_ip);
683 //static char instbuf[32];
684 //memset(_instbuf, 0x90, 32); // NOP
686 for (unsigned inc = 0; inc <= _ilen; inc += 4) {
687 *(unsigned*)(_instbuf + inc) = *(unsigned*)(_local_ip + inc);
690 //memcpy((void*)_instbuf, (void*)_local_ip, _ilen); // THE instruction
691 _instbuf[_ilen] = 0xC3; // RET
695 : "=a" (_vcpu->r()->ax),
696 "=c" (_vcpu->r()->cx),
697 "=d" (_vcpu->r()->dx),
698 "=S" (_vcpu->r()->si),
699 "=D" (_vcpu->r()->di)
700 : "r" (_instbuf), "a" (_vcpu->r()->ax),
701 "d" (_vcpu->r()->dx), "D" (_vcpu->r()->di),
702 "c" (_vcpu->r()->cx), "S" (_vcpu->r()->si)
712 if (count >= 100000) {
713 printf("DT: %lld %p %p\n", t / count, this, _instbuf);
718 _vcpu->r()->ip += _ilen;