Commit 7843c0d6 authored by David Gibson's avatar David Gibson
Browse files

pseries: Move CPU compatibility property to machine



Server class POWER CPUs have a "compat" property, which is used to set the
backwards compatibility mode for the processor.  However, this only makes
sense for machine types which don't give the guest access to hypervisor
privilege - otherwise the compatibility level is under the guest's control.

To reflect this, this removes the CPU 'compat' property and instead
creates a 'max-cpu-compat' property on the pseries machine.  Strictly
speaking this breaks compatibility, but AFAIK the 'compat' option was
never (directly) used with -device or device_add.

The option was used with -cpu.  So, to maintain compatibility, this
patch adds a hack to the cpu option parsing to strip out any compat
options supplied with -cpu and set them on the machine property
instead of the now deprecated cpu property.

Signed-off-by: default avatarDavid Gibson <david@gibson.dropbear.id.au>
Tested-by: default avatarSuraj Jitindar Singh <sjitindarsingh@gmail.com>
Reviewed-by: default avatarGreg Kurz <groug@kaod.org>
Tested-by: default avatarGreg Kurz <groug@kaod.org>
Tested-by: default avatarAndrea Bolognani <abologna@redhat.com>
parent a7333712
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -2131,7 +2131,7 @@ static void ppc_spapr_init(MachineState *machine)
        machine->cpu_model = kvm_enabled() ? "host" : smc->tcg_default_cpu;
    }

    ppc_cpu_parse_features(machine->cpu_model);
    spapr_cpu_parse_features(spapr);

    spapr_init_cpus(spapr);

@@ -2503,6 +2503,10 @@ static void spapr_machine_initfn(Object *obj)
                                    " place of standard EPOW events when possible"
                                    " (required for memory hot-unplug support)",
                                    NULL);

    ppc_compat_add_property(obj, "max-cpu-compat", &spapr->max_compat_pvr,
                            "Maximum permitted CPU compatibility mode",
                            &error_fatal);
}

static void spapr_machine_finalizefn(Object *obj)
+53 −2
Original line number Diff line number Diff line
@@ -20,6 +20,57 @@
#include "sysemu/numa.h"
#include "qemu/error-report.h"

void spapr_cpu_parse_features(sPAPRMachineState *spapr)
{
    /*
     * Backwards compatibility hack:
     *
     *   CPUs had a "compat=" property which didn't make sense for
     *   anything except pseries.  It was replaced by "max-cpu-compat"
     *   machine option.  This supports old command lines like
     *       -cpu POWER8,compat=power7
     *   By stripping the compat option and applying it to the machine
     *   before passing it on to the cpu level parser.
     */
    gchar **inpieces;
    int i, j;
    gchar *compat_str = NULL;

    inpieces = g_strsplit(MACHINE(spapr)->cpu_model, ",", 0);

    /* inpieces[0] is the actual model string */
    i = 1;
    j = 1;
    while (inpieces[i]) {
        if (g_str_has_prefix(inpieces[i], "compat=")) {
            /* in case of multiple compat= options */
            g_free(compat_str);
            compat_str = inpieces[i];
        } else {
            j++;
        }

        i++;
        /* Excise compat options from list */
        inpieces[j] = inpieces[i];
    }

    if (compat_str) {
        char *val = compat_str + strlen("compat=");
        gchar *newprops = g_strjoinv(",", inpieces);

        object_property_set_str(OBJECT(spapr), val, "max-cpu-compat",
                                &error_fatal);

        ppc_cpu_parse_features(newprops);
        g_free(newprops);
    } else {
        ppc_cpu_parse_features(MACHINE(spapr)->cpu_model);
    }

    g_strfreev(inpieces);
}

