mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-06-23 14:07:42 +00:00
hv: Add split-lock emulation for xchg
xchg may also cause the #AC for split-lock check. This patch adds this emulation. 1. Kick other vcpus of the guest to stop execution if the guest has more than one vcpu. 2. Emulate the xchg instruction. 3. Notify other vcpus (if any) to restart execution. Tracked-On: #5605 Signed-off-by: Jie Deng <jie.deng@intel.com> Acked-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
parent
47e193a7bb
commit
977e862192
@ -434,6 +434,12 @@ int32_t copy_from_gva(struct acrn_vcpu *vcpu, void *h_ptr, uint64_t gva,
|
||||
return copy_gva(vcpu, h_ptr, gva, size, err_code, fault_addr, 1);
|
||||
}
|
||||
|
||||
int32_t copy_to_gva(struct acrn_vcpu *vcpu, void *h_ptr, uint64_t gva,
|
||||
uint32_t size, uint32_t *err_code, uint64_t *fault_addr)
|
||||
{
|
||||
return copy_gva(vcpu, h_ptr, gva, size, err_code, fault_addr, false);
|
||||
}
|
||||
|
||||
/* gpa --> hpa -->hva */
|
||||
void *gpa2hva(struct acrn_vm *vm, uint64_t x)
|
||||
{
|
||||
|
@ -82,6 +82,7 @@
|
||||
#define VIE_OP_TYPE_STOS 13U
|
||||
#define VIE_OP_TYPE_BITTEST 14U
|
||||
#define VIE_OP_TYPE_TEST 15U
|
||||
#define VIE_OP_TYPE_XCHG 16U
|
||||
|
||||
/* struct vie_op.op_flags */
|
||||
#define VIE_OP_F_IMM (1U << 0U) /* 16/32-bit immediate operand */
|
||||
@ -202,6 +203,12 @@ static const struct instr_emul_vie_op one_byte_opcodes[256] = {
|
||||
[0x85] = {
|
||||
.op_type = VIE_OP_TYPE_TEST,
|
||||
},
|
||||
[0x86] = {
|
||||
.op_type = VIE_OP_TYPE_XCHG,
|
||||
},
|
||||
[0x87] = {
|
||||
.op_type = VIE_OP_TYPE_XCHG,
|
||||
},
|
||||
[0x08] = {
|
||||
.op_type = VIE_OP_TYPE_OR,
|
||||
.op_flags = VIE_OP_F_BYTE_OP,
|
||||
@ -1640,6 +1647,37 @@ static int32_t emulate_bittest(struct acrn_vcpu *vcpu, const struct instr_emul_v
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __attribute__((noinline)) int32_t emulate_xchg_for_splitlock(struct acrn_vcpu *vcpu, const struct instr_emul_vie *vie)
|
||||
{
|
||||
enum cpu_reg_name reg;
|
||||
uint64_t reg_val, data = 0UL;
|
||||
uint8_t opsize = vie->opsize;
|
||||
int32_t ret = 0;
|
||||
uint32_t err_code = 0U;
|
||||
uint64_t fault_addr;
|
||||
|
||||
reg = (enum cpu_reg_name)(vie->reg);
|
||||
reg_val = vm_get_register(vcpu, reg);
|
||||
|
||||
ret = copy_from_gva(vcpu, &data, vie->gva, opsize, &err_code, &fault_addr);
|
||||
if (ret == 0) {
|
||||
err_code = PAGE_FAULT_WR_FLAG;
|
||||
ret = copy_to_gva(vcpu, ®_val, vie->gva, opsize, &err_code, &fault_addr);
|
||||
if (ret == 0) {
|
||||
vie_update_register(vcpu, reg, data, opsize);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
pr_err("Error copy xchg data!");
|
||||
if (ret == -EFAULT) {
|
||||
vcpu_inject_pf(vcpu, fault_addr, err_code);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int32_t vie_init(struct instr_emul_vie *vie, struct acrn_vcpu *vcpu)
|
||||
{
|
||||
uint32_t inst_len = vcpu->arch.inst_len;
|
||||
@ -2276,6 +2314,7 @@ static int32_t instr_check_gva(struct acrn_vcpu *vcpu, enum vm_cpu_mode cpu_mode
|
||||
}
|
||||
|
||||
gva = segbase + base + (uint64_t)vie->scale * idx + (uint64_t)vie->displacement;
|
||||
vie->gva = gva;
|
||||
|
||||
if (vie_canonical_check(cpu_mode, gva) != 0) {
|
||||
if (seg == CPU_REG_SS) {
|
||||
@ -2399,6 +2438,13 @@ int32_t emulate_instruction(struct acrn_vcpu *vcpu)
|
||||
case VIE_OP_TYPE_BITTEST:
|
||||
error = emulate_bittest(vcpu, vie);
|
||||
break;
|
||||
case VIE_OP_TYPE_XCHG:
|
||||
if (vcpu->arch.emulating_lock) {
|
||||
error = emulate_xchg_for_splitlock(vcpu, vie);
|
||||
} else {
|
||||
error = -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error = -EINVAL;
|
||||
break;
|
||||
@ -2409,3 +2455,9 @@ int32_t emulate_instruction(struct acrn_vcpu *vcpu)
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
bool is_current_opcode_xchg(struct acrn_vcpu *vcpu)
|
||||
{
|
||||
return (vcpu->inst_ctxt.vie.op.op_type == VIE_OP_TYPE_XCHG);
|
||||
}
|
||||
|
||||
|
@ -553,7 +553,7 @@ static int32_t emulate_splitlock(struct acrn_vcpu *vcpu, uint32_t exception_vect
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* If #AC is caused by instruction with LOCK prefix, then emulate it,
|
||||
* If AC is caused by instruction with LOCK prefix or xchg, then emulate it,
|
||||
* otherwise, inject it back.
|
||||
*/
|
||||
if (inst[0] == 0xf0U) { /* This is LOCK prefix */
|
||||
@ -579,11 +579,49 @@ static int32_t emulate_splitlock(struct acrn_vcpu *vcpu, uint32_t exception_vect
|
||||
/* Skip the #AC, we have emulated it. */
|
||||
*queue_exception = false;
|
||||
} else {
|
||||
/*
|
||||
* TODO:
|
||||
* The xchg may also cause #AC for split-lock check.
|
||||
* Do xchg emulation here.
|
||||
*/
|
||||
status = decode_instruction(vcpu);
|
||||
if (status >= 0) {
|
||||
/*
|
||||
* If this is the xchg, then emulate it, otherwise,
|
||||
* inject it back.
|
||||
*/
|
||||
if (is_current_opcode_xchg(vcpu)) {
|
||||
/*
|
||||
* Kick other vcpus of the guest to stop execution
|
||||
* until the split-lock emulation being completed.
|
||||
*/
|
||||
vcpu_kick_splitlock_emulation(vcpu);
|
||||
|
||||
/*
|
||||
* Using emulating_lock to make sure xchg emulation
|
||||
* is only called by split-lock emulation.
|
||||
*/
|
||||
vcpu->arch.emulating_lock = true;
|
||||
status = emulate_instruction(vcpu);
|
||||
vcpu->arch.emulating_lock = false;
|
||||
if (status < 0) {
|
||||
if (status == -EFAULT) {
|
||||
pr_info("page fault happen during emulate_instruction");
|
||||
status = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Notify other vcpus of the guest to restart execution.
|
||||
*/
|
||||
vcpu_complete_splitlock_emulation(vcpu);
|
||||
|
||||
/* Do not inject #AC, we have emulated it */
|
||||
*queue_exception = false;
|
||||
}
|
||||
} else {
|
||||
if (status == -EFAULT) {
|
||||
pr_info("page fault happen during decode_instruction");
|
||||
status = 0;
|
||||
/* For this case, Inject #PF, not to queue #AC */
|
||||
*queue_exception = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,6 +95,21 @@ int32_t copy_to_gpa(struct acrn_vm *vm, void *h_ptr, uint64_t gpa, uint32_t size
|
||||
*/
|
||||
int32_t copy_from_gva(struct acrn_vcpu *vcpu, void *h_ptr, uint64_t gva,
|
||||
uint32_t size, uint32_t *err_code, uint64_t *fault_addr);
|
||||
/**
|
||||
* @brief Copy data to VM GVA space from HV address space
|
||||
*
|
||||
* @param[in] vcpu The pointer that points to vcpu data structure
|
||||
* @param[in] h_ptr The pointer that returns the start HV address
|
||||
* of HV memory region which data will be copied to
|
||||
* @param[out] gva The start GVA address of GVA memory region which data
|
||||
* is stored in
|
||||
* @param[in] size The size (bytes) of GVA memory region which data is
|
||||
* stored in
|
||||
* @param[out] err_code The page fault flags
|
||||
* @param[out] fault_addr The GVA address that causes a page fault
|
||||
*/
|
||||
int32_t copy_to_gva(struct acrn_vcpu *vcpu, void *h_ptr, uint64_t gva,
|
||||
uint32_t size, uint32_t *err_code, uint64_t *fault_addr);
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
@ -84,6 +84,7 @@ struct instr_emul_vie {
|
||||
struct instr_emul_vie_op op; /* opcode description */
|
||||
|
||||
uint64_t dst_gpa; /* saved dst operand gpa. Only for movs */
|
||||
uint64_t gva; /* saved gva for instruction emulation */
|
||||
};
|
||||
|
||||
struct instr_emul_ctxt {
|
||||
@ -92,5 +93,6 @@ struct instr_emul_ctxt {
|
||||
|
||||
int32_t emulate_instruction(struct acrn_vcpu *vcpu);
|
||||
int32_t decode_instruction(struct acrn_vcpu *vcpu);
|
||||
bool is_current_opcode_xchg(struct acrn_vcpu *vcpu);
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user