mask repeat prefix out of insn prefix
[fkvm-libfkvm.git] / mmio.c
blob9fe8035d249db1482afe76346ece672c72c53a8b
1 /*-
2 * Copyright (c) 2008 Brent Stephens <brents@rice.edu>
3 * Copyright (c) 2008 Diego Ongaro <diego.ongaro@rice.edu>
4 * Copyright (c) 2008 Oleg Pesok <olegpesok@gmail.com>
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
29 #define FKVM_INTERNAL
30 #include <sys/fkvm.h>
32 #include <unistd.h>
33 #include <stdlib.h>
34 #include <stdbool.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <errno.h>
38 #include <inttypes.h>
39 #include <assert.h>
40 #include <libdis.h>
42 #include "libkvm.h"
43 #include "libfkvm-common.h"
44 #include "disasm.h"
46 #define reg_size() _reg_size(regs, sregs)
47 #define test_repeat_noop() _test_repeat_noop(get_repeat_prefix(insn) == insn_rep_zero, \
48 reg_size(), regs)
49 #define test_repeat_tail() _test_repeat_tail(get_repeat_prefix(insn) == insn_rep_zero, \
50 reg_size(), regs)
52 static int
53 cb_mmio_read(kvm_context_t kvm,
54 uint64_t addr,
55 uint8_t *data,
56 int len)
58 int error;
59 if ((addr & 0xFFF) + len - 1 > 0xFFF) {
60 printf("mmio_read over page boundary\n");
61 EXIT();
63 error = kvm->callbacks->mmio_read(kvm->opaque, addr,
64 data, len);
65 return error;
68 static int
69 cb_mmio_write(kvm_context_t kvm,
70 uint64_t addr,
71 uint8_t *data,
72 int len)
74 int error;
75 if ((addr & 0xFFF) + len - 1 > 0xFFF) {
76 printf("mmio_write over page boundary\n");
77 EXIT();
79 error = kvm->callbacks->mmio_write(kvm->opaque, addr,
80 data, len);
81 return error;
84 static int
85 emulate_mmio_cmp(kvm_context_t kvm,
86 struct kvm_regs *regs,
87 struct kvm_sregs *sregs,
88 uint64_t fault_addr,
89 x86_insn_t *insn)
91 uint64_t source;
92 uint64_t dest;
93 uint64_t result;
94 uint64_t sign_mask;
95 uint8_t SF, PF, CF, OF, AF, ZF;
96 uint8_t data[8];
97 //int error;
98 int i;
99 unsigned int dest_size;
100 unsigned int src_size;
101 x86_op_t * dest_op;
102 x86_op_t *src_op;
104 assert(get_repeat_prefix(insn) == insn_no_prefix);
105 assert(insn->explicit_count == 2);
107 dest_op = x86_operand_1st(insn);
108 src_op = x86_operand_2nd(insn);
110 dest_size = x86_operand_size(dest_op);
111 src_size = x86_operand_size(src_op);
113 assert(dest_size == src_size);
115 switch (src_op->type) { /* source type */
116 case op_immediate:
117 if (dest_op->type != op_expression)
118 EXIT();
120 if ((dest_op->flags & ~0xFF) == 0)
121 dest_op->flags |= op_ds_seg;
123 source = get_source_data(regs, src_op);
125 dest = get_memi_address(regs, sregs, dest_op, reg_size());
126 //printf("Destination Address: %" PRIx64 "\n", dest);
127 dest = kvm_get_phys_addr(dest);
128 if (dest != fault_addr) {
129 EXIT_ERR_PATH();
132 memset(data, 0, sizeof(data));
133 cb_mmio_read(kvm, dest, data, dest_size);
134 dest = *(uint64_t *)data;
136 break;
138 default:
139 printf("op: %d\n", src_op->type);
140 EXIT();
142 /* CMP emulation */
144 #define SIGN_MASK_64 0x8000000000000000
145 #define SIGN_MASK_32 0x80000000
146 #define SIGN_MASK_16 0x8000
147 #define SIGN_MASK_8 0x80
149 switch(dest_size) {
150 case (1) : sign_mask = SIGN_MASK_8;
151 result = (uint8_t)dest - (uint8_t)source;
152 break;
153 case (4) : sign_mask = SIGN_MASK_32;
154 result = (uint32_t)dest - (uint32_t)source;
155 break;
156 case (8) : sign_mask = SIGN_MASK_64;
157 result = dest - source;
158 break;
159 case (2) :
160 default : sign_mask = SIGN_MASK_16;
161 result = (uint16_t)dest - (uint16_t)source;
162 break;
165 //printf("The dest is %" PRIx64 "\n", dest);
166 //printf("The src is %" PRIx64 "\n", source);
167 CF = (dest < source);
168 ZF = (dest == source);
169 SF = (result & sign_mask);
170 if ((dest & sign_mask) == 0 && (source & sign_mask) != 0)
171 OF = SF;
172 else if ((dest & sign_mask) != 0 && (source & sign_mask) == 0)
173 OF = !SF;
174 else
175 OF = 0;
176 #if 0
177 /* Taken from AMD VOL 3, CMP instruction information */
178 if((int64_t)dest > (int64_t)source)
179 OF = SF;
180 else if ((int64_t)dest < (int64_t)source)
181 OF = !SF;
182 else
183 OF = 0;
184 #endif
185 PF = 1;
186 for (i=0; i < 8; i++) {
187 PF = PF ^ ((result >> i) & 1);
190 AF = ( (dest & 0x0F) >= (source & 0x0F) );
192 regs->rflags &= ~(CF_MASK | PF_MASK | AF_MASK | ZF_MASK | SF_MASK | OF_MASK);
193 regs->rflags |= (CF) ? CF_MASK : 0;
194 regs->rflags |= (PF) ? PF_MASK : 0;
195 regs->rflags |= (AF) ? AF_MASK : 0;
196 regs->rflags |= (ZF) ? ZF_MASK : 0;
197 regs->rflags |= (SF) ? SF_MASK : 0;
198 regs->rflags |= (OF) ? OF_MASK : 0;
200 // EXIT();
201 return 0;
204 static int
205 emulate_mmio_movs(kvm_context_t kvm,
206 struct kvm_regs *regs,
207 struct kvm_sregs *sregs,
208 uint64_t fault_addr,
209 x86_insn_t *insn)
211 uint64_t dest_operand, src_operand;
212 uint8_t data[8];
213 bool mmio_write;
214 bool mmio_read;
215 int error = 0;
216 int reg_idx1;
217 int reg_idx2;
218 unsigned int dest_size;
219 unsigned int src_size;
220 x86_reg_t *dest;
221 x86_reg_t *src;
222 x86_op_t *dest_op;
223 x86_op_t *src_op;
225 dest_op = x86_operand_1st(insn);
226 src_op = x86_operand_2nd(insn);
228 assert(get_repeat_prefix(insn) == insn_no_prefix ||
229 get_repeat_prefix(insn) == insn_rep_zero);
230 assert(insn->explicit_count == 2);
232 assert(dest_op->data.expression.index.id == 0);
233 assert(src_op->data.expression.index.id == 0);
235 assert(dest_op->type == op_expression);
236 assert(src_op->type == op_expression);
238 dest = &dest_op->data.expression.base;
239 src = &src_op->data.expression.base;
241 reg_idx1 = kvm_reg_from_x86_reg(src->id);
242 reg_idx2 = kvm_reg_from_x86_reg(dest->id);
244 assert(reg_idx1 == KVM_REG_RSI);
245 assert(reg_idx2 == KVM_REG_RDI);
246 assert((dest_op->flags & op_es_seg) == op_es_seg);
247 assert((src_op->flags & op_ds_seg) == op_ds_seg);
249 dest_size = x86_operand_size(dest_op);
250 src_size = x86_operand_size(src_op);
252 assert(dest_size == src_size);
254 /* TODO: Can it ever be a segment other than DS
255 if (inst->operands[0].memi.base.segment == SEGMENT_DEFAULT)
256 inst->operands[0].memi.base.segment = SEGMENT_DS;
259 if (test_repeat_noop())
260 return 0;
262 dest_operand = get_memi_address(regs, sregs, dest_op, reg_size());
263 src_operand = get_memi_address(regs, sregs, src_op, reg_size());
265 //printf("dest_operand: %" PRIx64 "\n", dest_operand);
266 //printf("src_operand: %" PRIx64 "\n", src_operand);
268 if (kvm_get_phys_addr(src_operand) == fault_addr) { // Read
269 uint64_t dest_phys_addr;
270 dest_phys_addr = kvm_get_phys_addr(dest_operand);
271 if (dest_phys_addr == -1)
272 EXIT_ERR_PATH();
273 mmio_read = true;
274 mmio_write = !kvm_is_containing_region(kvm,
275 dest_phys_addr, dest_size);
277 else if (kvm_get_phys_addr(dest_operand) == fault_addr) { // Write
278 uint64_t src_phys_addr;
279 src_phys_addr = kvm_get_phys_addr(src_operand);
280 if (src_phys_addr == -1)
281 EXIT_ERR_PATH();
282 mmio_write = true;
283 mmio_read = !kvm_is_containing_region(kvm,
284 src_phys_addr, src_size);
286 else
287 EXIT_ERR_PATH();
289 /* TODO: what if we keep on writing past the page boundary due
290 * to REP? */
292 do {
293 if (mmio_read) {
294 uint64_t src_phys_addr;
295 src_phys_addr = kvm_get_phys_addr(src_operand);
296 if (src_phys_addr == -1)
297 EXIT_ERR_PATH();
298 error = cb_mmio_read(kvm,
299 src_phys_addr,
300 data,
301 dest_size);
303 else {
304 cpu_virtual_memory_rw(src_operand, data,
305 dest_size, 0);
308 if (mmio_write) {
309 uint64_t dest_phys_addr;
310 dest_phys_addr = kvm_get_phys_addr(dest_operand);
311 if (dest_phys_addr == -1)
312 EXIT_ERR_PATH();
313 error = cb_mmio_write(kvm,
314 dest_phys_addr,
315 data,
316 dest_size);
318 else {
319 cpu_virtual_memory_rw(dest_operand, data,
320 dest_size, 1);
323 if (error != 0)
324 return error;
326 if ((regs->rflags & RFLAGS_DF_MASK) != 0) {
327 regs->rsi -= dest_size;
328 regs->rdi -= dest_size;
329 src_operand -= dest_size;
330 dest_operand -= dest_size;
332 else {
333 regs->rsi += dest_size;
334 regs->rdi += dest_size;
335 src_operand += dest_size;
336 dest_operand += dest_size;
339 } while (test_repeat_tail());
341 return 0;
344 static int
345 emulate_mmio_stos(kvm_context_t kvm,
346 struct kvm_regs *regs,
347 struct kvm_sregs *sregs,
348 uint64_t fault_addr,
349 x86_insn_t *insn)
351 uint64_t dest_operand;
352 uint64_t source;
353 uint8_t source_data[8];
354 int error;
355 int reg_idx1;
356 int reg_idx2;
357 unsigned int dest_size;
358 unsigned int src_size;
359 x86_reg_t *dest;
360 x86_op_t *dest_op;
361 x86_op_t *src_op;
363 dest_op = x86_operand_1st(insn);
364 src_op = x86_operand_2nd(insn);
366 assert(insn->explicit_count == 2);
368 assert(dest_op->type == op_expression);
369 assert((dest_op->flags & op_es_seg) == op_es_seg);
370 assert(dest_op->data.expression.index.id == 0);
371 assert(src_op->type == op_register);
373 dest = &dest_op->data.expression.base;
375 reg_idx1 = kvm_reg_from_x86_reg(dest->id);
376 reg_idx2 = kvm_reg_from_x86_reg(src_op->data.reg.id);
377 assert(reg_idx1 == KVM_REG_RDI);
378 assert(reg_idx2 == KVM_REG_RAX);
380 dest_size = x86_operand_size(dest_op);
381 src_size = x86_operand_size(src_op);
383 assert(dest_size == src_size);
385 assert(get_repeat_prefix(insn) == insn_no_prefix ||
386 get_repeat_prefix(insn) == insn_rep_zero);
388 if (test_repeat_noop())
389 return 0;
391 dest_operand = get_memi_address(regs, sregs, dest_op, reg_size());
392 source = mask_reg(regs->rax, reg_size());
394 //printf("dest_operand: %" PRIx64 "\n", dest_operand);
395 //printf("source: %" PRIx64 "\n", source);
397 if (kvm_get_phys_addr(dest_operand) != fault_addr)
398 EXIT_ERR_PATH();
400 /* TODO: what if we keep on writing past the page boundary due
401 * to REP? */
403 *(uint64_t*) source_data = source;
405 do {
407 uint64_t dest_phys_addr;
408 dest_phys_addr = kvm_get_phys_addr(dest_operand);
409 if (dest_phys_addr == -1)
410 EXIT_ERR_PATH();
412 error = cb_mmio_write(kvm, dest_phys_addr, source_data, dest_size);
413 if (error != 0)
414 return error;
416 if ((regs->rflags & RFLAGS_DF_MASK) != 0) {
417 regs->rdi -= dest_size;
418 dest_operand -= dest_size;
420 else {
421 regs->rdi += dest_size;
422 dest_operand += dest_size;
425 } while (test_repeat_tail());
427 return 0;
430 static int
431 emulate_mmio_mov(kvm_context_t kvm,
432 struct kvm_regs *regs,
433 struct kvm_sregs *sregs,
434 uint64_t fault_addr,
435 x86_insn_t *insn)
437 uint64_t source;
438 uint64_t dest;
439 uint8_t data[8];
440 int error;
441 unsigned int dest_size;
442 unsigned int src_size;
443 x86_op_t *dest_op;
444 x86_op_t *src_op;
446 assert(get_repeat_prefix(insn) == insn_no_prefix);
447 assert(insn->explicit_count == 2);
449 dest_op = x86_operand_1st(insn);
450 src_op = x86_operand_2nd(insn);
452 dest_size = x86_operand_size(dest_op);
453 src_size = x86_operand_size(src_op);
455 if (strcmp(insn->mnemonic, "movzx") == 0)
456 /* pass */;
457 else if (strcmp(insn->mnemonic, "movsx") == 0)
458 EXIT();
459 else
460 assert(dest_size == src_size);
462 switch (src_op->type) { /* source type */
463 case op_register:
464 case op_immediate:
465 if ((dest_op->type != op_expression) &&
466 (dest_op->type != op_offset))
467 EXIT();
469 if ((dest_op->flags & ~0xFF) == 0)
470 dest_op->flags |= op_ds_seg;
472 *(uint64_t*) data = get_source_data(regs, src_op);
474 dest = get_memi_address(regs, sregs, dest_op, reg_size());
475 //printf("Destination Address: %" PRIx64 "\n", dest);
477 dest = kvm_get_phys_addr(dest);
479 if (dest != fault_addr) {
480 EXIT_ERR_PATH();
483 error = cb_mmio_write(kvm, dest, data, dest_size);
484 if (error != 0)
485 return error;
487 break;
489 case op_expression:
490 case op_offset:
491 if (dest_op->type != op_register)
492 EXIT();
493 if ((src_op->flags & ~0xFF) == 0)
494 src_op->flags |= op_ds_seg;
496 source = get_memi_address(regs, sregs, src_op, reg_size());
497 printf("source = %016" PRIx64 " -> %016" PRIx64 "\n",
498 source,
499 kvm_get_phys_addr(source));
500 printf("fault_addr = %016" PRIx64 " -> %016" PRIx64 "\n",
501 fault_addr,
502 kvm_get_phys_addr(fault_addr));
504 source = kvm_get_phys_addr(source);
506 if (source != fault_addr) {
507 printf("eax: 0x%" PRIx64 "\n", regs->rax);
508 EXIT_ERR_PATH();
511 error = cb_mmio_read(kvm, source, data, src_size);
512 if (error != 0)
513 return error;
515 dest = *(uint64_t*) data;
516 dest = mask_reg(dest, reg_size());
517 kvm_regs_set(regs,
518 kvm_reg_from_x86_reg(dest_op->data.reg.id),
519 dest);
521 break;
523 default:
524 printf("op: %d\n", src_op->type);
525 EXIT();
528 return 0;
531 static int
532 emulate_mmio_xchg(kvm_context_t kvm,
533 struct kvm_regs *regs,
534 struct kvm_sregs *sregs,
535 uint64_t fault_addr,
536 x86_insn_t *insn)
538 x86_op_t *mem_op;
539 x86_op_t *reg_op;
540 unsigned int mem_size;
541 unsigned int reg_size;
542 uint64_t reg_data;
543 uint64_t mem_addr;
544 uint64_t mem_data;
545 int error;
547 assert(get_repeat_prefix(insn) == insn_no_prefix);
548 assert(insn->explicit_count == 2);
550 mem_op = x86_operand_1st(insn);
551 reg_op = x86_operand_2nd(insn);
553 assert(mem_op->type == op_expression);
554 assert(reg_op->type == op_register);
556 mem_size = x86_operand_size(mem_op);
557 reg_size = x86_operand_size(reg_op);
558 assert(reg_size == mem_size);
560 /* read data */
561 reg_data = get_source_data(regs, reg_op);
563 if ((mem_op->flags & ~0xFF) == 0)
564 mem_op->flags |= op_ds_seg;
565 mem_addr = get_memi_address(regs, sregs, mem_op, reg_size());
566 mem_addr = kvm_get_phys_addr(mem_addr);
567 if (mem_addr != fault_addr)
568 EXIT_ERR_PATH();
569 error = cb_mmio_read(kvm, mem_addr, (uint8_t*) &mem_data, mem_size);
570 if (error != 0)
571 EXIT_ERR_PATH();
573 /* exchange */
575 uint64_t tmp_data;
576 tmp_data = mem_data;
577 mem_data = reg_data;
578 reg_data = tmp_data;
581 /* write data */
582 kvm_regs_set(regs,
583 kvm_reg_from_x86_reg(reg_op->data.reg.id),
584 reg_data);
585 error = cb_mmio_write(kvm, mem_addr, (uint8_t*) &mem_data, mem_size);
586 if (error != 0)
587 EXIT_ERR_PATH();
589 return 0;
594 emulate_mmio(kvm_context_t kvm,
595 struct kvm_regs *regs,
596 struct kvm_sregs *sregs,
597 uint64_t fault_addr)
599 x86_insn_t insn;
600 uint64_t loc, next_rip;
601 unsigned int insn_size;
602 int error = 0;
603 //x86_op_t *op;
605 libdisasm_init(reg_size());
607 loc = sregs->cs.base + regs->rip;
608 insn_size = get_x86_insn(loc, &insn);
609 if (insn_size == -1)
610 EXIT_ERR_PATH();
612 //printf("rip: %" PRIx64 "\n", regs->rip);
613 //printf("loc: %" PRIx64 "\n", loc);
614 next_rip = regs->rip + insn_size;
617 //printf("Number of args: %d\n", (int)insn.explicit_count);
619 #if 0
620 op = x86_operand_1st(&insn);
621 printf("Dest Type = %d\n", op->type);
622 op = x86_operand_2nd(&insn);
623 printf("Src Type = %d\n", op->type);
624 op = x86_operand_3rd(&insn);
625 if (op != NULL)
626 printf("Type = %d\n", op->type);
627 else
628 printf("Op was null\n");
629 #endif
631 switch (insn.type) {
633 case insn_strmov:
634 error = emulate_mmio_movs(kvm, regs, sregs, fault_addr, &insn);
635 break;
636 case insn_strstore:
637 error = emulate_mmio_stos(kvm, regs, sregs, fault_addr, &insn);
638 break;
639 case insn_mov:
640 error = emulate_mmio_mov(kvm, regs, sregs, fault_addr, &insn);
641 break;
642 case insn_cmp:
643 error = emulate_mmio_cmp(kvm, regs, sregs, fault_addr, &insn);
644 break;
645 case insn_xchg:
646 error = emulate_mmio_xchg(kvm, regs, sregs, fault_addr, &insn);
647 break;
648 default:
649 printf("MMIO %s insn.type=%d\n", insn.mnemonic, insn.type);
650 EXIT();
653 if (error == 0)
654 regs->rip = next_rip;
656 libdisasm_cleanup();
658 return error;