* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "target/ppc/mmu-hash64.h"
#include "cpu-models.h"
#include "kvm_ppc.h"
+#include "sysemu/qtest.h"
+#include "sysemu/tcg.h"
#include "hw/ppc/spapr.h"
-typedef struct sPAPRCapPossible {
+typedef struct SpaprCapPossible {
int num; /* size of vals array below */
const char *help; /* help text for vals */
/*
* point is observed
*/
const char *vals[];
-} sPAPRCapPossible;
+} SpaprCapPossible;
-typedef struct sPAPRCapabilityInfo {
+typedef struct SpaprCapabilityInfo {
const char *name;
const char *description;
int index;
ObjectPropertyAccessor *set;
const char *type;
/* Possible values if this is a custom string type */
- sPAPRCapPossible *possible;
+ SpaprCapPossible *possible;
/* Make sure the virtual hardware can support this capability */
- void (*apply)(sPAPRMachineState *spapr, uint8_t val, Error **errp);
- void (*cpu_apply)(sPAPRMachineState *spapr, PowerPCCPU *cpu,
+ void (*apply)(SpaprMachineState *spapr, uint8_t val, Error **errp);
+ void (*cpu_apply)(SpaprMachineState *spapr, PowerPCCPU *cpu,
uint8_t val, Error **errp);
-} sPAPRCapabilityInfo;
+ bool (*migrate_needed)(void *opaque);
+} SpaprCapabilityInfo;
static void spapr_cap_get_bool(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
- sPAPRCapabilityInfo *cap = opaque;
- sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
+ SpaprCapabilityInfo *cap = opaque;
+ SpaprMachineState *spapr = SPAPR_MACHINE(obj);
bool value = spapr_get_cap(spapr, cap->index) == SPAPR_CAP_ON;
visit_type_bool(v, name, &value, errp);
static void spapr_cap_set_bool(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
- sPAPRCapabilityInfo *cap = opaque;
- sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
+ SpaprCapabilityInfo *cap = opaque;
+ SpaprMachineState *spapr = SPAPR_MACHINE(obj);
bool value;
Error *local_err = NULL;
static void spapr_cap_get_string(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
- sPAPRCapabilityInfo *cap = opaque;
- sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
+ SpaprCapabilityInfo *cap = opaque;
+ SpaprMachineState *spapr = SPAPR_MACHINE(obj);
char *val = NULL;
uint8_t value = spapr_get_cap(spapr, cap->index);
static void spapr_cap_set_string(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
- sPAPRCapabilityInfo *cap = opaque;
- sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
+ SpaprCapabilityInfo *cap = opaque;
+ SpaprMachineState *spapr = SPAPR_MACHINE(obj);
Error *local_err = NULL;
uint8_t i;
char *val;
static void spapr_cap_get_pagesize(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
- sPAPRCapabilityInfo *cap = opaque;
- sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
+ SpaprCapabilityInfo *cap = opaque;
+ SpaprMachineState *spapr = SPAPR_MACHINE(obj);
uint8_t val = spapr_get_cap(spapr, cap->index);
uint64_t pagesize = (1ULL << val);
static void spapr_cap_set_pagesize(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
- sPAPRCapabilityInfo *cap = opaque;
- sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
+ SpaprCapabilityInfo *cap = opaque;
+ SpaprMachineState *spapr = SPAPR_MACHINE(obj);
uint64_t pagesize;
uint8_t val;
Error *local_err = NULL;
spapr->eff.caps[cap->index] = val;
}
-static void cap_htm_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp)
+static void cap_htm_apply(SpaprMachineState *spapr, uint8_t val, Error **errp)
{
if (!val) {
/* TODO: We don't support disabling htm yet */
}
}
-static void cap_vsx_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp)
+static void cap_vsx_apply(SpaprMachineState *spapr, uint8_t val, Error **errp)
{
PowerPCCPU *cpu = POWERPC_CPU(first_cpu);
CPUPPCState *env = &cpu->env;
}
}
-static void cap_dfp_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp)
+static void cap_dfp_apply(SpaprMachineState *spapr, uint8_t val, Error **errp)
{
PowerPCCPU *cpu = POWERPC_CPU(first_cpu);
CPUPPCState *env = &cpu->env;
}
}
-sPAPRCapPossible cap_cfpc_possible = {
+SpaprCapPossible cap_cfpc_possible = {
.num = 3,
.vals = {"broken", "workaround", "fixed"},
.help = "broken - no protection, workaround - workaround available,"
" fixed - fixed in hardware",
};
-static void cap_safe_cache_apply(sPAPRMachineState *spapr, uint8_t val,
+static void cap_safe_cache_apply(SpaprMachineState *spapr, uint8_t val,
Error **errp)
{
+ Error *local_err = NULL;
uint8_t kvm_val = kvmppc_get_cap_safe_cache();
if (tcg_enabled() && val) {
- /* TODO - for now only allow broken for TCG */
- error_setg(errp,
-"Requested safe cache capability level not supported by tcg, try a different value for cap-cfpc");
+ /* TCG only supports broken, allow other values and print a warning */
+ error_setg(&local_err,
+ "TCG doesn't support requested feature, cap-cfpc=%s",
+ cap_cfpc_possible.vals[val]);
} else if (kvm_enabled() && (val > kvm_val)) {
error_setg(errp,
"Requested safe cache capability level not supported by kvm, try cap-cfpc=%s",
cap_cfpc_possible.vals[kvm_val]);
}
+
+ if (local_err != NULL)
+ warn_report_err(local_err);
}
-sPAPRCapPossible cap_sbbc_possible = {
+SpaprCapPossible cap_sbbc_possible = {
.num = 3,
.vals = {"broken", "workaround", "fixed"},
.help = "broken - no protection, workaround - workaround available,"
" fixed - fixed in hardware",
};
-static void cap_safe_bounds_check_apply(sPAPRMachineState *spapr, uint8_t val,
+static void cap_safe_bounds_check_apply(SpaprMachineState *spapr, uint8_t val,
Error **errp)
{
+ Error *local_err = NULL;
uint8_t kvm_val = kvmppc_get_cap_safe_bounds_check();
if (tcg_enabled() && val) {
- /* TODO - for now only allow broken for TCG */
- error_setg(errp,
-"Requested safe bounds check capability level not supported by tcg, try a different value for cap-sbbc");
+ /* TCG only supports broken, allow other values and print a warning */
+ error_setg(&local_err,
+ "TCG doesn't support requested feature, cap-sbbc=%s",
+ cap_sbbc_possible.vals[val]);
} else if (kvm_enabled() && (val > kvm_val)) {
error_setg(errp,
"Requested safe bounds check capability level not supported by kvm, try cap-sbbc=%s",
cap_sbbc_possible.vals[kvm_val]);
}
+
+ if (local_err != NULL)
+ warn_report_err(local_err);
}
-sPAPRCapPossible cap_ibs_possible = {
- .num = 4,
+SpaprCapPossible cap_ibs_possible = {
+ .num = 5,
/* Note workaround only maintained for compatibility */
- .vals = {"broken", "workaround", "fixed-ibs", "fixed-ccd"},
- .help = "broken - no protection, fixed-ibs - indirect branch serialisation,"
- " fixed-ccd - cache count disabled",
+ .vals = {"broken", "workaround", "fixed-ibs", "fixed-ccd", "fixed-na"},
+ .help = "broken - no protection, workaround - count cache flush"
+ ", fixed-ibs - indirect branch serialisation,"
+ " fixed-ccd - cache count disabled,"
+ " fixed-na - fixed in hardware (no longer applicable)",
};
-static void cap_safe_indirect_branch_apply(sPAPRMachineState *spapr,
+static void cap_safe_indirect_branch_apply(SpaprMachineState *spapr,
uint8_t val, Error **errp)
{
+ Error *local_err = NULL;
uint8_t kvm_val = kvmppc_get_cap_safe_indirect_branch();
- if (val == SPAPR_CAP_WORKAROUND) { /* Can only be Broken or Fixed */
- error_setg(errp,
-"Requested safe indirect branch capability level \"workaround\" not valid, try cap-ibs=%s",
- cap_ibs_possible.vals[kvm_val]);
- } else if (tcg_enabled() && val) {
- /* TODO - for now only allow broken for TCG */
- error_setg(errp,
-"Requested safe indirect branch capability level not supported by tcg, try a different value for cap-ibs");
- } else if (kvm_enabled() && val && (val != kvm_val)) {
+ if (tcg_enabled() && val) {
+ /* TCG only supports broken, allow other values and print a warning */
+ error_setg(&local_err,
+ "TCG doesn't support requested feature, cap-ibs=%s",
+ cap_ibs_possible.vals[val]);
+ } else if (kvm_enabled() && (val > kvm_val)) {
error_setg(errp,
"Requested safe indirect branch capability level not supported by kvm, try cap-ibs=%s",
cap_ibs_possible.vals[kvm_val]);
}
+
+ if (local_err != NULL) {
+ warn_report_err(local_err);
+ }
}
#define VALUE_DESC_TRISTATE " (broken, workaround, fixed)"
-void spapr_check_pagesize(sPAPRMachineState *spapr, hwaddr pagesize,
+void spapr_check_pagesize(SpaprMachineState *spapr, hwaddr pagesize,
Error **errp)
{
hwaddr maxpagesize = (1ULL << spapr->eff.caps[SPAPR_CAP_HPT_MAXPAGESIZE]);
}
}
-static void cap_hpt_maxpagesize_apply(sPAPRMachineState *spapr,
+static void cap_hpt_maxpagesize_apply(SpaprMachineState *spapr,
uint8_t val, Error **errp)
{
if (val < 12) {
warn_report("Many guests require at least 64kiB hpt-max-page-size");
}
- spapr_check_pagesize(spapr, qemu_getrampagesize(), errp);
+ spapr_check_pagesize(spapr, qemu_minrampagesize(), errp);
+}
+
+static bool cap_hpt_maxpagesize_migrate_needed(void *opaque)
+{
+ return !SPAPR_MACHINE_GET_CLASS(opaque)->pre_4_1_migration;
}
static bool spapr_pagesize_cb(void *opaque, uint32_t seg_pshift,
return true;
}
-static void cap_hpt_maxpagesize_cpu_apply(sPAPRMachineState *spapr,
+static void cap_hpt_maxpagesize_cpu_apply(SpaprMachineState *spapr,
PowerPCCPU *cpu,
uint8_t val, Error **errp)
{
ppc_hash64_filter_pagesizes(cpu, spapr_pagesize_cb, &maxshift);
}
-sPAPRCapabilityInfo capability_table[SPAPR_CAP_NUM] = {
+static void cap_nested_kvm_hv_apply(SpaprMachineState *spapr,
+ uint8_t val, Error **errp)
+{
+ if (!val) {
+ /* capability disabled by default */
+ return;
+ }
+
+ if (tcg_enabled()) {
+ error_setg(errp,
+ "No Nested KVM-HV support in tcg, try cap-nested-hv=off");
+ } else if (kvm_enabled()) {
+ if (!kvmppc_has_cap_nested_kvm_hv()) {
+ error_setg(errp,
+"KVM implementation does not support Nested KVM-HV, try cap-nested-hv=off");
+ } else if (kvmppc_set_cap_nested_kvm_hv(val) < 0) {
+ error_setg(errp,
+"Error enabling cap-nested-hv with KVM, try cap-nested-hv=off");
+ }
+ }
+}
+
+static void cap_large_decr_apply(SpaprMachineState *spapr,
+ uint8_t val, Error **errp)
+{
+ PowerPCCPU *cpu = POWERPC_CPU(first_cpu);
+ PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
+
+ if (!val) {
+ return; /* Disabled by default */
+ }
+
+ if (tcg_enabled()) {
+ if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_3_00, 0,
+ spapr->max_compat_pvr)) {
+ error_setg(errp,
+ "Large decrementer only supported on POWER9, try -cpu POWER9");
+ return;
+ }
+ } else if (kvm_enabled()) {
+ int kvm_nr_bits = kvmppc_get_cap_large_decr();
+
+ if (!kvm_nr_bits) {
+ error_setg(errp,
+ "No large decrementer support, try cap-large-decr=off");
+ } else if (pcc->lrg_decr_bits != kvm_nr_bits) {
+ error_setg(errp,
+"KVM large decrementer size (%d) differs to model (%d), try -cap-large-decr=off",
+ kvm_nr_bits, pcc->lrg_decr_bits);
+ }
+ }
+}
+
+static void cap_large_decr_cpu_apply(SpaprMachineState *spapr,
+ PowerPCCPU *cpu,
+ uint8_t val, Error **errp)
+{
+ CPUPPCState *env = &cpu->env;
+ target_ulong lpcr = env->spr[SPR_LPCR];
+
+ if (kvm_enabled()) {
+ if (kvmppc_enable_cap_large_decr(cpu, val)) {
+ error_setg(errp,
+ "No large decrementer support, try cap-large-decr=off");
+ }
+ }
+
+ if (val) {
+ lpcr |= LPCR_LD;
+ } else {
+ lpcr &= ~LPCR_LD;
+ }
+ ppc_store_lpcr(cpu, lpcr);
+}
+
+static void cap_ccf_assist_apply(SpaprMachineState *spapr, uint8_t val,
+ Error **errp)
+{
+ uint8_t kvm_val = kvmppc_get_cap_count_cache_flush_assist();
+
+ if (tcg_enabled() && val) {
+ /* TODO - for now only allow broken for TCG */
+ error_setg(errp,
+"Requested count cache flush assist capability level not supported by tcg, try cap-ccf-assist=off");
+ } else if (kvm_enabled() && (val > kvm_val)) {
+ error_setg(errp,
+"Requested count cache flush assist capability level not supported by kvm, try cap-ccf-assist=off");
+ }
+}
+
+SpaprCapabilityInfo capability_table[SPAPR_CAP_NUM] = {
[SPAPR_CAP_HTM] = {
.name = "htm",
.description = "Allow Hardware Transactional Memory (HTM)",
[SPAPR_CAP_IBS] = {
.name = "ibs",
.description =
- "Indirect Branch Speculation (broken, fixed-ibs, fixed-ccd)",
+ "Indirect Branch Speculation (broken, workaround, fixed-ibs,"
+ "fixed-ccd, fixed-na)",
.index = SPAPR_CAP_IBS,
.get = spapr_cap_get_string,
.set = spapr_cap_set_string,
.type = "int",
.apply = cap_hpt_maxpagesize_apply,
.cpu_apply = cap_hpt_maxpagesize_cpu_apply,
+ .migrate_needed = cap_hpt_maxpagesize_migrate_needed,
+ },
+ [SPAPR_CAP_NESTED_KVM_HV] = {
+ .name = "nested-hv",
+ .description = "Allow Nested KVM-HV",
+ .index = SPAPR_CAP_NESTED_KVM_HV,
+ .get = spapr_cap_get_bool,
+ .set = spapr_cap_set_bool,
+ .type = "bool",
+ .apply = cap_nested_kvm_hv_apply,
+ },
+ [SPAPR_CAP_LARGE_DECREMENTER] = {
+ .name = "large-decr",
+ .description = "Allow Large Decrementer",
+ .index = SPAPR_CAP_LARGE_DECREMENTER,
+ .get = spapr_cap_get_bool,
+ .set = spapr_cap_set_bool,
+ .type = "bool",
+ .apply = cap_large_decr_apply,
+ .cpu_apply = cap_large_decr_cpu_apply,
+ },
+ [SPAPR_CAP_CCF_ASSIST] = {
+ .name = "ccf-assist",
+ .description = "Count Cache Flush Assist via HW Instruction",
+ .index = SPAPR_CAP_CCF_ASSIST,
+ .get = spapr_cap_get_bool,
+ .set = spapr_cap_set_bool,
+ .type = "bool",
+ .apply = cap_ccf_assist_apply,
},
};
-static sPAPRCapabilities default_caps_with_cpu(sPAPRMachineState *spapr,
+static SpaprCapabilities default_caps_with_cpu(SpaprMachineState *spapr,
const char *cputype)
{
- sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
- sPAPRCapabilities caps;
+ SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr);
+ SpaprCapabilities caps;
caps = smc->default_caps;
+ if (!ppc_type_check_compat(cputype, CPU_POWERPC_LOGICAL_3_00,
+ 0, spapr->max_compat_pvr)) {
+ caps.caps[SPAPR_CAP_LARGE_DECREMENTER] = SPAPR_CAP_OFF;
+ }
+
if (!ppc_type_check_compat(cputype, CPU_POWERPC_LOGICAL_2_07,
0, spapr->max_compat_pvr)) {
caps.caps[SPAPR_CAP_HTM] = SPAPR_CAP_OFF;
uint8_t mps;
if (kvmppc_hpt_needs_host_contiguous_pages()) {
- mps = ctz64(qemu_getrampagesize());
+ mps = ctz64(qemu_minrampagesize());
} else {
mps = 34; /* allow everything up to 16GiB, i.e. everything */
}
int spapr_caps_pre_load(void *opaque)
{
- sPAPRMachineState *spapr = opaque;
+ SpaprMachineState *spapr = opaque;
/* Set to default so we can tell if this came in with the migration */
spapr->mig = spapr->def;
int spapr_caps_pre_save(void *opaque)
{
- sPAPRMachineState *spapr = opaque;
+ SpaprMachineState *spapr = opaque;
spapr->mig = spapr->eff;
return 0;
* caps specific one. Otherwise it wouldn't be called when the source
* caps are all defaults, which could still conflict with overridden
* caps on the destination */
-int spapr_caps_post_migration(sPAPRMachineState *spapr)
+int spapr_caps_post_migration(SpaprMachineState *spapr)
{
int i;
bool ok = true;
- sPAPRCapabilities dstcaps = spapr->eff;
- sPAPRCapabilities srccaps;
+ SpaprCapabilities dstcaps = spapr->eff;
+ SpaprCapabilities srccaps;
srccaps = default_caps_with_cpu(spapr, MACHINE(spapr)->cpu_type);
for (i = 0; i < SPAPR_CAP_NUM; i++) {
}
for (i = 0; i < SPAPR_CAP_NUM; i++) {
- sPAPRCapabilityInfo *info = &capability_table[i];
+ SpaprCapabilityInfo *info = &capability_table[i];
if (srccaps.caps[i] > dstcaps.caps[i]) {
error_report("cap-%s higher level (%d) in incoming stream than on destination (%d)",
#define SPAPR_CAP_MIG_STATE(sname, cap) \
static bool spapr_cap_##sname##_needed(void *opaque) \
{ \
- sPAPRMachineState *spapr = opaque; \
+ SpaprMachineState *spapr = opaque; \
+ bool (*needed)(void *opaque) = \
+ capability_table[cap].migrate_needed; \
\
- return spapr->cmd_line_caps[cap] && \
+ return needed ? needed(opaque) : true && \
+ spapr->cmd_line_caps[cap] && \
(spapr->eff.caps[cap] != \
spapr->def.caps[cap]); \
} \
.needed = spapr_cap_##sname##_needed, \
.fields = (VMStateField[]) { \
VMSTATE_UINT8(mig.caps[cap], \
- sPAPRMachineState), \
+ SpaprMachineState), \
VMSTATE_END_OF_LIST() \
}, \
}
SPAPR_CAP_MIG_STATE(cfpc, SPAPR_CAP_CFPC);
SPAPR_CAP_MIG_STATE(sbbc, SPAPR_CAP_SBBC);
SPAPR_CAP_MIG_STATE(ibs, SPAPR_CAP_IBS);
+SPAPR_CAP_MIG_STATE(hpt_maxpagesize, SPAPR_CAP_HPT_MAXPAGESIZE);
+SPAPR_CAP_MIG_STATE(nested_kvm_hv, SPAPR_CAP_NESTED_KVM_HV);
+SPAPR_CAP_MIG_STATE(large_decr, SPAPR_CAP_LARGE_DECREMENTER);
+SPAPR_CAP_MIG_STATE(ccf_assist, SPAPR_CAP_CCF_ASSIST);
-void spapr_caps_init(sPAPRMachineState *spapr)
+void spapr_caps_init(SpaprMachineState *spapr)
{
- sPAPRCapabilities default_caps;
+ SpaprCapabilities default_caps;
int i;
/* Compute the actual set of caps we should run with */
}
}
-void spapr_caps_apply(sPAPRMachineState *spapr)
+void spapr_caps_apply(SpaprMachineState *spapr)
{
int i;
for (i = 0; i < SPAPR_CAP_NUM; i++) {
- sPAPRCapabilityInfo *info = &capability_table[i];
+ SpaprCapabilityInfo *info = &capability_table[i];
/*
* If the apply function can't set the desired level and thinks it's
}
}
-void spapr_caps_cpu_apply(sPAPRMachineState *spapr, PowerPCCPU *cpu)
+void spapr_caps_cpu_apply(SpaprMachineState *spapr, PowerPCCPU *cpu)
{
int i;
for (i = 0; i < SPAPR_CAP_NUM; i++) {
- sPAPRCapabilityInfo *info = &capability_table[i];
+ SpaprCapabilityInfo *info = &capability_table[i];
/*
* If the apply function can't set the desired level and thinks it's
}
}
-void spapr_caps_add_properties(sPAPRMachineClass *smc, Error **errp)
+void spapr_caps_add_properties(SpaprMachineClass *smc, Error **errp)
{
Error *local_err = NULL;
ObjectClass *klass = OBJECT_CLASS(smc);
int i;
for (i = 0; i < ARRAY_SIZE(capability_table); i++) {
- sPAPRCapabilityInfo *cap = &capability_table[i];
+ SpaprCapabilityInfo *cap = &capability_table[i];
const char *name = g_strdup_printf("cap-%s", cap->name);
char *desc;