mirror of
https://github.com/xen-project/xen.git
synced 2026-10-30 03:00:05 +01:00
Fix NMI race with context switch.
The machine used to auto reboot if an NMI was received in a critical time window when context switching domains. There is a small time window when the GDT may become unmapped (after CR3 is updated and before setting GDTR with the new GDT during a domain context switch. If an NMI is received during this time window a triple fault is triggered causing the machine to auto reboot. Bug found and original patch proposed by Jose Renato Santos <jsantos@hpl.hp.com>. Signed-off-by: Keir Fraser <keir@xensource.com>
This commit is contained in:
1 parent
81e869fdc6
commit
27a9d6d614
2 files changed
+28
-9
No files matched your search
+16
-5
@@ -226,11 +226,9 @@ struct vcpu *alloc_vcpu_struct(struct domain *d, unsigned int vcpu_id)
|
||||
|
||||
if ( (v->vcpu_id = vcpu_id) != 0 )
|
||||
{
|
||||
v->arch.schedule_tail = d->vcpu[0]->arch.schedule_tail;
|
||||
v->arch.schedule_tail = d->vcpu[0]->arch.schedule_tail;
|
||||
v->arch.perdomain_ptes =
|
||||
d->arch.mm_perdomain_pt + (vcpu_id << PDPT_VCPU_SHIFT);
|
||||
v->arch.perdomain_ptes[FIRST_RESERVED_GDT_PAGE] =
|
||||
l1e_from_page(virt_to_page(gdt_table), PAGE_HYPERVISOR);
|
||||
}
|
||||
|
||||
return v;
|
||||
@@ -256,6 +254,7 @@ void free_perdomain_pt(struct domain *d)
|
||||
void arch_do_createdomain(struct vcpu *v)
|
||||
{
|
||||
struct domain *d = v->domain;
|
||||
int vcpuid;
|
||||
|
||||
if ( is_idle_task(d) )
|
||||
return;
|
||||
@@ -275,8 +274,20 @@ void arch_do_createdomain(struct vcpu *v)
|
||||
set_pfn_from_mfn(virt_to_phys(d->arch.mm_perdomain_pt) >> PAGE_SHIFT,
|
||||
INVALID_M2P_ENTRY);
|
||||
v->arch.perdomain_ptes = d->arch.mm_perdomain_pt;
|
||||
v->arch.perdomain_ptes[FIRST_RESERVED_GDT_PAGE] =
|
||||
l1e_from_page(virt_to_page(gdt_table), PAGE_HYPERVISOR);
|
||||
|
||||
/*
|
||||
* Map Xen segments into every VCPU's GDT, irrespective of whether every
|
||||
* VCPU will actually be used. This avoids an NMI race during context
|
||||
* switch: if we take an interrupt after switching CR3 but before switching
|
||||
* GDT, and the old VCPU# is invalid in the new domain, we would otherwise
|
||||
* try to load CS from an invalid table.
|
||||
*/
|
||||
for ( vcpuid = 0; vcpuid < MAX_VIRT_CPUS; vcpuid++ )
|
||||
{
|
||||
d->arch.mm_perdomain_pt[
|
||||
(vcpuid << PDPT_VCPU_SHIFT) + FIRST_RESERVED_GDT_PAGE] =
|
||||
l1e_from_page(virt_to_page(gdt_table), PAGE_HYPERVISOR);
|
||||
}
|
||||
|
||||
v->arch.guest_vtable = __linear_l2_table;
|
||||
v->arch.shadow_vtable = __shadow_linear_l2_table;
|
||||
|
||||
+12
-4
@@ -141,6 +141,7 @@ static void __init do_initcalls(void)
|
||||
static void __init start_of_day(void)
|
||||
{
|
||||
int i;
|
||||
unsigned long vgdt;
|
||||
|
||||
early_cpu_init();
|
||||
|
||||
@@ -158,10 +159,17 @@ static void __init start_of_day(void)
|
||||
|
||||
arch_do_createdomain(current);
|
||||
|
||||
/* Map default GDT into their final position in the idle page table. */
|
||||
map_pages_to_xen(
|
||||
GDT_VIRT_START(current) + FIRST_RESERVED_GDT_BYTE,
|
||||
virt_to_phys(gdt_table) >> PAGE_SHIFT, 1, PAGE_HYPERVISOR);
|
||||
/*
|
||||
* Map default GDT into its final positions in the idle page table. As
|
||||
* noted in arch_do_createdomain(), we must map for every possible VCPU#.
|
||||
*/
|
||||
vgdt = GDT_VIRT_START(current) + FIRST_RESERVED_GDT_BYTE;
|
||||
for ( i = 0; i < MAX_VIRT_CPUS; i++ )
|
||||
{
|
||||
map_pages_to_xen(
|
||||
vgdt, virt_to_phys(gdt_table) >> PAGE_SHIFT, 1, PAGE_HYPERVISOR);
|
||||
vgdt += 1 << PDPT_VCPU_VA_SHIFT;
|
||||
}
|
||||
|
||||
find_smp_config();
|
||||
|
||||
|
||||