Skip to content

Instantly share code, notes, and snippets.

@drew-gpf
Created September 19, 2018 02:27
Show Gist options
  • Save drew-gpf/7c2d4c0f03aa3c06ed9c94ec435355c1 to your computer and use it in GitHub Desktop.
Save drew-gpf/7c2d4c0f03aa3c06ed9c94ec435355c1 to your computer and use it in GitHub Desktop.
/*
purpose: update the control register value and read shadow based off of the host mask, and determine if an invalid bit was set
new_value: the new value of the control register that the guest wants to set
host_mask_must_be_set: host bits which the guest should not change
host_mask_dont_care: host bits which can be any value, but only change for the read shadow (generally should be set to the bits required by the cpu)
host_mask_trap: host bits which are updated in the actual control register and are trapped on
read_shadow_encoding: VMCS field for the read shadow
control_register_encoding: VMCS field for the control register
different_bits_buffer (optional): pointer to variable which will be filled out with all the bits which were changed
return value: returns false if a #GP(0) should be injected into the guest
*/
static bool handle_cr_change(uint64_t new_value, uint64_t host_mask_must_be_set, uint64_t host_mask_dont_care, uint64_t host_mask_trap, uint64_t read_shadow_encoding, uint64_t control_register_encoding, uint64_t *different_bits_buffer)
{
//calculate different bits based off of what the guest sees when it performs a cr read
//bits not inside the host mask come from the actual value; bits which are in the host mask come from the read shadow
const uint64_t control_register = vmx_vmread(control_register_encoding);
const uint64_t read_shadow = vmx_vmread(read_shadow_encoding);
const uint64_t full_host_mask = host_mask_must_be_set | host_mask_dont_care | host_mask_trap;
const uint64_t different_bits = ((control_register & ~full_host_mask) | (read_shadow & full_host_mask)) ^ new_value;
if (different_bits_buffer)
*different_bits_buffer = different_bits;
//if a different bit is one which can't be changed, inject a gp
if (different_bits & host_mask_must_be_set)
return false;
//update read shadow bits
{
//the 'dont care' bits only update the read shadow; trap bits still need to be updated since they're read from the read shadow
const uint64_t updateable_bits = host_mask_dont_care | host_mask_trap;
const uint64_t different_updateable_bits = different_bits & updateable_bits;
const uint64_t different_updated_bits = new_value & updateable_bits;
uint64_t read_shadow_buffer = read_shadow;
//remove updateable bits which are different
read_shadow_buffer &= ~different_updateable_bits;
//add in bits which have changed and can be updated
read_shadow_buffer |= different_updated_bits;
vmx_vmwrite(read_shadow_encoding, read_shadow_buffer);
}
//update the actual register, ignoring bits from the mask which must be set or the mask which only modifies the read shadow
{
const uint64_t updateable_bits = ~(host_mask_must_be_set | host_mask_dont_care);
const uint64_t different_updateable_bits = different_bits & updateable_bits;
const uint64_t different_updated_bits = new_value & updateable_bits;
uint64_t control_register_buffer = control_register;
//remove updateable bits which are different
control_register_buffer &= ~different_updateable_bits;
//add in bits which have changed and can be updated
control_register_buffer |= different_updated_bits;
vmx_vmwrite(control_register_encoding, control_register_buffer);
}
return true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment