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.
18 #include <l4/sys/kdebug.h>
20 #define MSG() DEBUGf(Romain::Log::Emulator)
23 * Debugging: get human-readable operand type
25 static char const *operand_type_string(ud_operand_t *op)
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";
39 void Romain::Emulator_base::init_ud()
42 ud_set_mode(&_ud, 32);
43 ud_set_syntax(&_ud, UD_SYN_INTEL);
45 ud_set_pc(&_ud, ip());
46 ud_set_input_buffer(&_ud, (unsigned char*)_local_ip, 32);
48 int num_bytes = ud_disassemble(&_ud);
50 MSG() << "print_instruction "
51 << num_bytes << " byte"
52 << (num_bytes > 1 ? "s" : "") << ".";
57 * Romain::Emulator constructor
59 * Nothing fancy -- use of udis86 should be hidden behind another
62 Romain::Emulator_base::Emulator_base(L4vcpu::Vcpu *vcpu,
63 Romain::AddressTranslator const *trans)
64 : _vcpu(vcpu), _translator(trans)
66 _local_ip = _translator->translate(ip());
72 * Get register value from VCPU
74 * Returns an MWord even if the real operand is only 16 or 8 bit.
76 l4_umword_t Romain::Emulator_base::register_to_value(ud_type op)
80 #define REG(udis_name, vcpu_name, target, ...) \
81 case UD_R_##udis_name: target = _vcpu->r()->vcpu_name __VA_ARGS__; break
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);
89 REG(AH, ax, val, & 0xFF00); REG(CH, cx, val, & 0xFF00); REG(DH, dx, val, & 0xFF00);
90 REG(BH, bx, val, & 0xFF00);
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);
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);
102 MSG() << "target register: " << std::hex << op;
103 enter_kdebug("unhandled register target");
111 void Romain::Emulator_base::value_to_register(l4_umword_t val, ud_type op)
113 #define REG(udis_name, vcpu_name, ...) \
114 case UD_R_##udis_name: _vcpu->r()->vcpu_name = val __VA_ARGS__; break;
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);
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);
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);
130 MSG() << "target register: " << std::hex << op;
131 enter_kdebug("unhandled register target");
140 * Calculate the value for an operand
142 * Note, this always returns an MWord. Users need to check op->size
143 * to determine what to do with the value.
145 l4_umword_t Romain::Emulator_base::operand_to_value(ud_operand_t *op)
148 l4_umword_t val = ~0;
151 // Operand is a register. The specific register is contained in
152 // base in the form of an enumerated constant, enum ud_type.
156 snprintf(buf, 80, "reg b %02x idx %02x scale %02x offs %02x",
157 op->base, op->index, op->scale, op->offset);
160 // addr: = base + index * scale + offset
162 _check(op->scale != 0, "!! implement register scaling");
163 _check(op->offset != 0, "!! implement register offset");
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;
168 val = bas + idx * op->scale;
170 val += op->lval.sdword;
172 MSG() << "val = " << std::hex << val;
178 // Immediate operand. Value available in lval.
181 MSG() << "op sz " << (int)op->size
182 << "op val " << std::hex << op->lval.uqword;
183 val = op->lval.udword;
186 case 8: val &= 0xFF; break;
187 case 16: val &= 0xFFFF; break;
188 default: MSG() << "strange op size: " << op->size;
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
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);
214 _check(op->scale != 0, "!! implement register scaling");
216 l4_umword_t addr = register_to_value(op->base);
217 MSG() << " reg " << std::hex << addr;
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??");
228 MSG() << std::hex << addr << " + " << offset << " = " << addr + offset;
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);
236 MSG() << std::hex << addr << " := " << val;
243 MSG() << "Need to handle " << operand_type_string(op);
244 enter_kdebug("unhandled src operand type");
248 MSG() << std::hex << "v " << val
249 << (valid ? " (ok)" : " \033[31;1m(INV!)\033[0m")
250 << " ilen " << ilen();
253 enter_kdebug("unhandled operand type");
259 * Extract the offset encoded in an operand.
261 * This incorporates looking at the operand's size to figure out
262 * the right masking. Plus, the result is _SIGNED_!
264 int Romain::Emulator_base::offset_from_operand(ud_operand_t *op)
266 uint8_t offs = op->offset;
270 if (!offs) return op->lval.sword;
273 * Mask only the lower N bits
275 value = op->lval.sdword & ((1LL << offs) - 1);
276 neg = value & (1LL << (offs-1));
278 value = -((1LL << offs) - value);
281 // XXX: so far, we don't support 64bit offsets...
287 * Given a value, write it to whatever target is described by the
290 * So far, only memory targets are needed as we don't get to see writes to
291 * other stuff, such as registers.
293 void Romain::Emulator_base::value_to_operand(l4_umword_t val, ud_operand_t *op)
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
306 // addr: = base + index * scale + offset
310 snprintf(buf, 80, "b %02x idx %02x scale %02x offs %02x",
311 op->base, op->index, op->scale, op->offset);
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);
318 << "writing to address: (r " << op->lval.sdword
319 << " l " << target << ") := " << val;
320 *(l4_umword_t*)target = val;
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;
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);
332 l4_addr_t target = _translator->translate(b_addr);
333 MSG() << "target: " << std::hex << target;
336 << "writing to address: (r " << b_addr
337 << " l " << target << ") := " << val;
338 write_target(target, val, op->size);
341 MSG() << "strange mem encoding??";
347 MSG() << "Need to handle " << operand_type_string(op);
348 enter_kdebug("unhandled target operand");
355 * Handle PUSH instruction
357 void Romain::WriteEmulator::handle_push()
359 l4_umword_t val = ~0;
360 ud_operand_t *op = &_ud.operand[0];
362 val = operand_to_value(op);
364 Romain::Stack(_translator->translate(_vcpu->r()->sp)).push(val);
366 _vcpu->r()->sp -= sizeof(l4_umword_t);
367 _vcpu->r()->ip += ilen();
372 * Emulate a CALL instruction
374 void Romain::WriteEmulator::handle_call()
376 // push return address
377 _vcpu->r()->ip += ilen();
378 Romain::Stack(_translator->translate(_vcpu->r()->sp)).push(ip());
381 ud_operand_t *op = &_ud.operand[0];
383 // XXX: check later, if this can be moved into operand_to_value(), too
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;
392 case UD_OP_MEM: // fallthrough
395 l4_umword_t v = operand_to_value(op);
396 MSG() << std::hex << v;
402 MSG() << "Unhandled: " << operand_type_string(op);
403 enter_kdebug("unhandled target");
407 /* We must not touch the SP _before_ looking at the immediate value,
408 * because otherwise offset calculations might be wrong.
410 _vcpu->r()->sp -= sizeof(l4_umword_t);
415 * Handle MOV instruction
417 void Romain::WriteEmulator::handle_mov()
419 ud_operand_t *op1 = &_ud.operand[0];
420 ud_operand_t *op2 = &_ud.operand[1];
422 l4_umword_t val = operand_to_value(op2);
423 value_to_operand(val, op1);
425 _vcpu->r()->ip += ilen();
430 * Handle (REP:)STOS instruction
432 void Romain::WriteEmulator::handle_stos()
434 _check(_ud.mnemonic != UD_Istosd, "non-word string copy");
436 unsigned count = _ud.pfx_rep != UD_NONE ? _vcpu->r()->cx : 1;
438 MSG() << std::hex << "rep = 0x" << (int)_ud.pfx_rep;
439 MSG() << "iterations: " << count;
441 l4_addr_t base = _vcpu->r()->di;
442 base = _translator->translate(base);
444 for (unsigned idx = 0; idx < count; ++idx) {
445 *(l4_umword_t*)base = _vcpu->r()->ax;
448 // 1) other stos-sizes than 4
453 if (_ud.pfx_rep != UD_NONE) // we're done rep'ing
456 _vcpu->r()->di += count * sizeof(l4_umword_t);
458 _vcpu->r()->ip += ilen();
463 * Handle MOVSD instruction
465 void Romain::WriteEmulator::handle_movsd()
467 _check(_ud.mnemonic != UD_Imovsd, "non-word memcopy");
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;
473 l4_addr_t src = _translator->translate(_vcpu->r()->si);
474 l4_addr_t dst = _translator->translate(_vcpu->r()->di);
476 for (unsigned idx = 0; idx < count; ++idx) {
477 *(l4_umword_t*)dst = *(l4_umword_t*)src;
482 if (_ud.pfx_rep != UD_NONE) // we're done rep'ing
484 _vcpu->r()->si += count * sizeof(l4_umword_t);
485 _vcpu->r()->di += count * sizeof(l4_umword_t);
487 _vcpu->r()->ip += ilen();
492 * Handle MOVSB instruction
494 void Romain::WriteEmulator::handle_movsb()
496 _check(_ud.mnemonic != UD_Imovsb, "non-byte memcopy");
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;
502 l4_addr_t src = _translator->translate(_vcpu->r()->si);
503 l4_addr_t dst = _translator->translate(_vcpu->r()->di);
505 for (unsigned idx = 0; idx < count; ++idx) {
506 *(unsigned char*)dst = *(unsigned char*)src;
511 if (_ud.pfx_rep != UD_NONE) // we're done rep'ing
513 _vcpu->r()->si += count * sizeof(unsigned char);
514 _vcpu->r()->di += count * sizeof(unsigned char);
516 _vcpu->r()->ip += ilen();
521 * Handle arithmetic instructions
523 * Arithmetics modify EFLAGS, too...
525 void Romain::WriteEmulator::handle_arithmetics(ArithmeticOperations op)
527 static char const *opstr[] = {"+", "-", "*", "/", "%", "--"};
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;
541 op2 = &_ud.operand[1];
542 arval = operand_to_value(op2);
549 MSG() << "value: " << std::hex << orig
550 << opstr[op] << arval << " = ";
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;
562 * Now obtain the flags and insert them into the
565 asm volatile ("pushf\n\t"
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.
573 if (flags & OverflowFlag) {
574 _vcpu->r()->flags |= OverflowFlag;
576 _vcpu->r()->flags &= ~OverflowFlag;
579 MSG() << std::hex << "flags " << flags << " result " << orig;
581 value_to_operand(orig, op1);
583 _vcpu->r()->ip += ilen();
584 MSG() << std::hex << "vcpu.eflags = " << _vcpu->r()->flags;
585 //enter_kdebug("arith");
589 * Emulation entry point
591 void Romain::WriteEmulator::emulate()
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;
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);
609 MSG() << _ud.mnemonic;
610 enter_kdebug("unhandled mnemonic");
618 void Romain::Emulator_base::print_instruction()
620 INFO() << "INSTR(" << std::setw(16) << ud_insn_hex(&_ud) << ") "
621 << std::setw(20) << ud_insn_asm(&_ud);