[HVM] MMIO/PIO fixes and cleanups.

1. Fix MMIO/PIO cross page boundary copy for MOVS and OUTS/INS
    handling.
 2. Clean up send_mmio_req/send_pio_req interface.
 3. Clean up handle_mmio.

Signed-off-by: Xin Li <xin.b.li@intel.com>
Signed-off-by: Xiaowei Yang <xiaowei.yang@intel.com>
Signed-off-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
kfraser@localhost.localdomain committed 2006-10-18 15:13:41 +01:00
1 parent 3943db7763
commit a20cc3e41a
8 files changed
+503 -512

No files matched your search

+40 -50
View File
@@ -61,49 +61,39 @@ static inline void hvm_mmio_access(struct vcpu *v,
hvm_mmio_read_t read_handler,
hvm_mmio_write_t write_handler)
{
ioreq_t *req;
vcpu_iodata_t *vio = get_vio(v->domain, v->vcpu_id);
unsigned int tmp1, tmp2;
unsigned long data;
if (vio == NULL) {
printk("vlapic_access: bad shared page\n");
domain_crash_synchronous();
}
req = &vio->vp_ioreq;
switch (req->type) {
switch ( p->type ) {
case IOREQ_TYPE_COPY:
{
int sign = (req->df) ? -1 : 1, i;
if ( !p->pdata_valid ) {
if ( p->dir == IOREQ_READ )
p->u.data = read_handler(v, p->addr, p->size);
else /* p->dir == IOREQ_WRITE */
write_handler(v, p->addr, p->size, p->u.data);
} else { /* !p->pdata_valid */
int i, sign = (p->df) ? -1 : 1;
if (!req->pdata_valid) {
if (req->dir == IOREQ_READ){
req->u.data = read_handler(v, req->addr, req->size);
} else { /* req->dir != IOREQ_READ */
write_handler(v, req->addr, req->size, req->u.data);
}
} else { /* !req->pdata_valid */
if (req->dir == IOREQ_READ) {
for (i = 0; i < req->count; i++) {
if ( p->dir == IOREQ_READ ) {
for ( i = 0; i < p->count; i++ ) {
data = read_handler(v,
req->addr + (sign * i * req->size),
req->size);
(void)hvm_copy_to_guest_virt(
(unsigned long)p->u.pdata + (sign * i * req->size),
p->addr + (sign * i * p->size),
p->size);
(void)hvm_copy_to_guest_phys(
(unsigned long)p->u.pdata + (sign * i * p->size),
&data,
p->size);
}
} else { /* !req->dir == IOREQ_READ */
for (i = 0; i < req->count; i++) {
(void)hvm_copy_from_guest_virt(
} else {/* p->dir == IOREQ_WRITE */
for ( i = 0; i < p->count; i++ ) {
(void)hvm_copy_from_guest_phys(
&data,
(unsigned long)p->u.pdata + (sign * i * req->size),
(unsigned long)p->u.pdata + (sign * i * p->size),
p->size);
write_handler(v,
req->addr + (sign * i * req->size),
req->size, data);
p->addr + (sign * i * p->size),
p->size, data);
}
}
}
@@ -111,44 +101,44 @@ static inline void hvm_mmio_access(struct vcpu *v,
}
case IOREQ_TYPE_AND:
tmp1 = read_handler(v, req->addr, req->size);
if (req->dir == IOREQ_WRITE) {
tmp2 = tmp1 & (unsigned long) req->u.data;
write_handler(v, req->addr, req->size, tmp2);
tmp1 = read_handler(v, p->addr, p->size);
if ( p->dir == IOREQ_WRITE ) {
tmp2 = tmp1 & (unsigned long) p->u.data;
write_handler(v, p->addr, p->size, tmp2);
}
req->u.data = tmp1;
p->u.data = tmp1;
break;
case IOREQ_TYPE_OR:
tmp1 = read_handler(v, req->addr, req->size);
if (req->dir == IOREQ_WRITE) {
tmp2 = tmp1 | (unsigned long) req->u.data;
write_handler(v, req->addr, req->size, tmp2);
tmp1 = read_handler(v, p->addr, p->size);
if ( p->dir == IOREQ_WRITE ) {
tmp2 = tmp1 | (unsigned long) p->u.data;
write_handler(v, p->addr, p->size, tmp2);
}
req->u.data = tmp1;
p->u.data = tmp1;
break;
case IOREQ_TYPE_XOR:
tmp1 = read_handler(v, req->addr, req->size);
if (req->dir == IOREQ_WRITE) {
tmp2 = tmp1 ^ (unsigned long) req->u.data;
write_handler(v, req->addr, req->size, tmp2);
tmp1 = read_handler(v, p->addr, p->size);
if ( p->dir == IOREQ_WRITE ) {
tmp2 = tmp1 ^ (unsigned long) p->u.data;
write_handler(v, p->addr, p->size, tmp2);
}
req->u.data = tmp1;
p->u.data = tmp1;
break;
case IOREQ_TYPE_XCHG:
/*
/*
* Note that we don't need to be atomic here since VCPU is accessing
* its own local APIC.
*/
tmp1 = read_handler(v, req->addr, req->size);
write_handler(v, req->addr, req->size, (unsigned long) req->u.data);
req->u.data = tmp1;
tmp1 = read_handler(v, p->addr, p->size);
write_handler(v, p->addr, p->size, (unsigned long) p->u.data);
p->u.data = tmp1;
break;
default:
printk("error ioreq type for local APIC %x\n", req->type);
printk("hvm_mmio_access: error ioreq type %x\n", p->type);
domain_crash_synchronous();
break;
}
+20 -17
View File
@@ -369,18 +369,18 @@ static void hvm_pio_assist(struct cpu_user_regs *regs, ioreq_t *p,
{
if ( pio_opp->flags & REPZ )
regs->ecx -= p->count;
if ( p->dir == IOREQ_READ )
{
regs->edi += sign * p->count * p->size;
if ( pio_opp->flags & OVERLAP )
{
unsigned long addr = regs->edi;
if (hvm_realmode(current))
addr += regs->es << 4;
if (sign > 0)
addr -= p->size;
(void)hvm_copy_to_guest_virt(addr, &p->u.data, p->size);
unsigned long addr = pio_opp->addr;
if ( hvm_paging_enabled(current) )
(void)hvm_copy_to_guest_virt(addr, &p->u.data, p->size);
else
(void)hvm_copy_to_guest_phys(addr, &p->u.data, p->size);
}
regs->edi += sign * p->count * p->size;
}
else /* p->dir == IOREQ_WRITE */
{
@@ -485,19 +485,22 @@ static void hvm_mmio_assist(struct cpu_user_regs *regs, ioreq_t *p,
case INSTR_MOVS:
sign = p->df ? -1 : 1;
regs->esi += sign * p->count * p->size;
regs->edi += sign * p->count * p->size;
if ((mmio_opp->flags & OVERLAP) && p->dir == IOREQ_READ) {
unsigned long addr = regs->edi;
if (sign > 0)
addr -= p->size;
(void)hvm_copy_to_guest_virt(addr, &p->u.data, p->size);
}
if (mmio_opp->flags & REPZ)
regs->ecx -= p->count;
if ((mmio_opp->flags & OVERLAP) && p->dir == IOREQ_READ) {
unsigned long addr = mmio_opp->addr;
if (hvm_paging_enabled(current))
(void)hvm_copy_to_guest_virt(addr, &p->u.data, p->size);
else
(void)hvm_copy_to_guest_phys(addr, &p->u.data, p->size);
}
regs->esi += sign * p->count * p->size;
regs->edi += sign * p->count * p->size;
break;
case INSTR_STOS:
File diff suppressed because it is too large. Load diff
+23 -11
View File
@@ -59,8 +59,6 @@ extern int inst_copy_from_guest(unsigned char *buf, unsigned long guest_eip,
int inst_len);
extern uint32_t vlapic_update_ppr(struct vlapic *vlapic);
extern asmlinkage void do_IRQ(struct cpu_user_regs *);
extern void send_pio_req(struct cpu_user_regs *regs, unsigned long port,
unsigned long count, int size, long value, int dir, int pvalid);
extern void svm_dump_inst(unsigned long eip);
extern int svm_dbg_on;
void svm_dump_regs(const char *from, struct cpu_user_regs *regs);
@@ -1410,7 +1408,7 @@ static void svm_io_instruction(struct vcpu *v)
struct cpu_user_regs *regs;
struct hvm_io_op *pio_opp;
unsigned int port;
unsigned int size, dir;
unsigned int size, dir, df;
ioio_info_t info;
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
@@ -1429,6 +1427,8 @@ static void svm_io_instruction(struct vcpu *v)
port = info.fields.port; /* port used to be addr */
dir = info.fields.type; /* direction */
df = regs->eflags & X86_EFLAGS_DF ? 1 : 0;
if (info.fields.sz32)
size = 4;
else if (info.fields.sz16)
@@ -1445,7 +1445,7 @@ static void svm_io_instruction(struct vcpu *v)
if (info.fields.str)
{
unsigned long addr, count;
int sign = regs->eflags & EF_DF ? -1 : 1;
int sign = regs->eflags & X86_EFLAGS_DF ? -1 : 1;
if (!svm_get_io_address(v, regs, dir, &count, &addr))
{
@@ -1475,25 +1475,37 @@ static void svm_io_instruction(struct vcpu *v)
unsigned long value = 0;
pio_opp->flags |= OVERLAP;
pio_opp->addr = addr;
if (dir == IOREQ_WRITE)
(void)hvm_copy_from_guest_virt(&value, addr, size);
if (dir == IOREQ_WRITE) /* OUTS */
{
if (hvm_paging_enabled(current))
(void)hvm_copy_from_guest_virt(&value, addr, size);
else
(void)hvm_copy_from_guest_phys(&value, addr, size);
}
send_pio_req(regs, port, 1, size, value, dir, 0);
if (count == 1)
regs->eip = vmcb->exitinfo2;
send_pio_req(port, 1, size, value, dir, df, 0);
}
else
{
if ((addr & PAGE_MASK) != ((addr + count * size - 1) & PAGE_MASK))
unsigned long last_addr = sign > 0 ? addr + count * size - 1
: addr - (count - 1) * size;
if ((addr & PAGE_MASK) != (last_addr & PAGE_MASK))
{
if (sign > 0)
count = (PAGE_SIZE - (addr & ~PAGE_MASK)) / size;
else
count = (addr & ~PAGE_MASK) / size;
count = (addr & ~PAGE_MASK) / size + 1;
}
else
regs->eip = vmcb->exitinfo2;
send_pio_req(regs, port, count, size, addr, dir, 1);
send_pio_req(port, count, size, addr, dir, df, 1);
}
}
else
@@ -1507,7 +1519,7 @@ static void svm_io_instruction(struct vcpu *v)
if (port == 0xe9 && dir == IOREQ_WRITE && size == 1)
hvm_print_line(v, regs->eax); /* guest debug output */
send_pio_req(regs, port, 1, size, regs->eax, dir, 0);
send_pio_req(port, 1, size, regs->eax, dir, df, 0);
}
}
+48 -34
View File
@@ -1041,14 +1041,20 @@ static void vmx_vmexit_do_invlpg(unsigned long va)
}
static int check_for_null_selector(unsigned long eip)
static int check_for_null_selector(unsigned long eip, int inst_len, int dir)
{
unsigned char inst[MAX_INST_LEN];
unsigned long sel;
int i, inst_len;
int i;
int inst_copy_from_guest(unsigned char *, unsigned long, int);
inst_len = __get_instruction_length(); /* Safe: INS/OUTS */
/* INS can only use ES segment register, and it can't be overridden */
if ( dir == IOREQ_READ )
{
__vmread(GUEST_ES_SELECTOR, &sel);
return sel == 0 ? 1 : 0;
}
memset(inst, 0, MAX_INST_LEN);
if ( inst_copy_from_guest(inst, eip, inst_len) != inst_len )
{
@@ -1093,18 +1099,13 @@ static int check_for_null_selector(unsigned long eip)
return 0;
}
extern void send_pio_req(struct cpu_user_regs *regs, unsigned long port,
unsigned long count, int size, long value,
int dir, int pvalid);
static void vmx_io_instruction(unsigned long exit_qualification,
unsigned long inst_len)
{
struct cpu_user_regs *regs;
struct hvm_io_op *pio_opp;
unsigned long eip, cs, eflags;
unsigned long port, size, dir;
int vm86;
unsigned long port, size;
int dir, df, vm86;
pio_opp = &current->arch.hvm_vcpu.io_op;
pio_opp->instr = INSTR_PIO;
@@ -1116,28 +1117,26 @@ static void vmx_io_instruction(unsigned long exit_qualification,
memcpy(regs, guest_cpu_user_regs(), HVM_CONTEXT_STACK_BYTES);
hvm_store_cpu_guest_regs(current, regs, NULL);
eip = regs->eip;
cs = regs->cs;
eflags = regs->eflags;
vm86 = regs->eflags & X86_EFLAGS_VM ? 1 : 0;
df = regs->eflags & X86_EFLAGS_DF ? 1 : 0;
vm86 = eflags & X86_EFLAGS_VM ? 1 : 0;
HVM_DBG_LOG(DBG_LEVEL_IO,
"vmx_io_instruction: vm86 %d, eip=%lx:%lx, "
HVM_DBG_LOG(DBG_LEVEL_IO, "vm86 %d, eip=%x:%lx, "
"exit_qualification = %lx",
vm86, cs, eip, exit_qualification);
vm86, regs->cs, (unsigned long)regs->eip, exit_qualification);
if (test_bit(6, &exit_qualification))
if ( test_bit(6, &exit_qualification) )
port = (exit_qualification >> 16) & 0xFFFF;
else
port = regs->edx & 0xffff;
TRACE_VMEXIT(1, port);
TRACE_VMEXIT(1,port);
size = (exit_qualification & 7) + 1;
dir = test_bit(3, &exit_qualification); /* direction */
if (test_bit(4, &exit_qualification)) { /* string instruction */
if ( test_bit(4, &exit_qualification) ) { /* string instruction */
unsigned long addr, count = 1;
int sign = regs->eflags & EF_DF ? -1 : 1;
int sign = regs->eflags & X86_EFLAGS_DF ? -1 : 1;
__vmread(GUEST_LINEAR_ADDRESS, &addr);
@@ -1145,10 +1144,10 @@ static void vmx_io_instruction(unsigned long exit_qualification,
* In protected mode, guest linear address is invalid if the
* selector is null.
*/
if (!vm86 && check_for_null_selector(eip))
if ( !vm86 && check_for_null_selector(regs->eip, inst_len, dir) )
addr = dir == IOREQ_WRITE ? regs->esi : regs->edi;
if (test_bit(5, &exit_qualification)) { /* "rep" prefix */
if ( test_bit(5, &exit_qualification) ) { /* "rep" prefix */
pio_opp->flags |= REPZ;
count = vm86 ? regs->ecx & 0xFFFF : regs->ecx;
}
@@ -1157,30 +1156,45 @@ static void vmx_io_instruction(unsigned long exit_qualification,
* Handle string pio instructions that cross pages or that
* are unaligned. See the comments in hvm_domain.c/handle_mmio()
*/
if ((addr & PAGE_MASK) != ((addr + size - 1) & PAGE_MASK)) {
if ( (addr & PAGE_MASK) != ((addr + size - 1) & PAGE_MASK) ) {
unsigned long value = 0;
pio_opp->flags |= OVERLAP;
if (dir == IOREQ_WRITE)
(void)hvm_copy_from_guest_virt(&value, addr, size);
send_pio_req(regs, port, 1, size, value, dir, 0);
if ( dir == IOREQ_WRITE ) /* OUTS */
{
if ( hvm_paging_enabled(current) )
(void)hvm_copy_from_guest_virt(&value, addr, size);
else
(void)hvm_copy_from_guest_phys(&value, addr, size);
} else
pio_opp->addr = addr;
if ( count == 1 )
regs->eip += inst_len;
send_pio_req(port, 1, size, value, dir, df, 0);
} else {
if ((addr & PAGE_MASK) != ((addr + count * size - 1) & PAGE_MASK)) {
if (sign > 0)
unsigned long last_addr = sign > 0 ? addr + count * size - 1
: addr - (count - 1) * size;
if ( (addr & PAGE_MASK) != (last_addr & PAGE_MASK) )
{
if ( sign > 0 )
count = (PAGE_SIZE - (addr & ~PAGE_MASK)) / size;
else
count = (addr & ~PAGE_MASK) / size;
count = (addr & ~PAGE_MASK) / size + 1;
} else
regs->eip += inst_len;
send_pio_req(regs, port, count, size, addr, dir, 1);
send_pio_req(port, count, size, addr, dir, df, 1);
}
} else {
if (port == 0xe9 && dir == IOREQ_WRITE && size == 1)
if ( port == 0xe9 && dir == IOREQ_WRITE && size == 1 )
hvm_print_line(current, regs->eax); /* guest debug output */
regs->eip += inst_len;
send_pio_req(regs, port, 1, size, regs->eax, dir, 0);
send_pio_req(port, 1, size, regs->eax, dir, df, 0);
}
}
+1 -1
View File
@@ -2880,7 +2880,7 @@ static int sh_page_fault(struct vcpu *v,
shadow_audit_tables(v);
reset_early_unshadow(v);
shadow_unlock(d);
handle_mmio(va, gpa);
handle_mmio(gpa);
return EXCRET_fault_fixed;
not_a_shadow_fault:
+17 -25
View File
@@ -25,11 +25,6 @@
#include <public/hvm/ioreq.h>
#include <public/event_channel.h>
#define MAX_OPERAND_NUM 2
#define mk_operand(size_reg, index, seg, flag) \
(((size_reg) << 24) | ((index) << 16) | ((seg) << 8) | (flag))
#define operand_size(operand) \
((operand >> 24) & 0xFF)
@@ -70,29 +65,23 @@
#define INSTR_XCHG 14
#define INSTR_SUB 15
struct instruction {
__s8 instr; /* instruction type */
__s16 op_size; /* the operand's bit size, e.g. 16-bit or 32-bit */
__u64 immediate;
__u16 seg_sel; /* segmentation selector */
__u32 operand[MAX_OPERAND_NUM]; /* order is AT&T assembly */
__u32 flags;
};
#define MAX_INST_LEN 15 /* Maximum instruction length = 15 bytes */
struct hvm_io_op {
int flags;
int instr; /* instruction */
unsigned long operand[2]; /* operands */
unsigned long immediate; /* immediate portion */
struct cpu_user_regs io_context; /* current context */
unsigned int instr; /* instruction */
unsigned int flags;
unsigned long addr; /* virt addr for overlap PIO/MMIO */
struct {
unsigned int operand[2]; /* operands */
unsigned long immediate; /* immediate portion */
};
struct cpu_user_regs io_context; /* current context */
};
#define MAX_IO_HANDLER 8
#define VMX_PORTIO 0
#define VMX_MMIO 1
#define HVM_PORTIO 0
#define HVM_MMIO 1
typedef int (*intercept_action_t)(ioreq_t *);
typedef unsigned long (*hvm_mmio_read_t)(struct vcpu *v,
@@ -131,16 +120,17 @@ extern int register_io_handler(unsigned long addr, unsigned long size,
static inline int hvm_portio_intercept(ioreq_t *p)
{
return hvm_io_intercept(p, VMX_PORTIO);
return hvm_io_intercept(p, HVM_PORTIO);
}
int hvm_mmio_intercept(ioreq_t *p);
extern int hvm_mmio_intercept(ioreq_t *p);
extern int hvm_buffered_io_intercept(ioreq_t *p);
static inline int register_portio_handler(unsigned long addr,
unsigned long size,
intercept_action_t action)
{
return register_io_handler(addr, size, action, VMX_PORTIO);
return register_io_handler(addr, size, action, HVM_PORTIO);
}
#if defined(__i386__) || defined(__x86_64__)
@@ -150,7 +140,9 @@ static inline int irq_masked(unsigned long eflags)
}
#endif
extern void handle_mmio(unsigned long, unsigned long);
extern void send_pio_req(unsigned long port, unsigned long count, int size,
long value, int dir, int df, int pvalid);
extern void handle_mmio(unsigned long gpa);
extern void hvm_interrupt_post(struct vcpu *v, int vector, int type);
extern void hvm_io_assist(struct vcpu *v);
extern void pic_irq_request(void *data, int level);
-4
View File
@@ -142,10 +142,6 @@ int hvm_copy_to_guest_virt(unsigned long vaddr, void *buf, int size);
int hvm_copy_from_guest_virt(void *buf, unsigned long vaddr, int size);
void hvm_setup_platform(struct domain* d);
int hvm_mmio_intercept(ioreq_t *p);
int hvm_io_intercept(ioreq_t *p, int type);
int hvm_buffered_io_intercept(ioreq_t *p);
void hvm_hooks_assist(struct vcpu *v);
void hvm_print_line(struct vcpu *v, const char c);
void hlt_timer_fn(void *data);
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.