static void spapr_cpu_reset(void *opaque)
{
    sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
@@ -67,10 +118,10 @@ static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu,
    /* Enable PAPR mode in TCG or KVM */
    cpu_ppc_set_papr(cpu, PPC_VIRTUAL_HYPERVISOR(spapr));

    if (cpu->max_compat) {
    if (spapr->max_compat_pvr) {
        Error *local_err = NULL;

        ppc_set_compat(cpu, cpu->max_compat, &local_err);
        ppc_set_compat(cpu, spapr->max_compat_pvr, &local_err);
        if (local_err) {
            error_propagate(errp, local_err);
            return;
+4 −4
Original line number Diff line number Diff line
@@ -1045,11 +1045,11 @@ static target_ulong h_signal_sys_reset(PowerPCCPU *cpu,
    }
}

static uint32_t cas_check_pvr(PowerPCCPU *cpu, target_ulong *addr,
                              Error **errp)
static uint32_t cas_check_pvr(sPAPRMachineState *spapr, PowerPCCPU *cpu,
                              target_ulong *addr, Error **errp)
{
    bool explicit_match = false; /* Matched the CPU's real PVR */
    uint32_t max_compat = cpu->max_compat;
    uint32_t max_compat = spapr->max_compat_pvr;
    uint32_t best_compat = 0;
    int i;

@@ -1105,7 +1105,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu,
    bool guest_radix;
    Error *local_err = NULL;

    cas_pvr = cas_check_pvr(cpu, &addr, &local_err);
    cas_pvr = cas_check_pvr(spapr, cpu, &addr, &local_err);
    if (local_err) {
        error_report_err(local_err);
        return H_HARDWARE;
+8 −4
Original line number Diff line number Diff line
@@ -86,16 +86,19 @@ struct sPAPRMachineState {
    uint64_t rtc_offset; /* Now used only during incoming migration */
    struct PPCTimebase tb;
    bool has_graphics;
    sPAPROptionVector *ov5;         /* QEMU-supported option vectors */
    sPAPROptionVector *ov5_cas;     /* negotiated (via CAS) option vectors */
    bool cas_reboot;
    bool cas_legacy_guest_workaround;

    Notifier epow_notifier;
    QTAILQ_HEAD(, sPAPREventLogEntry) pending_events;
    bool use_hotplug_event_source;
    sPAPREventSource *event_sources;

    /* ibm,client-architecture-support option negotiation */
    bool cas_reboot;
    bool cas_legacy_guest_workaround;
    sPAPROptionVector *ov5;         /* QEMU-supported option vectors */
    sPAPROptionVector *ov5_cas;     /* negotiated (via CAS) option vectors */
    uint32_t max_compat_pvr;

    /* Migration state */
    int htab_save_index;
    bool htab_first_pass;
@@ -635,6 +638,7 @@ void spapr_hotplug_req_add_by_count_indexed(sPAPRDRConnectorType drc_type,
                                            uint32_t count, uint32_t index);
void spapr_hotplug_req_remove_by_count_indexed(sPAPRDRConnectorType drc_type,
                                               uint32_t count, uint32_t index);
void spapr_cpu_parse_features(sPAPRMachineState *spapr);
void *spapr_populate_hotplug_cpu_dt(CPUState *cs, int *fdt_offset,
                                    sPAPRMachineState *spapr);

+102 −0
Original line number Diff line number Diff line
@@ -24,9 +24,11 @@
#include "sysemu/cpus.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "qapi/visitor.h"
#include "cpu-models.h"

typedef struct {
    const char *name;
    uint32_t pvr;
    uint64_t pcr;
    uint64_t pcr_level;
@@ -38,6 +40,7 @@ static const CompatInfo compat_table[] = {
     * Ordered from oldest to newest - the code relies on this
     */
    { /* POWER6, ISA2.05 */
        .name = "power6",
        .pvr = CPU_POWERPC_LOGICAL_2_05,
        .pcr = PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | PCR_COMPAT_2_06 |
               PCR_COMPAT_2_05 | PCR_TM_DIS | PCR_VSX_DIS,
@@ -45,24 +48,28 @@ static const CompatInfo compat_table[] = {
        .max_threads = 2,
    },
    { /* POWER7, ISA2.06 */
        .name = "power7",
        .pvr = CPU_POWERPC_LOGICAL_2_06,
        .pcr = PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_TM_DIS,
        .pcr_level = PCR_COMPAT_2_06,
        .max_threads = 4,
    },
    {
        .name = "power7+",
        .pvr = CPU_POWERPC_LOGICAL_2_06_PLUS,
        .pcr = PCR_COMPAT_3_00 | PCR_COMPAT_2_07 | PCR_COMPAT_2_06 | PCR_TM_DIS,
        .pcr_level = PCR_COMPAT_2_06,
        .max_threads = 4,
    },
    { /* POWER8, ISA2.07 */
        .name = "power8",
        .pvr = CPU_POWERPC_LOGICAL_2_07,
        .pcr = PCR_COMPAT_3_00 | PCR_COMPAT_2_07,
        .pcr_level = PCR_COMPAT_2_07,
        .max_threads = 8,
    },
    { /* POWER9, ISA3.00 */
        .name = "power9",
        .pvr = CPU_POWERPC_LOGICAL_3_00,
        .pcr = PCR_COMPAT_3_00,
        .pcr_level = PCR_COMPAT_3_00,
@@ -189,3 +196,98 @@ int ppc_compat_max_threads(PowerPCCPU *cpu)

    return n_threads;
}

static void ppc_compat_prop_get(Object *obj, Visitor *v, const char *name,
                                void *opaque, Error **errp)
{
    uint32_t compat_pvr = *((uint32_t *)opaque);
    const char *value;

    if (!compat_pvr) {
        value = "";
    } else {
        const CompatInfo *compat = compat_by_pvr(compat_pvr);

        g_assert(compat);

        value = compat->name;
    }

    visit_type_str(v, name, (char **)&value, errp);
}

static void ppc_compat_prop_set(Object *obj, Visitor *v, const char *name,
                                void *opaque, Error **errp)
{
    Error *local_err = NULL;
    char *value;
    uint32_t compat_pvr;

    visit_type_str(v, name, &value, &local_err);
    if (local_err) {
        error_propagate(errp, local_err);
        return;
    }

    if (strcmp(value, "") == 0) {
        compat_pvr = 0;
    } else {
        int i;
        const CompatInfo *compat = NULL;

        for (i = 0; i < ARRAY_SIZE(compat_table); i++) {
            if (strcmp(value, compat_table[i].name) == 0) {
                compat = &compat_table[i];
                break;

            }
        }

        if (!compat) {
            error_setg(errp, "Invalid compatibility mode \"%s\"", value);
            goto out;
        }
        compat_pvr = compat->pvr;
    }

    *((uint32_t *)opaque) = compat_pvr;

out:
    g_free(value);
}

void ppc_compat_add_property(Object *obj, const char *name,
                             uint32_t *compat_pvr, const char *basedesc,
                             Error **errp)
{
    Error *local_err = NULL;
    gchar *namesv[ARRAY_SIZE(compat_table) + 1];
    gchar *names, *desc;
    int i;

    object_property_add(obj, name, "string",
                        ppc_compat_prop_get, ppc_compat_prop_set, NULL,
                        compat_pvr, &local_err);
    if (local_err) {
        goto out;
    }

    for (i = 0; i < ARRAY_SIZE(compat_table); i++) {
        /*
         * Have to discard const here, because g_strjoinv() takes
         * (gchar **), not (const gchar **) :(
         */
        namesv[i] = (gchar *)compat_table[i].name;
    }
    namesv[ARRAY_SIZE(compat_table)] = NULL;

    names = g_strjoinv(", ", namesv);
    desc = g_strdup_printf("%s. Valid values are %s.", basedesc, names);
    object_property_set_description(obj, name, desc, &local_err);

    g_free(names);
    g_free(desc);

out:
    error_propagate(errp, local_err);
}
Loading