mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
target/i386/mshv: Implement mshv_arch_put_registers()
Write CPU register state to MSHV vCPUs. Various mapping functions to prepare the payload for the HV call have been implemented. Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com> Link: https://lore.kernel.org/r/20250916164847.77883-17-magnuskulke@linux.microsoft.com [mshv.h/mshv_int.h split. - Paolo] Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
committed by
Paolo Bonzini
parent
0382c2c854
commit
25a1d871e0
@@ -49,6 +49,20 @@ typedef struct MshvMsiControl {
|
||||
#define mshv_vcpufd(cpu) (cpu->accel->cpufd)
|
||||
|
||||
/* cpu */
|
||||
typedef struct MshvFPU {
|
||||
uint8_t fpr[8][16];
|
||||
uint16_t fcw;
|
||||
uint16_t fsw;
|
||||
uint8_t ftwx;
|
||||
uint8_t pad1;
|
||||
uint16_t last_opcode;
|
||||
uint64_t last_ip;
|
||||
uint64_t last_dp;
|
||||
uint8_t xmm[16][16];
|
||||
uint32_t mxcsr;
|
||||
uint32_t pad2;
|
||||
} MshvFPU;
|
||||
|
||||
typedef enum MshvVmExit {
|
||||
MshvVmExitIgnore = 0,
|
||||
MshvVmExitShutdown = 1,
|
||||
@@ -58,6 +72,7 @@ typedef enum MshvVmExit {
|
||||
void mshv_init_mmio_emu(void);
|
||||
int mshv_create_vcpu(int vm_fd, uint8_t vp_index, int *cpu_fd);
|
||||
void mshv_remove_vcpu(int vm_fd, int cpu_fd);
|
||||
int mshv_configure_vcpu(const CPUState *cpu, const MshvFPU *fpu, uint64_t xcr0);
|
||||
int mshv_get_standard_regs(CPUState *cpu);
|
||||
int mshv_get_special_regs(CPUState *cpu);
|
||||
int mshv_run_vcpu(int vm_fd, CPUState *cpu, hv_message *msg, MshvVmExit *exit);
|
||||
|
||||
@@ -73,6 +73,35 @@ static enum hv_register_name SPECIAL_REGISTER_NAMES[17] = {
|
||||
HV_X64_REGISTER_APIC_BASE,
|
||||
};
|
||||
|
||||
static enum hv_register_name FPU_REGISTER_NAMES[26] = {
|
||||
HV_X64_REGISTER_XMM0,
|
||||
HV_X64_REGISTER_XMM1,
|
||||
HV_X64_REGISTER_XMM2,
|
||||
HV_X64_REGISTER_XMM3,
|
||||
HV_X64_REGISTER_XMM4,
|
||||
HV_X64_REGISTER_XMM5,
|
||||
HV_X64_REGISTER_XMM6,
|
||||
HV_X64_REGISTER_XMM7,
|
||||
HV_X64_REGISTER_XMM8,
|
||||
HV_X64_REGISTER_XMM9,
|
||||
HV_X64_REGISTER_XMM10,
|
||||
HV_X64_REGISTER_XMM11,
|
||||
HV_X64_REGISTER_XMM12,
|
||||
HV_X64_REGISTER_XMM13,
|
||||
HV_X64_REGISTER_XMM14,
|
||||
HV_X64_REGISTER_XMM15,
|
||||
HV_X64_REGISTER_FP_MMX0,
|
||||
HV_X64_REGISTER_FP_MMX1,
|
||||
HV_X64_REGISTER_FP_MMX2,
|
||||
HV_X64_REGISTER_FP_MMX3,
|
||||
HV_X64_REGISTER_FP_MMX4,
|
||||
HV_X64_REGISTER_FP_MMX5,
|
||||
HV_X64_REGISTER_FP_MMX6,
|
||||
HV_X64_REGISTER_FP_MMX7,
|
||||
HV_X64_REGISTER_FP_CONTROL_STATUS,
|
||||
HV_X64_REGISTER_XMM_CONTROL_STATUS,
|
||||
};
|
||||
|
||||
int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs,
|
||||
size_t n_regs)
|
||||
{
|
||||
@@ -372,8 +401,216 @@ int mshv_load_regs(CPUState *cpu)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void populate_hv_segment_reg(SegmentCache *seg,
|
||||
hv_x64_segment_register *hv_reg)
|
||||
{
|
||||
uint32_t flags = seg->flags;
|
||||
|
||||
hv_reg->base = seg->base;
|
||||
hv_reg->limit = seg->limit;
|
||||
hv_reg->selector = seg->selector;
|
||||
hv_reg->segment_type = (flags >> DESC_TYPE_SHIFT) & 0xF;
|
||||
hv_reg->non_system_segment = (flags & DESC_S_MASK) != 0;
|
||||
hv_reg->descriptor_privilege_level = (flags >> DESC_DPL_SHIFT) & 0x3;
|
||||
hv_reg->present = (flags & DESC_P_MASK) != 0;
|
||||
hv_reg->reserved = 0;
|
||||
hv_reg->available = (flags & DESC_AVL_MASK) != 0;
|
||||
hv_reg->_long = (flags >> DESC_L_SHIFT) & 0x1;
|
||||
hv_reg->_default = (flags >> DESC_B_SHIFT) & 0x1;
|
||||
hv_reg->granularity = (flags & DESC_G_MASK) != 0;
|
||||
}
|
||||
|
||||
static inline void populate_hv_table_reg(const struct SegmentCache *seg,
|
||||
hv_x64_table_register *hv_reg)
|
||||
{
|
||||
memset(hv_reg, 0, sizeof(*hv_reg));
|
||||
|
||||
hv_reg->base = seg->base;
|
||||
hv_reg->limit = seg->limit;
|
||||
}
|
||||
|
||||
static int set_special_regs(const CPUState *cpu)
|
||||
{
|
||||
X86CPU *x86cpu = X86_CPU(cpu);
|
||||
CPUX86State *env = &x86cpu->env;
|
||||
struct hv_register_assoc assocs[ARRAY_SIZE(SPECIAL_REGISTER_NAMES)];
|
||||
size_t n_regs = ARRAY_SIZE(SPECIAL_REGISTER_NAMES);
|
||||
int ret;
|
||||
|
||||
/* set names */
|
||||
for (size_t i = 0; i < n_regs; i++) {
|
||||
assocs[i].name = SPECIAL_REGISTER_NAMES[i];
|
||||
}
|
||||
populate_hv_segment_reg(&env->segs[R_CS], &assocs[0].value.segment);
|
||||
populate_hv_segment_reg(&env->segs[R_DS], &assocs[1].value.segment);
|
||||
populate_hv_segment_reg(&env->segs[R_ES], &assocs[2].value.segment);
|
||||
populate_hv_segment_reg(&env->segs[R_FS], &assocs[3].value.segment);
|
||||
populate_hv_segment_reg(&env->segs[R_GS], &assocs[4].value.segment);
|
||||
populate_hv_segment_reg(&env->segs[R_SS], &assocs[5].value.segment);
|
||||
populate_hv_segment_reg(&env->tr, &assocs[6].value.segment);
|
||||
populate_hv_segment_reg(&env->ldt, &assocs[7].value.segment);
|
||||
|
||||
populate_hv_table_reg(&env->gdt, &assocs[8].value.table);
|
||||
populate_hv_table_reg(&env->idt, &assocs[9].value.table);
|
||||
|
||||
assocs[10].value.reg64 = env->cr[0];
|
||||
assocs[11].value.reg64 = env->cr[2];
|
||||
assocs[12].value.reg64 = env->cr[3];
|
||||
assocs[13].value.reg64 = env->cr[4];
|
||||
assocs[14].value.reg64 = cpu_get_apic_tpr(x86cpu->apic_state);
|
||||
assocs[15].value.reg64 = env->efer;
|
||||
assocs[16].value.reg64 = cpu_get_apic_base(x86cpu->apic_state);
|
||||
|
||||
ret = mshv_set_generic_regs(cpu, assocs, n_regs);
|
||||
if (ret < 0) {
|
||||
error_report("failed to set special registers");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_fpu(const CPUState *cpu, const struct MshvFPU *regs)
|
||||
{
|
||||
struct hv_register_assoc assocs[ARRAY_SIZE(FPU_REGISTER_NAMES)];
|
||||
union hv_register_value *value;
|
||||
size_t fp_i;
|
||||
union hv_x64_fp_control_status_register *ctrl_status;
|
||||
union hv_x64_xmm_control_status_register *xmm_ctrl_status;
|
||||
int ret;
|
||||
size_t n_regs = ARRAY_SIZE(FPU_REGISTER_NAMES);
|
||||
|
||||
/* first 16 registers are xmm0-xmm15 */
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
assocs[i].name = FPU_REGISTER_NAMES[i];
|
||||
value = &assocs[i].value;
|
||||
memcpy(&value->reg128, ®s->xmm[i], 16);
|
||||
}
|
||||
|
||||
/* next 8 registers are fp_mmx0-fp_mmx7 */
|
||||
for (size_t i = 16; i < 24; i++) {
|
||||
assocs[i].name = FPU_REGISTER_NAMES[i];
|
||||
fp_i = (i - 16);
|
||||
value = &assocs[i].value;
|
||||
memcpy(&value->reg128, ®s->fpr[fp_i], 16);
|
||||
}
|
||||
|
||||
/* last two registers are fp_control_status and xmm_control_status */
|
||||
assocs[24].name = FPU_REGISTER_NAMES[24];
|
||||
value = &assocs[24].value;
|
||||
ctrl_status = &value->fp_control_status;
|
||||
ctrl_status->fp_control = regs->fcw;
|
||||
ctrl_status->fp_status = regs->fsw;
|
||||
ctrl_status->fp_tag = regs->ftwx;
|
||||
ctrl_status->reserved = 0;
|
||||
ctrl_status->last_fp_op = regs->last_opcode;
|
||||
ctrl_status->last_fp_rip = regs->last_ip;
|
||||
|
||||
assocs[25].name = FPU_REGISTER_NAMES[25];
|
||||
value = &assocs[25].value;
|
||||
xmm_ctrl_status = &value->xmm_control_status;
|
||||
xmm_ctrl_status->xmm_status_control = regs->mxcsr;
|
||||
xmm_ctrl_status->xmm_status_control_mask = 0;
|
||||
xmm_ctrl_status->last_fp_rdp = regs->last_dp;
|
||||
|
||||
ret = mshv_set_generic_regs(cpu, assocs, n_regs);
|
||||
if (ret < 0) {
|
||||
error_report("failed to set fpu registers");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_xc_reg(const CPUState *cpu, uint64_t xcr0)
|
||||
{
|
||||
int ret;
|
||||
struct hv_register_assoc assoc = {
|
||||
.name = HV_X64_REGISTER_XFEM,
|
||||
.value.reg64 = xcr0,
|
||||
};
|
||||
|
||||
ret = mshv_set_generic_regs(cpu, &assoc, 1);
|
||||
if (ret < 0) {
|
||||
error_report("failed to set xcr0");
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_cpu_state(const CPUState *cpu, const MshvFPU *fpu_regs,
|
||||
uint64_t xcr0)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = set_standard_regs(cpu);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
ret = set_special_regs(cpu);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
ret = set_fpu(cpu, fpu_regs);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
ret = set_xc_reg(cpu, xcr0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: populate topology info:
|
||||
*
|
||||
* X86CPU *x86cpu = X86_CPU(cpu);
|
||||
* CPUX86State *env = &x86cpu->env;
|
||||
* X86CPUTopoInfo *topo_info = &env->topo_info;
|
||||
*/
|
||||
int mshv_configure_vcpu(const CPUState *cpu, const struct MshvFPU *fpu,
|
||||
uint64_t xcr0)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = set_cpu_state(cpu, fpu, xcr0);
|
||||
if (ret < 0) {
|
||||
error_report("failed to set cpu state");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int put_regs(const CPUState *cpu)
|
||||
{
|
||||
X86CPU *x86cpu = X86_CPU(cpu);
|
||||
CPUX86State *env = &x86cpu->env;
|
||||
MshvFPU fpu = {0};
|
||||
int ret;
|
||||
|
||||
memset(&fpu, 0, sizeof(fpu));
|
||||
|
||||
ret = mshv_configure_vcpu(cpu, &fpu, env->xcr0);
|
||||
if (ret < 0) {
|
||||
error_report("failed to configure vcpu");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mshv_arch_put_registers(const CPUState *cpu)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = put_regs(cpu);
|
||||
if (ret < 0) {
|
||||
error_report("Failed to put registers");
|
||||
return -1;
|
||||
}
|
||||
|
||||
error_report("unimplemented");
|
||||
abort();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user