ia64/xen-unstable

changeset 12337:8fc5abda5200

[VMXASSIST] Fix mishandling of segment registers on entry into VM86_PROTECTED mode.

The bug occurs when the guest is entering protected mode and reaches
the far jump to set up the 32-bit segment registers for the virtual
VMENTER we're about to perform. vmxassist, in protected_mode(), looks
at the segment registers, which might be 16-bit segments or might be
32-bit segment selectors at this stage (depending on whether the OS
has reloaded them since entering protected mode); and it tries to load
the segments from the GDT. Unconditionally. Even if the segment
register still actually contains a real mode 16-bit segment. Whoops.

vmxassist already has code to detect 16-bit segments that survived
unmodified from a transition into and out of protected mode, and to
save and restore those appropriately. It does this using
"saved_rm_regs", which get cleared on entry to protected mode, and
then set to the old segment value if we fail to set a given 32-bit
segment correctly.

The fix is to save the 16-bit segments *always*, on entry to protected
mode when %CR0(PE) is first set; and to clear the saved 16-bit segment
and set the 32-bit variant in oldctx whenever a 32-bit segment
descriptor is set during the transition to 32-bit CS. Then, when we
finally do the VMENTER, we will set up the VMCS from only the 32-bit
segments, clearing the VMCS entries for segments that have not been
assigned valid 32-bit segments yet.

Tested on various RHEL-5 boot.isos, including ones which worked before
and ones which triggered the bug; all now boot correctly.

