aboutsummaryrefslogtreecommitdiffstats
path: root/xen/arch/x86/traps.c
diff options
context:
space:
mode:
Diffstat (limited to 'xen/arch/x86/traps.c')
-rw-r--r--xen/arch/x86/traps.c295
1 files changed, 178 insertions, 117 deletions
diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c
index 019e3e56cf..5e39c9b417 100644
--- a/xen/arch/x86/traps.c
+++ b/xen/arch/x86/traps.c
@@ -1305,23 +1305,24 @@ static int read_gate_descriptor(unsigned int gate_sel,
const struct desc_struct *pdesc;
- pdesc = (const struct desc_struct *)(!(gate_sel & 4) ?
- GDT_VIRT_START(v) :
- LDT_VIRT_START(v))
- + (gate_sel >> 3);
- if ( gate_sel < 4 ||
- (gate_sel >= FIRST_RESERVED_GDT_BYTE && !(gate_sel & 4)) ||
+ pdesc = (const struct desc_struct *)
+ (!(gate_sel & 4) ? GDT_VIRT_START(v) : LDT_VIRT_START(v))
+ + (gate_sel >> 3);
+ if ( (gate_sel < 4) ||
+ ((gate_sel >= FIRST_RESERVED_GDT_BYTE) && !(gate_sel & 4)) ||
__get_user(desc, pdesc) )
return 0;
*sel = (desc.a >> 16) & 0x0000fffc;
*off = (desc.a & 0x0000ffff) | (desc.b & 0xffff0000);
*ar = desc.b & 0x0000ffff;
+
/*
* check_descriptor() clears the DPL field and stores the
* guest requested DPL in the selector's RPL field.
*/
- ASSERT(!(*ar & _SEGMENT_DPL));
+ if ( *ar & _SEGMENT_DPL )
+ return 0;
*ar |= (desc.a >> (16 - 13)) & _SEGMENT_DPL;
if ( !is_pv_32bit_vcpu(v) )
@@ -1352,7 +1353,7 @@ static int read_gate_descriptor(unsigned int gate_sel,
#endif
/* Has the guest requested sufficient permission for this I/O access? */
-static inline int guest_io_okay(
+static int guest_io_okay(
unsigned int port, unsigned int bytes,
struct vcpu *v, struct cpu_user_regs *regs)
{
@@ -1394,19 +1395,130 @@ static inline int guest_io_okay(
}
/* Has the administrator granted sufficient permission for this I/O access? */
-static inline int admin_io_okay(
+static int admin_io_okay(
unsigned int port, unsigned int bytes,
struct vcpu *v, struct cpu_user_regs *regs)
{
return ioports_access_permitted(v->domain, port, port + bytes - 1);
}
-#define guest_inb_okay(_p, _d, _r) admin_io_okay(_p, 1, _d, _r)
-#define guest_inw_okay(_p, _d, _r) admin_io_okay(_p, 2, _d, _r)
-#define guest_inl_okay(_p, _d, _r) admin_io_okay(_p, 4, _d, _r)
-#define guest_outb_okay(_p, _d, _r) admin_io_okay(_p, 1, _d, _r)
-#define guest_outw_okay(_p, _d, _r) admin_io_okay(_p, 2, _d, _r)
-#define guest_outl_okay(_p, _d, _r) admin_io_okay(_p, 4, _d, _r)
+static uint32_t guest_io_read(
+ unsigned int port, unsigned int bytes,
+ struct vcpu *v, struct cpu_user_regs *regs)
+{
+ extern uint32_t pci_conf_read(
+ uint32_t cf8, uint8_t offset, uint8_t bytes);
+
+ uint32_t data = 0;
+ unsigned int shift = 0;
+
+ if ( admin_io_okay(port, bytes, v, regs) )
+ {
+ switch ( bytes )
+ {
+ case 1: return inb(port);
+ case 2: return inw(port);
+ case 4: return inl(port);
+ }
+ }
+
+ while ( bytes != 0 )
+ {
+ unsigned int size = 1;
+ uint32_t sub_data = 0xff;
+
+ if ( (port == 0x42) || (port == 0x43) || (port == 0x61) )
+ {
+ sub_data = pv_pit_handler(port, 0, 0);
+ }
+ else if ( (port & 0xfffc) == 0xcf8 )
+ {
+ size = min(bytes, 4 - (port & 3));
+ sub_data = v->domain->arch.pci_cf8 >> ((port & 3) * 8);
+ }
+ else if ( ((port & 0xfffc) == 0xcfc) && IS_PRIV(v->domain) )
+ {
+ size = min(bytes, 4 - (port & 3));
+ if ( size == 3 )
+ size = 2;
+ sub_data = pci_conf_read(v->domain->arch.pci_cf8, port & 3, size);
+ }
+
+ if ( size == 4 )
+ return sub_data;
+
+ data |= (sub_data & ((1u << (size * 8)) - 1)) << shift;
+ shift += size * 8;
+ port += size;
+ bytes -= size;
+ }
+
+ return data;
+}
+
+static void guest_io_write(
+ unsigned int port, unsigned int bytes, uint32_t data,
+ struct vcpu *v, struct cpu_user_regs *regs)
+{
+ extern void pci_conf_write(
+ uint32_t cf8, uint8_t offset, uint8_t bytes, uint32_t data);
+
+ if ( admin_io_okay(port, bytes, v, regs) )
+ {
+ switch ( bytes ) {
+ case 1:
+ outb((uint8_t)data, port);
+ if ( pv_post_outb_hook )
+ pv_post_outb_hook(port, (uint8_t)data);
+ break;
+ case 2:
+ outw((uint16_t)data, port);
+ break;
+ case 4:
+ outl(data, port);
+ break;
+ }
+ return;
+ }
+
+ while ( bytes != 0 )
+ {
+ unsigned int size = 1;
+
+ if ( (port == 0x42) || (port == 0x43) || (port == 0x61) )
+ {
+ pv_pit_handler(port, (uint8_t)data, 1);
+ }
+ else if ( (port & 0xfffc) == 0xcf8 )
+ {
+ size = min(bytes, 4 - (port & 3));
+ if ( size == 4 )
+ {
+ v->domain->arch.pci_cf8 = data;
+ }
+ else
+ {
+ uint32_t mask = ((1u << (size * 8)) - 1) << ((port & 3) * 8);
+ v->domain->arch.pci_cf8 &= ~mask;
+ v->domain->arch.pci_cf8 |= (data << ((port & 3) * 8)) & mask;
+ }
+ }
+ else if ( ((port & 0xfffc) == 0xcfc) && IS_PRIV(v->domain) )
+ {
+ size = min(bytes, 4 - (port & 3));
+ if ( size == 3 )
+ size = 2;
+ pci_conf_write(v->domain->arch.pci_cf8, port & 3, size, data);
+ }
+
+ if ( size == 4 )
+ return;
+
+ port += size;
+ bytes -= size;
+ data >>= size * 8;
+ }
+}
/* I/O emulation support. Helper routines for, and type of, the stack stub.*/
void host_to_guest_gpr_switch(struct cpu_user_regs *)
@@ -1525,7 +1637,7 @@ static int emulate_privileged_op(struct cpu_user_regs *regs)
/* REX prefix. */
if ( rex & 8 ) /* REX.W */
- op_bytes = 4; /* emulating only opcodes not supporting 64-bit operands */
+ op_bytes = 4; /* emulate only opcodes not supporting 64-bit operands */
modrm_reg = (rex & 4) << 1; /* REX.R */
/* REX.X does not need to be decoded. */
modrm_rm = (rex & 1) << 3; /* REX.B */
@@ -1554,7 +1666,8 @@ static int emulate_privileged_op(struct cpu_user_regs *regs)
{
if ( !read_descriptor(data_sel, v, regs,
&data_base, &data_limit, &ar,
- _SEGMENT_WR|_SEGMENT_S|_SEGMENT_DPL|_SEGMENT_P) )
+ _SEGMENT_WR|_SEGMENT_S|_SEGMENT_DPL|
+ _SEGMENT_P) )
goto fail;
if ( !(ar & _SEGMENT_S) ||
!(ar & _SEGMENT_P) ||
@@ -1601,69 +1714,39 @@ static int emulate_privileged_op(struct cpu_user_regs *regs)
case 0x6c: /* INSB */
op_bytes = 1;
case 0x6d: /* INSW/INSL */
- if ( data_limit < op_bytes - 1 ||
- rd_ad(edi) > data_limit - (op_bytes - 1) ||
+ if ( (data_limit < (op_bytes - 1)) ||
+ (rd_ad(edi) > (data_limit - (op_bytes - 1))) ||
!guest_io_okay(port, op_bytes, v, regs) )
goto fail;
- switch ( op_bytes )
- {
- case 1:
- /* emulate PIT counter 2 */
- data = (u8)(guest_inb_okay(port, v, regs) ? inb(port) :
- ((port == 0x42 || port == 0x43 || port == 0x61) ?
- pv_pit_handler(port, 0, 0) : ~0));
- break;
- case 2:
- data = (u16)(guest_inw_okay(port, v, regs) ? inw(port) : ~0);
- break;
- case 4:
- data = (u32)(guest_inl_okay(port, v, regs) ? inl(port) : ~0);
- break;
- }
- if ( (rc = copy_to_user((void *)data_base + rd_ad(edi), &data, op_bytes)) != 0 )
+ data = guest_io_read(port, op_bytes, v, regs);
+ if ( (rc = copy_to_user((void *)data_base + rd_ad(edi),
+ &data, op_bytes)) != 0 )
{
propagate_page_fault(data_base + rd_ad(edi) + op_bytes - rc,
PFEC_write_access);
return EXCRET_fault_fixed;
}
- wr_ad(edi, regs->edi + (int)((regs->eflags & EF_DF) ? -op_bytes : op_bytes));
+ wr_ad(edi, regs->edi + (int)((regs->eflags & EF_DF)
+ ? -op_bytes : op_bytes));
break;
case 0x6e: /* OUTSB */
op_bytes = 1;
case 0x6f: /* OUTSW/OUTSL */
- if ( data_limit < op_bytes - 1 ||
- rd_ad(esi) > data_limit - (op_bytes - 1) ||
- !guest_io_okay(port, op_bytes, v, regs) )
+ if ( (data_limit < (op_bytes - 1)) ||
+ (rd_ad(esi) > (data_limit - (op_bytes - 1))) ||
+ !guest_io_okay(port, op_bytes, v, regs) )
goto fail;
- rc = copy_from_user(&data, (void *)data_base + rd_ad(esi), op_bytes);
- if ( rc != 0 )
+ if ( (rc = copy_from_user(&data, (void *)data_base + rd_ad(esi),
+ op_bytes)) != 0 )
{
- propagate_page_fault(data_base + rd_ad(esi) + op_bytes - rc, 0);
+ propagate_page_fault(data_base + rd_ad(esi)
+ + op_bytes - rc, 0);
return EXCRET_fault_fixed;
}
- switch ( op_bytes )
- {
- case 1:
- if ( guest_outb_okay(port, v, regs) )
- {
- outb((u8)data, port);
- if ( pv_post_outb_hook )
- pv_post_outb_hook(port, data);
- }
- else if ( port == 0x42 || port == 0x43 || port == 0x61 )
- pv_pit_handler(port, data, 1);
- break;
- case 2:
- if ( guest_outw_okay(port, v, regs) )
- outw((u16)data, port);
- break;
- case 4:
- if ( guest_outl_okay(port, v, regs) )
- outl((u32)data, port);
- break;
- }
- wr_ad(esi, regs->esi + (int)((regs->eflags & EF_DF) ? -op_bytes : op_bytes));
+ guest_io_write(port, op_bytes, data, v, regs);
+ wr_ad(esi, regs->esi + (int)((regs->eflags & EF_DF)
+ ? -op_bytes : op_bytes));
break;
}
@@ -1727,31 +1810,17 @@ static int emulate_privileged_op(struct cpu_user_regs *regs)
exec_in:
if ( !guest_io_okay(port, op_bytes, v, regs) )
goto fail;
- switch ( op_bytes )
+ if ( admin_io_okay(port, op_bytes, v, regs) )
{
- case 1:
- if ( guest_inb_okay(port, v, regs) )
- io_emul(regs);
- else if ( port == 0x42 || port == 0x43 || port == 0x61 )
- {
- regs->eax &= ~0xffUL;
- regs->eax |= pv_pit_handler(port, 0, 0);
- }
- else
- regs->eax |= (u8)~0;
- break;
- case 2:
- if ( guest_inw_okay(port, v, regs) )
- io_emul(regs);
- else
- regs->eax |= (u16)~0;
- break;
- case 4:
- if ( guest_inl_okay(port, v, regs) )
- io_emul(regs);
+ io_emul(regs);
+ }
+ else
+ {
+ if ( op_bytes == 4 )
+ regs->eax = 0;
else
- regs->eax = (u32)~0;
- break;
+ regs->eax &= ~((1u << (op_bytes * 8)) - 1);
+ regs->eax |= guest_io_read(port, op_bytes, v, regs);
}
bpmatch = check_guest_io_breakpoint(v, port, op_bytes);
goto done;
@@ -1770,26 +1839,15 @@ static int emulate_privileged_op(struct cpu_user_regs *regs)
exec_out:
if ( !guest_io_okay(port, op_bytes, v, regs) )
goto fail;
- switch ( op_bytes )
+ if ( admin_io_okay(port, op_bytes, v, regs) )
{
- case 1:
- if ( guest_outb_okay(port, v, regs) )
- {
- io_emul(regs);
- if ( pv_post_outb_hook )
- pv_post_outb_hook(port, regs->eax);
- }
- else if ( port == 0x42 || port == 0x43 || port == 0x61 )
- pv_pit_handler(port, regs->eax, 1);
- break;
- case 2:
- if ( guest_outw_okay(port, v, regs) )
- io_emul(regs);
- break;
- case 4:
- if ( guest_outl_okay(port, v, regs) )
- io_emul(regs);
- break;
+ io_emul(regs);
+ if ( (op_bytes == 1) && pv_post_outb_hook )
+ pv_post_outb_hook(port, regs->eax);
+ }
+ else
+ {
+ guest_io_write(port, op_bytes, regs->eax, v, regs);
}
bpmatch = check_guest_io_breakpoint(v, port, op_bytes);
goto done;
@@ -1921,14 +1979,14 @@ static int emulate_privileged_op(struct cpu_user_regs *regs)
break;
case 3: /* Write CR3 */
- LOCK_BIGLOCK(v->domain);
+ domain_lock(v->domain);
if ( !is_pv_32on64_vcpu(v) )
rc = new_guest_cr3(gmfn_to_mfn(v->domain, xen_cr3_to_pfn(*reg)));
#ifdef CONFIG_COMPAT
else
rc = new_guest_cr3(gmfn_to_mfn(v->domain, compat_cr3_to_pfn(*reg)));
#endif
- UNLOCK_BIGLOCK(v->domain);
+ domain_unlock(v->domain);
if ( rc == 0 ) /* not okay */
goto fail;
break;
@@ -2137,8 +2195,8 @@ static void emulate_gate_op(struct cpu_user_regs *regs)
/* Check whether this fault is due to the use of a call gate. */
if ( !read_gate_descriptor(regs->error_code, v, &sel, &off, &ar) ||
- ((ar >> 13) & 3) < (regs->cs & 3) ||
- (ar & _SEGMENT_TYPE) != 0xc00 )
+ (((ar >> 13) & 3) < (regs->cs & 3)) ||
+ ((ar & _SEGMENT_TYPE) != 0xc00) )
{
do_guest_trap(TRAP_gp_fault, regs, 1);
return;
@@ -2232,15 +2290,18 @@ static void emulate_gate_op(struct cpu_user_regs *regs)
{
if ( (modrm & 7) == 4 )
{
- unsigned int sib = insn_fetch(u8, base, eip, limit);
+ unsigned int sib;
+ sib = insn_fetch(u8, base, eip, limit);
modrm = (modrm & ~7) | (sib & 7);
if ( (sib >>= 3) != 4 )
- opnd_off = *(unsigned long *)decode_register(sib & 7, regs, 0);
+ opnd_off = *(unsigned long *)
+ decode_register(sib & 7, regs, 0);
opnd_off <<= sib >> 3;
}
if ( (modrm & 7) != 5 || (modrm & 0xc0) )
- opnd_off += *(unsigned long *)decode_register(modrm & 7, regs, 0);
+ opnd_off += *(unsigned long *)
+ decode_register(modrm & 7, regs, 0);
else
modrm |= 0x87;
if ( !opnd_sel )
@@ -2576,12 +2637,14 @@ asmlinkage void do_general_protection(struct cpu_user_regs *regs)
panic("GENERAL PROTECTION FAULT\n[error_code=%04x]\n", regs->error_code);
}
-static void nmi_softirq(void)
+static void nmi_action(unsigned long unused)
{
/* Only used to defer wakeup of dom0,vcpu0 to a safe (non-NMI) context. */
vcpu_kick(dom0->vcpu[0]);
}
+static DECLARE_TASKLET(nmi_tasklet, nmi_action, 0);
+
static void nmi_dom0_report(unsigned int reason_idx)
{
struct domain *d;
@@ -2593,7 +2656,7 @@ static void nmi_dom0_report(unsigned int reason_idx)
set_bit(reason_idx, nmi_reason(d));
if ( !test_and_set_bool(v->nmi_pending) )
- raise_softirq(NMI_SOFTIRQ); /* not safe to wake up a vcpu here */
+ tasklet_schedule(&nmi_tasklet); /* not safe to wake a vcpu here */
}
asmlinkage void mem_parity_error(struct cpu_user_regs *regs)
@@ -2871,8 +2934,6 @@ void __init trap_init(void)
percpu_traps_init();
cpu_init();
-
- open_softirq(NMI_SOFTIRQ, nmi_softirq);
}
long register_guest_nmi_callback(unsigned long address)