diff options
author | Gleb Natapov <gleb@redhat.com> | 2010-02-10 07:21:33 -0500 |
---|---|---|
committer | Marcelo Tosatti <mtosatti@redhat.com> | 2010-03-01 10:36:11 -0500 |
commit | f850e2e603bf5a05b0aee7901857cf85715aa694 (patch) | |
tree | d3c841530a11187bbe70b36bf4b9bca97bf7dd64 /arch/x86/kvm | |
parent | 1871c6020d7308afb99127bba51f04548e7ca84e (diff) |
KVM: x86 emulator: Check IOPL level during io instruction emulation
Make emulator check that vcpu is allowed to execute IN, INS, OUT,
OUTS, CLI, STI.
Signed-off-by: Gleb Natapov <gleb@redhat.com>
Cc: stable@kernel.org
Signed-off-by: Avi Kivity <avi@redhat.com>
Diffstat (limited to 'arch/x86/kvm')
-rw-r--r-- | arch/x86/kvm/emulate.c | 89 | ||||
-rw-r--r-- | arch/x86/kvm/x86.c | 10 |
2 files changed, 86 insertions, 13 deletions
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index c44b46014842..296e8519dc53 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c | |||
@@ -1698,6 +1698,57 @@ emulate_sysexit(struct x86_emulate_ctxt *ctxt) | |||
1698 | return 0; | 1698 | return 0; |
1699 | } | 1699 | } |
1700 | 1700 | ||
1701 | static bool emulator_bad_iopl(struct x86_emulate_ctxt *ctxt) | ||
1702 | { | ||
1703 | int iopl; | ||
1704 | if (ctxt->mode == X86EMUL_MODE_REAL) | ||
1705 | return false; | ||
1706 | if (ctxt->mode == X86EMUL_MODE_VM86) | ||
1707 | return true; | ||
1708 | iopl = (ctxt->eflags & X86_EFLAGS_IOPL) >> IOPL_SHIFT; | ||
1709 | return kvm_x86_ops->get_cpl(ctxt->vcpu) > iopl; | ||
1710 | } | ||
1711 | |||
1712 | static bool emulator_io_port_access_allowed(struct x86_emulate_ctxt *ctxt, | ||
1713 | struct x86_emulate_ops *ops, | ||
1714 | u16 port, u16 len) | ||
1715 | { | ||
1716 | struct kvm_segment tr_seg; | ||
1717 | int r; | ||
1718 | u16 io_bitmap_ptr; | ||
1719 | u8 perm, bit_idx = port & 0x7; | ||
1720 | unsigned mask = (1 << len) - 1; | ||
1721 | |||
1722 | kvm_get_segment(ctxt->vcpu, &tr_seg, VCPU_SREG_TR); | ||
1723 | if (tr_seg.unusable) | ||
1724 | return false; | ||
1725 | if (tr_seg.limit < 103) | ||
1726 | return false; | ||
1727 | r = ops->read_std(tr_seg.base + 102, &io_bitmap_ptr, 2, ctxt->vcpu, | ||
1728 | NULL); | ||
1729 | if (r != X86EMUL_CONTINUE) | ||
1730 | return false; | ||
1731 | if (io_bitmap_ptr + port/8 > tr_seg.limit) | ||
1732 | return false; | ||
1733 | r = ops->read_std(tr_seg.base + io_bitmap_ptr + port/8, &perm, 1, | ||
1734 | ctxt->vcpu, NULL); | ||
1735 | if (r != X86EMUL_CONTINUE) | ||
1736 | return false; | ||
1737 | if ((perm >> bit_idx) & mask) | ||
1738 | return false; | ||
1739 | return true; | ||
1740 | } | ||
1741 | |||
1742 | static bool emulator_io_permited(struct x86_emulate_ctxt *ctxt, | ||
1743 | struct x86_emulate_ops *ops, | ||
1744 | u16 port, u16 len) | ||
1745 | { | ||
1746 | if (emulator_bad_iopl(ctxt)) | ||
1747 | if (!emulator_io_port_access_allowed(ctxt, ops, port, len)) | ||
1748 | return false; | ||
1749 | return true; | ||
1750 | } | ||
1751 | |||
1701 | int | 1752 | int |
1702 | x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops) | 1753 | x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops) |
1703 | { | 1754 | { |
@@ -1889,7 +1940,12 @@ special_insn: | |||
1889 | break; | 1940 | break; |
1890 | case 0x6c: /* insb */ | 1941 | case 0x6c: /* insb */ |
1891 | case 0x6d: /* insw/insd */ | 1942 | case 0x6d: /* insw/insd */ |
1892 | if (kvm_emulate_pio_string(ctxt->vcpu, | 1943 | if (!emulator_io_permited(ctxt, ops, c->regs[VCPU_REGS_RDX], |
1944 | (c->d & ByteOp) ? 1 : c->op_bytes)) { | ||
1945 | kvm_inject_gp(ctxt->vcpu, 0); | ||
1946 | goto done; | ||
1947 | } | ||
1948 | if (kvm_emulate_pio_string(ctxt->vcpu, | ||
1893 | 1, | 1949 | 1, |
1894 | (c->d & ByteOp) ? 1 : c->op_bytes, | 1950 | (c->d & ByteOp) ? 1 : c->op_bytes, |
1895 | c->rep_prefix ? | 1951 | c->rep_prefix ? |
@@ -1905,6 +1961,11 @@ special_insn: | |||
1905 | return 0; | 1961 | return 0; |
1906 | case 0x6e: /* outsb */ | 1962 | case 0x6e: /* outsb */ |
1907 | case 0x6f: /* outsw/outsd */ | 1963 | case 0x6f: /* outsw/outsd */ |
1964 | if (!emulator_io_permited(ctxt, ops, c->regs[VCPU_REGS_RDX], | ||
1965 | (c->d & ByteOp) ? 1 : c->op_bytes)) { | ||
1966 | kvm_inject_gp(ctxt->vcpu, 0); | ||
1967 | goto done; | ||
1968 | } | ||
1908 | if (kvm_emulate_pio_string(ctxt->vcpu, | 1969 | if (kvm_emulate_pio_string(ctxt->vcpu, |
1909 | 0, | 1970 | 0, |
1910 | (c->d & ByteOp) ? 1 : c->op_bytes, | 1971 | (c->d & ByteOp) ? 1 : c->op_bytes, |
@@ -2202,7 +2263,13 @@ special_insn: | |||
2202 | case 0xef: /* out (e/r)ax,dx */ | 2263 | case 0xef: /* out (e/r)ax,dx */ |
2203 | port = c->regs[VCPU_REGS_RDX]; | 2264 | port = c->regs[VCPU_REGS_RDX]; |
2204 | io_dir_in = 0; | 2265 | io_dir_in = 0; |
2205 | do_io: if (kvm_emulate_pio(ctxt->vcpu, io_dir_in, | 2266 | do_io: |
2267 | if (!emulator_io_permited(ctxt, ops, port, | ||
2268 | (c->d & ByteOp) ? 1 : c->op_bytes)) { | ||
2269 | kvm_inject_gp(ctxt->vcpu, 0); | ||
2270 | goto done; | ||
2271 | } | ||
2272 | if (kvm_emulate_pio(ctxt->vcpu, io_dir_in, | ||
2206 | (c->d & ByteOp) ? 1 : c->op_bytes, | 2273 | (c->d & ByteOp) ? 1 : c->op_bytes, |
2207 | port) != 0) { | 2274 | port) != 0) { |
2208 | c->eip = saved_eip; | 2275 | c->eip = saved_eip; |
@@ -2227,13 +2294,21 @@ special_insn: | |||
2227 | c->dst.type = OP_NONE; /* Disable writeback. */ | 2294 | c->dst.type = OP_NONE; /* Disable writeback. */ |
2228 | break; | 2295 | break; |
2229 | case 0xfa: /* cli */ | 2296 | case 0xfa: /* cli */ |
2230 | ctxt->eflags &= ~X86_EFLAGS_IF; | 2297 | if (emulator_bad_iopl(ctxt)) |
2231 | c->dst.type = OP_NONE; /* Disable writeback. */ | 2298 | kvm_inject_gp(ctxt->vcpu, 0); |
2299 | else { | ||
2300 | ctxt->eflags &= ~X86_EFLAGS_IF; | ||
2301 | c->dst.type = OP_NONE; /* Disable writeback. */ | ||
2302 | } | ||
2232 | break; | 2303 | break; |
2233 | case 0xfb: /* sti */ | 2304 | case 0xfb: /* sti */ |
2234 | toggle_interruptibility(ctxt, X86_SHADOW_INT_STI); | 2305 | if (emulator_bad_iopl(ctxt)) |
2235 | ctxt->eflags |= X86_EFLAGS_IF; | 2306 | kvm_inject_gp(ctxt->vcpu, 0); |
2236 | c->dst.type = OP_NONE; /* Disable writeback. */ | 2307 | else { |
2308 | toggle_interruptibility(ctxt, X86_SHADOW_INT_STI); | ||
2309 | ctxt->eflags |= X86_EFLAGS_IF; | ||
2310 | c->dst.type = OP_NONE; /* Disable writeback. */ | ||
2311 | } | ||
2237 | break; | 2312 | break; |
2238 | case 0xfc: /* cld */ | 2313 | case 0xfc: /* cld */ |
2239 | ctxt->eflags &= ~EFLG_DF; | 2314 | ctxt->eflags &= ~EFLG_DF; |
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index ea3a8af8a478..86b739f8f173 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c | |||
@@ -3599,6 +3599,8 @@ int kvm_emulate_pio(struct kvm_vcpu *vcpu, int in, int size, unsigned port) | |||
3599 | { | 3599 | { |
3600 | unsigned long val; | 3600 | unsigned long val; |
3601 | 3601 | ||
3602 | trace_kvm_pio(!in, port, size, 1); | ||
3603 | |||
3602 | vcpu->run->exit_reason = KVM_EXIT_IO; | 3604 | vcpu->run->exit_reason = KVM_EXIT_IO; |
3603 | vcpu->run->io.direction = in ? KVM_EXIT_IO_IN : KVM_EXIT_IO_OUT; | 3605 | vcpu->run->io.direction = in ? KVM_EXIT_IO_IN : KVM_EXIT_IO_OUT; |
3604 | vcpu->run->io.size = vcpu->arch.pio.size = size; | 3606 | vcpu->run->io.size = vcpu->arch.pio.size = size; |
@@ -3610,9 +3612,6 @@ int kvm_emulate_pio(struct kvm_vcpu *vcpu, int in, int size, unsigned port) | |||
3610 | vcpu->arch.pio.down = 0; | 3612 | vcpu->arch.pio.down = 0; |
3611 | vcpu->arch.pio.rep = 0; | 3613 | vcpu->arch.pio.rep = 0; |
3612 | 3614 | ||
3613 | trace_kvm_pio(vcpu->run->io.direction == KVM_EXIT_IO_OUT, port, | ||
3614 | size, 1); | ||
3615 | |||
3616 | if (!vcpu->arch.pio.in) { | 3615 | if (!vcpu->arch.pio.in) { |
3617 | val = kvm_register_read(vcpu, VCPU_REGS_RAX); | 3616 | val = kvm_register_read(vcpu, VCPU_REGS_RAX); |
3618 | memcpy(vcpu->arch.pio_data, &val, 4); | 3617 | memcpy(vcpu->arch.pio_data, &val, 4); |
@@ -3633,6 +3632,8 @@ int kvm_emulate_pio_string(struct kvm_vcpu *vcpu, int in, | |||
3633 | unsigned now, in_page; | 3632 | unsigned now, in_page; |
3634 | int ret = 0; | 3633 | int ret = 0; |
3635 | 3634 | ||
3635 | trace_kvm_pio(!in, port, size, count); | ||
3636 | |||
3636 | vcpu->run->exit_reason = KVM_EXIT_IO; | 3637 | vcpu->run->exit_reason = KVM_EXIT_IO; |
3637 | vcpu->run->io.direction = in ? KVM_EXIT_IO_IN : KVM_EXIT_IO_OUT; | 3638 | vcpu->run->io.direction = in ? KVM_EXIT_IO_IN : KVM_EXIT_IO_OUT; |
3638 | vcpu->run->io.size = vcpu->arch.pio.size = size; | 3639 | vcpu->run->io.size = vcpu->arch.pio.size = size; |
@@ -3644,9 +3645,6 @@ int kvm_emulate_pio_string(struct kvm_vcpu *vcpu, int in, | |||
3644 | vcpu->arch.pio.down = down; | 3645 | vcpu->arch.pio.down = down; |
3645 | vcpu->arch.pio.rep = rep; | 3646 | vcpu->arch.pio.rep = rep; |
3646 | 3647 | ||
3647 | trace_kvm_pio(vcpu->run->io.direction == KVM_EXIT_IO_OUT, port, | ||
3648 | size, count); | ||
3649 | |||
3650 | if (!count) { | 3648 | if (!count) { |
3651 | kvm_x86_ops->skip_emulated_instruction(vcpu); | 3649 | kvm_x86_ops->skip_emulated_instruction(vcpu); |
3652 | return 1; | 3650 | return 1; |