Signed-off-by: Stephen Tweedie <sct@redhat.com>
author kfraser@localhost.localdomain
date Thu Nov 09 14:00:40 2006 +0000 (2006-11-09)
parents d173b32f54ab
children f54e29a3be1f
files tools/firmware/vmxassist/vm86.c
line diff
     1.1 --- a/tools/firmware/vmxassist/vm86.c	Thu Nov 09 13:43:21 2006 +0000
     1.2 +++ b/tools/firmware/vmxassist/vm86.c	Thu Nov 09 14:00:40 2006 +0000
     1.3 @@ -867,6 +867,18 @@ load_seg(unsigned long sel, uint32_t *ba
     1.4  }
     1.5  
     1.6  /*
     1.7 + * Emulate a protected mode segment load, falling back to clearing it if
     1.8 + * the descriptor was invalid.
     1.9 + */
    1.10 +static void
    1.11 +load_or_clear_seg(unsigned long sel, uint32_t *base, uint32_t *limit, union vmcs_arbytes *arbytes)
    1.12 +{
    1.13 +	if (!load_seg(sel, base, limit, arbytes))
    1.14 +		load_seg(0, base, limit, arbytes);	    
    1.15 +}
    1.16 +
    1.17 +
    1.18 +/*
    1.19   * Transition to protected mode
    1.20   */
    1.21  static void
    1.22 @@ -878,63 +890,22 @@ protected_mode(struct regs *regs)
    1.23  	oldctx.esp = regs->uesp;
    1.24  	oldctx.eflags = regs->eflags;
    1.25  
    1.26 -	memset(&saved_rm_regs, 0, sizeof(struct regs));
    1.27 -
    1.28  	/* reload all segment registers */
    1.29  	if (!load_seg(regs->cs, &oldctx.cs_base,
    1.30  				&oldctx.cs_limit, &oldctx.cs_arbytes))
    1.31  		panic("Invalid %%cs=0x%x for protected mode\n", regs->cs);
    1.32  	oldctx.cs_sel = regs->cs;
    1.33  
    1.34 -	if (load_seg(regs->ves, &oldctx.es_base,
    1.35 -				&oldctx.es_limit, &oldctx.es_arbytes))
    1.36 -		oldctx.es_sel = regs->ves;
    1.37 -	else {
    1.38 -		load_seg(0, &oldctx.es_base,
    1.39 -			    &oldctx.es_limit, &oldctx.es_arbytes);
    1.40 -		oldctx.es_sel = 0;
    1.41 -		saved_rm_regs.ves = regs->ves;
    1.42 -	}
    1.43 -
    1.44 -	if (load_seg(regs->uss, &oldctx.ss_base,
    1.45 -				&oldctx.ss_limit, &oldctx.ss_arbytes))
    1.46 -		oldctx.ss_sel = regs->uss;
    1.47 -	else {
    1.48 -		load_seg(0, &oldctx.ss_base,
    1.49 -			    &oldctx.ss_limit, &oldctx.ss_arbytes);
    1.50 -		oldctx.ss_sel = 0;
    1.51 -		saved_rm_regs.uss = regs->uss;
    1.52 -	}
    1.53 -
    1.54 -	if (load_seg(regs->vds, &oldctx.ds_base,
    1.55 -				&oldctx.ds_limit, &oldctx.ds_arbytes))
    1.56 -		oldctx.ds_sel = regs->vds;
    1.57 -	else {
    1.58 -		load_seg(0, &oldctx.ds_base,
    1.59 -			    &oldctx.ds_limit, &oldctx.ds_arbytes);
    1.60 -		oldctx.ds_sel = 0;
    1.61 -		saved_rm_regs.vds = regs->vds;
    1.62 -	}
    1.63 -
    1.64 -	if (load_seg(regs->vfs, &oldctx.fs_base,
    1.65 -				&oldctx.fs_limit, &oldctx.fs_arbytes))
    1.66 -		oldctx.fs_sel = regs->vfs;
    1.67 -	else {
    1.68 -		load_seg(0, &oldctx.fs_base,
    1.69 -			    &oldctx.fs_limit, &oldctx.fs_arbytes);
    1.70 -		oldctx.fs_sel = 0;
    1.71 -		saved_rm_regs.vfs = regs->vfs;
    1.72 -	}
    1.73 -
    1.74 -	if (load_seg(regs->vgs, &oldctx.gs_base,
    1.75 -				&oldctx.gs_limit, &oldctx.gs_arbytes))
    1.76 -		oldctx.gs_sel = regs->vgs;
    1.77 -	else {
    1.78 -		load_seg(0, &oldctx.gs_base,
    1.79 -			    &oldctx.gs_limit, &oldctx.gs_arbytes);
    1.80 -		oldctx.gs_sel = 0;
    1.81 -		saved_rm_regs.vgs = regs->vgs;
    1.82 -	}
    1.83 +	load_or_clear_seg(oldctx.es_sel, &oldctx.es_base,
    1.84 +			  &oldctx.es_limit, &oldctx.es_arbytes);
    1.85 +	load_or_clear_seg(oldctx.ss_sel, &oldctx.ss_base,
    1.86 +			  &oldctx.ss_limit, &oldctx.ss_arbytes);
    1.87 +	load_or_clear_seg(oldctx.ds_sel, &oldctx.ds_base,
    1.88 +			  &oldctx.ds_limit, &oldctx.ds_arbytes);
    1.89 +	load_or_clear_seg(oldctx.fs_sel, &oldctx.fs_base,
    1.90 +			  &oldctx.fs_limit, &oldctx.fs_arbytes);
    1.91 +	load_or_clear_seg(oldctx.gs_sel, &oldctx.gs_base,
    1.92 +			  &oldctx.gs_limit, &oldctx.gs_arbytes);
    1.93  
    1.94  	/* initialize jump environment to warp back to protected mode */
    1.95  	regs->cs = CODE_SELECTOR;
    1.96 @@ -1022,6 +993,16 @@ set_mode(struct regs *regs, enum vm86_mo
    1.97  	case VM86_REAL_TO_PROTECTED:
    1.98  		if (mode == VM86_REAL) {
    1.99  			regs->eflags |= EFLAGS_TF;
   1.100 +			saved_rm_regs.vds = regs->vds;
   1.101 +			saved_rm_regs.ves = regs->ves;
   1.102 +			saved_rm_regs.vfs = regs->vfs;
   1.103 +			saved_rm_regs.vgs = regs->vgs;
   1.104 +			saved_rm_regs.uss = regs->uss;
   1.105 +			oldctx.ds_sel = 0;
   1.106 +			oldctx.es_sel = 0;
   1.107 +			oldctx.fs_sel = 0;
   1.108 +			oldctx.gs_sel = 0;
   1.109 +			oldctx.ss_sel = 0;
   1.110  			break;
   1.111  		} else if (mode == VM86_REAL_TO_PROTECTED) {
   1.112  			break;
   1.113 @@ -1282,6 +1263,10 @@ opcode(struct regs *regs)
   1.114  			else
   1.115  				regs->ves = pop16(regs);
   1.116  			TRACE((regs, regs->eip - eip, "pop %%es"));
   1.117 +			if (mode == VM86_REAL_TO_PROTECTED) {
   1.118 +				saved_rm_regs.ves = 0;
   1.119 +				oldctx.es_sel = regs->ves;
   1.120 +			}
   1.121  			return OPC_EMULATED;
   1.122  
   1.123  		case 0x0F: /* two byte opcode */