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:
kaf24@firebug.cl.cam.ac.uk committed 2005-10-20 11:25:55 +01:00
1 parent 81e869fdc6
commit 27a9d6d614
2 files changed
+28 -9

No files matched your search

+16 -5
View File
@@ -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
View File
@@ -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();
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.