From patchwork Tue Feb 25 16:52:50 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alvise Rigo X-Patchwork-Id: 324031 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 2F0FE2C00CD for ; Wed, 26 Feb 2014 03:58:22 +1100 (EST) Received: from localhost ([::1]:35882 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WILKm-0007t5-5h for incoming@patchwork.ozlabs.org; Tue, 25 Feb 2014 11:58:20 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:42247) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WILHb-0003SE-MD for qemu-devel@nongnu.org; Tue, 25 Feb 2014 11:55:09 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1WILHT-0002SB-8T for qemu-devel@nongnu.org; Tue, 25 Feb 2014 11:55:03 -0500 Received: from mail-wg0-f49.google.com ([74.125.82.49]:50413) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1WILHS-0002S5-Uj for qemu-devel@nongnu.org; Tue, 25 Feb 2014 11:54:55 -0500 Received: by mail-wg0-f49.google.com with SMTP id x12so655178wgg.20 for ; Tue, 25 Feb 2014 08:54:54 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=Ycu4IXZRB9VJWwegjw556xM5NPQ5Q8IT2kPivaCjIhA=; b=Hno56KxJwZLikTv+YaclbzzeA2b4CzMQr6tOQ+zXzAD3h7n+QrTw8OO176abU81HlE l6yjvkqs0ZUotMXOK/3LXVtQoiFQeiRrJFLczeoWWNcT6DfUdOXud4Jg5HaGkpoMOpwa oKKhvq7eR1XOo5EAFarzUyoaiDCxLckN7HFCvW9qOfsmsXeJgeyZmxCPnjJWqEs+UXuY tHVLjju0mKxIJn7zmkaVfL6IdLeM8/+8S4wodRG6foN+YJjxmg6e64FU6NYpJhePXW4U RYA8VECA3YlPwPlqBOw09/9GOyDcF2CxpYvlLwR7BPyohCiTmrjXdu1S/MbJhVdfQ1RC 1+zA== X-Gm-Message-State: ALoCoQlttL2q2Xu2+3hB9J8sE/lVUzwIKS/bdCQ/xW/l7jLaGgkw5z+SL6iG+kFYARvDD6IOoQKB X-Received: by 10.180.107.136 with SMTP id hc8mr885044wib.11.1393347294139; Tue, 25 Feb 2014 08:54:54 -0800 (PST) Received: from localhost.localdomain (AGrenoble-651-1-620-211.w92-129.abo.wanadoo.fr. [92.129.252.211]) by mx.google.com with ESMTPSA id fm3sm1752079wib.8.2014.02.25.08.54.52 for (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 25 Feb 2014 08:54:53 -0800 (PST) From: Alvise Rigo To: qemu-devel@nongnu.org Date: Tue, 25 Feb 2014 17:52:50 +0100 Message-Id: <1393347170-28502-5-git-send-email-a.rigo@virtualopensystems.com> X-Mailer: git-send-email 1.8.3.2 In-Reply-To: <1393347170-28502-1-git-send-email-a.rigo@virtualopensystems.com> References: <1393347170-28502-1-git-send-email-a.rigo@virtualopensystems.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 74.125.82.49 Cc: Peter Maydell , tech@virtualopensystems.com, Alvise Rigo Subject: [Qemu-devel] [RFC 4/4] Relevant changes to enable KVM to TCG migration X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org CPUARMState: * added adfsr cp register. * added aifsr cp register. These registers have been added because they are migrated by KVM. This prevents the migration from failing when trying to copy their values. ARMCPRegInfo: * added a pointer to the parent that generated the register (if any). * a flag to inform that we have already copied the value of the register: this prevents the register from being overwritten by the parent register value, which could be not set. helper.c: * added mechanism to track the cp register "parent". * compare_cpreg_array(): compare the incoming cp registers with the cpreg_list keeping a list of the registers that do not succeeded the match. * handle_cpreg_kvm2tcg_migration(): try to solve the mismatch of cp registers coming from KVM; without this additional step the migration would fail even if it's feasible. Signed-off-by: Alvise Rigo --- target-arm/cpu.h | 43 +++++++++++++ target-arm/helper.c | 167 +++++++++++++++++++++++++++++++++++++++++++++++++-- target-arm/machine.c | 46 ++++++++++---- 3 files changed, 238 insertions(+), 18 deletions(-) diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 3c8a2db..a97246d 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -184,6 +184,8 @@ typedef struct CPUARMState { MPU write buffer control. */ uint32_t c5_insn; /* Fault status registers. */ uint32_t c5_data; + uint32_t c5_adfsr; + uint32_t c5_aifsr; uint32_t c6_region[8]; /* MPU base/size registers. */ uint32_t c6_insn; /* Fault address registers. */ uint32_t c6_data; @@ -197,6 +199,7 @@ typedef struct CPUARMState { uint32_t c9_pmxevtyper; /* perf monitor event type */ uint32_t c9_pmuserenr; /* perf monitor user enable */ uint32_t c9_pminten; /* perf monitor interrupt enables */ + uint32_t c9_l2ctlr; /* L2 Control Register */ uint32_t c12_vbar; /* vector base address register */ uint32_t c13_fcse; /* FCSE PID. */ uint32_t c13_context; /* Context ID. */ @@ -867,6 +870,15 @@ struct ARMCPRegInfo { uint8_t opc0; uint8_t opc1; uint8_t opc2; + /* When migrating this flag tells if the register's value has already + * been copied. This is used in some unlikely cases where KVM migrates + * a register that in TCG is generated by a wildcarded or + * ARM_CP_STATE_BOTH parent (hence a not migratable register); + * in those cases, we will set the field "parent" to point to the + * generating register. Most of the time this field can stay false. + * */ + bool skip_cpreglist; + ARMCPRegInfo *parent; /* Execution state in which this register is visible: ARM_CP_STATE_* */ int state; /* Register type: ARM_CP_* bits/values */ @@ -995,8 +1007,39 @@ bool write_list_to_cpustate(ARMCPU *cpu); * Note that we do not stop early on failure -- we will attempt * reading all registers in the list. */ + bool write_cpustate_to_list(ARMCPU *cpu); +typedef struct coproc_pair { + uint64_t id; + uint64_t val; +} cp_pair; + +#ifndef KVM_REG_ARM_CORE +#define KVM_REG_ARM_CORE (0x0010 << CP_REG_ARM_COPROC_SHIFT) +#endif +#ifndef KVM_REG_ARM_DEMUX +#define KVM_REG_ARM_DEMUX (0x0011 << CP_REG_ARM_COPROC_SHIFT) +#endif +#ifndef KVM_REG_ARM_VFP +#define KVM_REG_ARM_VFP (0x0012 << CP_REG_ARM_COPROC_SHIFT) +#endif +/* This method compares two arrays of coprocessor registers; in case of + * migration, the array b contains the incoming values. If a not NULL + * pointer to an empty list is provided, the method will fill it with + * pairs of the registers that are in b but not in a. + * */ +int compare_cpreg_array(uint32_t len_a, uint64_t *idx_a, uint64_t *val_a, + uint32_t len_b, uint64_t *idx_b, uint64_t *val_b, + GList **mis_list); + +/* This method is supposed to take in input those registers which failed the + * copy in compare_cpreg_array and tries to handle them in such a way to allow + * migration from KVM to the TCG processor. + * It returns the number of registers not handled, -1 in case of error. + * */ +int handle_cpreg_kvm2tcg_migration(ARMCPU *cpu, GList *mis_list, uint32_t len); + /* Does the core conform to the the "MicroController" profile. e.g. Cortex-M3. Note the M in older cores (eg. ARM7TDMI) stands for Multiply. These are conventional cores (ie. Application or Realtime profile). */ diff --git a/target-arm/helper.c b/target-arm/helper.c index 1b111b6..2abe169 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -197,7 +197,7 @@ bool write_list_to_cpustate(ARMCPU *cpu) ok = false; continue; } - if (ri->type & ARM_CP_NO_MIGRATE) { + if (ri->type & ARM_CP_NO_MIGRATE || (ri->skip_cpreglist)) { continue; } /* Write value and confirm it reads back as written @@ -1213,6 +1213,10 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { { .name = "DFAR", .cp = 15, .crn = 6, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c6_data), .resetvalue = 0, }, + { .name = "ADFSR", .cp = 15, .crn = 5, .crm = 1, .opc1 = 0, .opc2 = 0, + .access = PL1_R, .fieldoffset = offsetof(CPUARMState, cp15.c5_adfsr) }, + { .name = "AIFSR", .cp = 15, .crn = 5, .crm = 1, .opc1 = 0, .opc2 = 1, + .access = PL1_R, .fieldoffset = offsetof(CPUARMState, cp15.c5_aifsr) }, REGINFO_SENTINEL }; @@ -1922,8 +1926,8 @@ CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp) } static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, - void *opaque, int state, - int crm, int opc1, int opc2) + void *opaque, int state, int crm, int opc1, + int opc2, ARMCPRegInfo **parent) { /* Private utility function for define_one_arm_cp_reg_with_opaque(): * add a single reginfo struct to the hash table. @@ -1931,6 +1935,12 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, uint32_t *key = g_new(uint32_t, 1); ARMCPRegInfo *r2 = g_memdup(r, sizeof(ARMCPRegInfo)); int is64 = (r->type & ARM_CP_64BIT) ? 1 : 0; + + /* these fields are used when migrating from TCG to KVM or the other way + * around*/ + r2->skip_cpreglist = false; + r2->parent = NULL; + if (r->state == ARM_CP_STATE_BOTH && state == ARM_CP_STATE_AA32) { /* The AArch32 view of a shared register sees the lower 32 bits * of a 64 bit backing field. It is not migratable as the AArch64 @@ -1945,6 +1955,9 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, r2->fieldoffset += sizeof(uint32_t); } #endif + if (*parent != NULL) { + r2->parent = *parent; + } } if (state == ARM_CP_STATE_AA64) { /* To allow abbreviation of ARMCPRegInfo @@ -1979,6 +1992,21 @@ static void add_cpreg_to_hashtable(ARMCPU *cpu, const ARMCPRegInfo *r, ((r->opc1 == CP_ANY) && opc1 != 0) || ((r->opc2 == CP_ANY) && opc2 != 0)) { r2->type |= ARM_CP_NO_MIGRATE; + if (*parent != NULL) { + r2->parent = *parent; + } + } + + /* Check if this register is going to generate other "child" + * registers, in this case set the parent pointer. + * */ + bool crm_cond = (r->crm != CP_ANY) ? true : (crm == 0); + bool opc1_cond = (r->opc1 != CP_ANY) ? true : (opc1 == 0); + bool opc2_cond = (r->opc2 != CP_ANY) ? true : (opc2 == 0); + bool state_cond = (state == ARM_CP_STATE_AA64 && + r->state == ARM_CP_STATE_BOTH); + if ((crm_cond && opc1_cond && opc2_cond) || state_cond) { + *parent = r2; } /* Overriding of an existing definition must be explicitly @@ -2094,22 +2122,149 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, } /* Bad type field probably means missing sentinel at end of reg list */ assert(cptype_valid(r->type)); + ARMCPRegInfo *parent = NULL; for (crm = crmmin; crm <= crmmax; crm++) { for (opc1 = opc1min; opc1 <= opc1max; opc1++) { for (opc2 = opc2min; opc2 <= opc2max; opc2++) { - for (state = ARM_CP_STATE_AA32; - state <= ARM_CP_STATE_AA64; state++) { + for (state = ARM_CP_STATE_AA64; + state >= ARM_CP_STATE_AA32; state--) { if (r->state != state && r->state != ARM_CP_STATE_BOTH) { continue; } add_cpreg_to_hashtable(cpu, r, opaque, state, - crm, opc1, opc2); + crm, opc1, opc2, &parent); } } } } } +static void add_pair_to_list(uint64_t id, uint64_t val, GList **list) +{ + cp_pair *new = g_malloc(sizeof(cp_pair)); + new->id = id; + new->val = val; + *list = g_list_prepend(*list, new); +} + +static void set_skip_cpreglist(gpointer key, gpointer value, gpointer type) +{ + ARMCPRegInfo *reg = (ARMCPRegInfo *)value; + + if ((type != NULL && reg->type & *(int *)type) || type == NULL) { + reg->skip_cpreglist = true; + } +} + +static void unset_skip_cpreglist(gpointer key, gpointer value, gpointer type) +{ + ARMCPRegInfo *reg = (ARMCPRegInfo *)value; + + if ((type != NULL && reg->type & *(int *)type) || type == NULL) { + reg->skip_cpreglist = false; + } +} + +int compare_cpreg_array(uint32_t len_a, uint64_t *idx_a, uint64_t *val_a, + uint32_t len_b, uint64_t *idx_b, uint64_t *val_b, + GList **mis_list) +{ + int i = 0, j = 0, ret = 0; + + while (i < len_a && j < len_b) { + if (idx_b[j] > idx_a[i]) { + i++; + continue; + } + if (idx_b[j] < idx_a[i]) { + if (mis_list != NULL) { + add_pair_to_list(idx_b[j], val_b[j], mis_list); + } + j++; + + ret = -1; + continue; + } + val_a[i] = val_b[j]; + j++; + i++; + } + + if ((i == len_a) && (j != len_b)) { + if (mis_list != NULL) { + for (i = j; i < len_b; i++) { + add_pair_to_list(idx_b[i], val_b[i], mis_list); + } + } + + ret = -1; + } + + return ret; +} + +int handle_cpreg_kvm2tcg_migration(ARMCPU *cpu, GList *mis_list, uint32_t len) +{ + GList *ptr_l = mis_list; + + /* restore skip_cpreglist flag from last migration */ + g_hash_table_foreach(cpu->cp_regs, unset_skip_cpreglist, NULL); + + while (ptr_l != NULL) { + GList *n = ptr_l->next; + cp_pair *el = (cp_pair *)ptr_l->data; + const ARMCPRegInfo *ri; + /* + * KVM migrates all the CSSIDR cp registers while QEMU doesn't. + * They don't follow the usual nomenclature. + **/ + switch (el->id & CP_REG_ARM_COPROC_MASK) { + case KVM_REG_ARM_CORE: + case KVM_REG_ARM_DEMUX: + case KVM_REG_ARM_VFP: + g_free(el); + mis_list = g_list_delete_link(mis_list, ptr_l); + + break; + default: + /* probably its value has already been copied because + * the register is covered by a wilcarded case or it's + * a register not migrated by TCG. In any case, the + * register type has to be ARM_CP_NO_MIGRATE. + */ + ri = get_arm_cp_reginfo(cpu->cp_regs, kvm_to_cpreg_id(el->id)); + if (ri) { + if ((ri->type & ARM_CP_NO_MIGRATE) == 0) { + /* error */ + return -1; + } + /* If it has a migratable parent, we raw write its value, + * otherwise we just remove it from the list since not + * migratable. + * */ + if (ri->parent != NULL && !((*(ri->parent)).type & + ARM_CP_NO_MIGRATE)) { + write_raw_cp_reg(&cpu->env, ri, el->val); + (*(ri->parent)).skip_cpreglist = true; + } + + g_free(el); + mis_list = g_list_delete_link(mis_list, ptr_l); + } else { + return -1; + } + } + ptr_l = n; + } + + /* set all ARM_CP_CONST as already migrated, this will prevent the migration + * from failing when TCG and KVM constant registers do not match */ + int type = ARM_CP_CONST; + g_hash_table_foreach(cpu->cp_regs, set_skip_cpreglist, &type); + + return g_list_length(mis_list); +} + void define_arm_cp_regs_with_opaque(ARMCPU *cpu, const ARMCPRegInfo *regs, void *opaque) { diff --git a/target-arm/machine.c b/target-arm/machine.c index 8f9e7d4..244ba41 100644 --- a/target-arm/machine.c +++ b/target-arm/machine.c @@ -155,11 +155,13 @@ static void cpu_pre_save(void *opaque) ARMCPU *cpu = opaque; if (kvm_enabled()) { + cpu->running_kvm = true; if (!write_kvmstate_to_list(cpu)) { /* This should never fail */ abort(); } } else { + cpu->running_kvm = false; if (!write_cpustate_to_list(cpu)) { /* This should never fail. */ abort(); @@ -176,7 +178,6 @@ static void cpu_pre_save(void *opaque) static int cpu_post_load(void *opaque, int version_id) { ARMCPU *cpu = opaque; - int i, v; /* Update the values list from the incoming migration data. * Anything in the incoming data which we don't know about is @@ -186,22 +187,42 @@ static int cpu_post_load(void *opaque, int version_id) * incoming migration index list so we can match the values array * entries with the right slots in our own values array. */ + GList *missing_regs = NULL; + if (compare_cpreg_array(cpu->cpreg_array_len, cpu->cpreg_indexes, + cpu->cpreg_values, cpu->cpreg_vmstate_array_len, + cpu->cpreg_vmstate_indexes, cpu->cpreg_vmstate_values, + &missing_regs)) { + /* three different cases: + * 1. KVM to TCG migration + * 2. TCG to KVM migration + * 3. KVM to KVM / TCG to TCG migration*/ + if (cpu->running_kvm && !kvm_enabled()) { + /* case 1. */ + if (handle_cpreg_kvm2tcg_migration(cpu, missing_regs, + g_list_length(missing_regs))) { + g_list_free(missing_regs); + + return -1; + } + /* update the value for future migrations */ + cpu->running_kvm = false; + } else if (!cpu->running_kvm && kvm_enabled()) { + /* case 2. TODO */ + cpu->running_kvm = true; + g_list_free(missing_regs); + + return -1; + } else { + /* case 3. - regular migration, an error occurred */ + g_list_free(missing_regs); - for (i = 0, v = 0; i < cpu->cpreg_array_len - && v < cpu->cpreg_vmstate_array_len; i++) { - if (cpu->cpreg_vmstate_indexes[v] > cpu->cpreg_indexes[i]) { - /* register in our list but not incoming : skip it */ - continue; - } - if (cpu->cpreg_vmstate_indexes[v] < cpu->cpreg_indexes[i]) { - /* register in their list but not ours: fail migration */ return -1; } - /* matching register, copy the value over */ - cpu->cpreg_values[i] = cpu->cpreg_vmstate_values[v]; - v++; } + /* free the list */ + g_list_free(missing_regs); + if (kvm_enabled()) { if (!write_list_to_kvmstate(cpu)) { return -1; @@ -238,6 +259,7 @@ const VMStateDescription vmstate_arm_cpu = { .offset = 0, }, VMSTATE_UINT32(env.spsr, ARMCPU), + VMSTATE_BOOL(running_kvm, ARMCPU), VMSTATE_UINT32_ARRAY(env.banked_spsr, ARMCPU, 6), VMSTATE_UINT32_ARRAY(env.banked_r13, ARMCPU, 6), VMSTATE_UINT32_ARRAY(env.banked_r14, ARMCPU, 6),