xref: /openbmc/qemu/target/ppc/compat.c (revision 6b829602e2f10f301ff8508f3a6850a0e913142c)
19d6f1065SDavid Gibson /*
29d6f1065SDavid Gibson  *  PowerPC CPU initialization for qemu.
39d6f1065SDavid Gibson  *
49d6f1065SDavid Gibson  *  Copyright 2016, David Gibson, Red Hat Inc. <dgibson@redhat.com>
59d6f1065SDavid Gibson  *
69d6f1065SDavid Gibson  * This library is free software; you can redistribute it and/or
79d6f1065SDavid Gibson  * modify it under the terms of the GNU Lesser General Public
89d6f1065SDavid Gibson  * License as published by the Free Software Foundation; either
96bd039cdSChetan Pant  * version 2.1 of the License, or (at your option) any later version.
109d6f1065SDavid Gibson  *
119d6f1065SDavid Gibson  * This library is distributed in the hope that it will be useful,
129d6f1065SDavid Gibson  * but WITHOUT ANY WARRANTY; without even the implied warranty of
139d6f1065SDavid Gibson  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
149d6f1065SDavid Gibson  * Lesser General Public License for more details.
159d6f1065SDavid Gibson  *
169d6f1065SDavid Gibson  * You should have received a copy of the GNU Lesser General Public
179d6f1065SDavid Gibson  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
189d6f1065SDavid Gibson  */
199d6f1065SDavid Gibson 
209d6f1065SDavid Gibson #include "qemu/osdep.h"
21f6f242c7SDavid Gibson #include "sysemu/hw_accel.h"
229d6f1065SDavid Gibson #include "sysemu/kvm.h"
239d6f1065SDavid Gibson #include "kvm_ppc.h"
249d6f1065SDavid Gibson #include "sysemu/cpus.h"
259d6f1065SDavid Gibson #include "qemu/error-report.h"
269d6f1065SDavid Gibson #include "qapi/error.h"
277843c0d6SDavid Gibson #include "qapi/visitor.h"
289d6f1065SDavid Gibson #include "cpu-models.h"
299d6f1065SDavid Gibson 
309d6f1065SDavid Gibson typedef struct {
317843c0d6SDavid Gibson     const char *name;
329d6f1065SDavid Gibson     uint32_t pvr;
339d6f1065SDavid Gibson     uint64_t pcr;
349d2179d6SDavid Gibson     uint64_t pcr_level;
35abbc1247SDavid Gibson 
36abbc1247SDavid Gibson     /*
37abbc1247SDavid Gibson      * Maximum allowed virtual threads per virtual core
38abbc1247SDavid Gibson      *
39abbc1247SDavid Gibson      * This is to stop older guests getting confused by seeing more
40abbc1247SDavid Gibson      * threads than they think the cpu can support.  Usually it's
41abbc1247SDavid Gibson      * equal to the number of threads supported on bare metal
42abbc1247SDavid Gibson      * hardware, but not always (see POWER9).
43abbc1247SDavid Gibson      */
44abbc1247SDavid Gibson     int max_vthreads;
459d6f1065SDavid Gibson } CompatInfo;
469d6f1065SDavid Gibson 
479d6f1065SDavid Gibson static const CompatInfo compat_table[] = {
489d2179d6SDavid Gibson     /*
499d2179d6SDavid Gibson      * Ordered from oldest to newest - the code relies on this
509d2179d6SDavid Gibson      */
519d6f1065SDavid Gibson     { /* POWER6, ISA2.05 */
527843c0d6SDavid Gibson         .name = "power6",
539d6f1065SDavid Gibson         .pvr = CPU_POWERPC_LOGICAL_2_05,
547d37b274SCédric Le Goater         .pcr = PCR_COMPAT_3_10 | PCR_COMPAT_3_00 | PCR_COMPAT_2_07 |
557d37b274SCédric Le Goater                PCR_COMPAT_2_06 | PCR_COMPAT_2_05 | PCR_TM_DIS | PCR_VSX_DIS,
569d2179d6SDavid Gibson         .pcr_level = PCR_COMPAT_2_05,
57abbc1247SDavid Gibson         .max_vthreads = 2,
589d6f1065SDavid Gibson     },
599d6f1065SDavid Gibson     { /* POWER7, ISA2.06 */
607843c0d6SDavid Gibson         .name = "power7",
619d6f1065SDavid Gibson         .pvr = CPU_POWERPC_LOGICAL_2_06,
627d37b274SCédric Le Goater         .pcr = PCR_COMPAT_3_10 | PCR_COMPAT_3_00 | PCR_COMPAT_2_07 |
637d37b274SCédric Le Goater                PCR_COMPAT_2_06 | PCR_TM_DIS,
649d2179d6SDavid Gibson         .pcr_level = PCR_COMPAT_2_06,
65abbc1247SDavid Gibson         .max_vthreads = 4,
669d6f1065SDavid Gibson     },
679d6f1065SDavid Gibson     {
687843c0d6SDavid Gibson         .name = "power7+",
699d6f1065SDavid Gibson         .pvr = CPU_POWERPC_LOGICAL_2_06_PLUS,
707d37b274SCédric Le Goater         .pcr = PCR_COMPAT_3_10 | PCR_COMPAT_3_00 | PCR_COMPAT_2_07 |
717d37b274SCédric Le Goater                PCR_COMPAT_2_06 | PCR_TM_DIS,
729d2179d6SDavid Gibson         .pcr_level = PCR_COMPAT_2_06,
73abbc1247SDavid Gibson         .max_vthreads = 4,
749d6f1065SDavid Gibson     },
759d6f1065SDavid Gibson     { /* POWER8, ISA2.07 */
767843c0d6SDavid Gibson         .name = "power8",
779d6f1065SDavid Gibson         .pvr = CPU_POWERPC_LOGICAL_2_07,
787d37b274SCédric Le Goater         .pcr = PCR_COMPAT_3_10 | PCR_COMPAT_3_00 | PCR_COMPAT_2_07,
799d2179d6SDavid Gibson         .pcr_level = PCR_COMPAT_2_07,
80abbc1247SDavid Gibson         .max_vthreads = 8,
819d6f1065SDavid Gibson     },
829b44c836SSuraj Jitindar Singh     { /* POWER9, ISA3.00 */
837843c0d6SDavid Gibson         .name = "power9",
849b44c836SSuraj Jitindar Singh         .pvr = CPU_POWERPC_LOGICAL_3_00,
857d37b274SCédric Le Goater         .pcr = PCR_COMPAT_3_10 | PCR_COMPAT_3_00,
869b44c836SSuraj Jitindar Singh         .pcr_level = PCR_COMPAT_3_00,
8703ee51d3SJose Ricardo Ziviani         /*
8803ee51d3SJose Ricardo Ziviani          * POWER9 hardware only supports 4 threads / core, but this
8903ee51d3SJose Ricardo Ziviani          * limit is for guests.  We need to support 8 vthreads/vcore
9003ee51d3SJose Ricardo Ziviani          * on POWER9 for POWER8 compatibility guests, and it's very
9103ee51d3SJose Ricardo Ziviani          * confusing if half of the threads disappear from the guest
9203ee51d3SJose Ricardo Ziviani          * if it announces it's POWER9 aware at CAS time.
9303ee51d3SJose Ricardo Ziviani          */
94abbc1247SDavid Gibson         .max_vthreads = 8,
959b44c836SSuraj Jitindar Singh     },
967d37b274SCédric Le Goater     { /* POWER10, ISA3.10 */
977d37b274SCédric Le Goater         .name = "power10",
987d37b274SCédric Le Goater         .pvr = CPU_POWERPC_LOGICAL_3_10,
997d37b274SCédric Le Goater         .pcr = PCR_COMPAT_3_10,
1007d37b274SCédric Le Goater         .pcr_level = PCR_COMPAT_3_10,
1017d37b274SCédric Le Goater         .max_vthreads = 8,
1027d37b274SCédric Le Goater     },
103*c0d96407SAditya Gupta     { /* POWER11, ISA3.10 */
104*c0d96407SAditya Gupta         .name = "power11",
105*c0d96407SAditya Gupta         .pvr = CPU_POWERPC_LOGICAL_3_10_P11,
106*c0d96407SAditya Gupta         .pcr = PCR_COMPAT_3_10,
107*c0d96407SAditya Gupta         .pcr_level = PCR_COMPAT_3_10,
108*c0d96407SAditya Gupta         .max_vthreads = 8,
109*c0d96407SAditya Gupta     },
1109d6f1065SDavid Gibson };
1119d6f1065SDavid Gibson 
compat_by_pvr(uint32_t pvr)1129d6f1065SDavid Gibson static const CompatInfo *compat_by_pvr(uint32_t pvr)
1139d6f1065SDavid Gibson {
1149d6f1065SDavid Gibson     int i;
1159d6f1065SDavid Gibson 
1169d6f1065SDavid Gibson     for (i = 0; i < ARRAY_SIZE(compat_table); i++) {
1179d6f1065SDavid Gibson         if (compat_table[i].pvr == pvr) {
1189d6f1065SDavid Gibson             return &compat_table[i];
1199d6f1065SDavid Gibson         }
1209d6f1065SDavid Gibson     }
1219d6f1065SDavid Gibson     return NULL;
1229d6f1065SDavid Gibson }
1239d6f1065SDavid Gibson 
pcc_compat(PowerPCCPUClass * pcc,uint32_t compat_pvr,uint32_t min_compat_pvr,uint32_t max_compat_pvr)124ad99d04cSDavid Gibson static bool pcc_compat(PowerPCCPUClass *pcc, uint32_t compat_pvr,
1259d2179d6SDavid Gibson                        uint32_t min_compat_pvr, uint32_t max_compat_pvr)
1269d2179d6SDavid Gibson {
1279d2179d6SDavid Gibson     const CompatInfo *compat = compat_by_pvr(compat_pvr);
1289d2179d6SDavid Gibson     const CompatInfo *min = compat_by_pvr(min_compat_pvr);
1299d2179d6SDavid Gibson     const CompatInfo *max = compat_by_pvr(max_compat_pvr);
1309d2179d6SDavid Gibson 
1319d2179d6SDavid Gibson     g_assert(!min_compat_pvr || min);
1329d2179d6SDavid Gibson     g_assert(!max_compat_pvr || max);
1339d2179d6SDavid Gibson 
1349d2179d6SDavid Gibson     if (!compat) {
1359d2179d6SDavid Gibson         /* Not a recognized logical PVR */
1369d2179d6SDavid Gibson         return false;
1379d2179d6SDavid Gibson     }
1389d2179d6SDavid Gibson     if ((min && (compat < min)) || (max && (compat > max))) {
1399d2179d6SDavid Gibson         /* Outside specified range */
1409d2179d6SDavid Gibson         return false;
1419d2179d6SDavid Gibson     }
142ac0fbbb2SAditya Gupta     if (compat->pvr > pcc->spapr_logical_pvr) {
143ac0fbbb2SAditya Gupta         /* Older CPU cannot support a newer processor's compat mode */
144ac0fbbb2SAditya Gupta         return false;
145ac0fbbb2SAditya Gupta     }
1469d2179d6SDavid Gibson     if (!(pcc->pcr_supported & compat->pcr_level)) {
1479d2179d6SDavid Gibson         /* Not supported by this CPU */
1489d2179d6SDavid Gibson         return false;
1499d2179d6SDavid Gibson     }
1509d2179d6SDavid Gibson     return true;
1519d2179d6SDavid Gibson }
1529d2179d6SDavid Gibson 
ppc_check_compat(PowerPCCPU * cpu,uint32_t compat_pvr,uint32_t min_compat_pvr,uint32_t max_compat_pvr)153ad99d04cSDavid Gibson bool ppc_check_compat(PowerPCCPU *cpu, uint32_t compat_pvr,
154ad99d04cSDavid Gibson                       uint32_t min_compat_pvr, uint32_t max_compat_pvr)
155ad99d04cSDavid Gibson {
156ad99d04cSDavid Gibson     PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
157ad99d04cSDavid Gibson 
158ad99d04cSDavid Gibson #if !defined(CONFIG_USER_ONLY)
159ad99d04cSDavid Gibson     g_assert(cpu->vhyp);
160ad99d04cSDavid Gibson #endif
161ad99d04cSDavid Gibson 
162ad99d04cSDavid Gibson     return pcc_compat(pcc, compat_pvr, min_compat_pvr, max_compat_pvr);
163ad99d04cSDavid Gibson }
164ad99d04cSDavid Gibson 
ppc_type_check_compat(const char * cputype,uint32_t compat_pvr,uint32_t min_compat_pvr,uint32_t max_compat_pvr)165ad99d04cSDavid Gibson bool ppc_type_check_compat(const char *cputype, uint32_t compat_pvr,
166ad99d04cSDavid Gibson                            uint32_t min_compat_pvr, uint32_t max_compat_pvr)
167ad99d04cSDavid Gibson {
168ad99d04cSDavid Gibson     PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(object_class_by_name(cputype));
169ad99d04cSDavid Gibson     return pcc_compat(pcc, compat_pvr, min_compat_pvr, max_compat_pvr);
170ad99d04cSDavid Gibson }
171ad99d04cSDavid Gibson 
ppc_set_compat(PowerPCCPU * cpu,uint32_t compat_pvr,Error ** errp)1722c82e8dfSGreg Kurz int ppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr, Error **errp)
1739d6f1065SDavid Gibson {
1749d6f1065SDavid Gibson     const CompatInfo *compat = compat_by_pvr(compat_pvr);
1759d6f1065SDavid Gibson     CPUPPCState *env = &cpu->env;
1769d6f1065SDavid Gibson     PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
1779d6f1065SDavid Gibson     uint64_t pcr;
1789d6f1065SDavid Gibson 
1799d6f1065SDavid Gibson     if (!compat_pvr) {
1809d6f1065SDavid Gibson         pcr = 0;
1819d6f1065SDavid Gibson     } else if (!compat) {
1829d6f1065SDavid Gibson         error_setg(errp, "Unknown compatibility PVR 0x%08"PRIx32, compat_pvr);
1832c82e8dfSGreg Kurz         return -EINVAL;
1849d2179d6SDavid Gibson     } else if (!ppc_check_compat(cpu, compat_pvr, 0, 0)) {
1859d2179d6SDavid Gibson         error_setg(errp, "Compatibility PVR 0x%08"PRIx32" not valid for CPU",
1869d2179d6SDavid Gibson                    compat_pvr);
1872c82e8dfSGreg Kurz         return -EINVAL;
1889d6f1065SDavid Gibson     } else {
1899d6f1065SDavid Gibson         pcr = compat->pcr;
1909d6f1065SDavid Gibson     }
1919d6f1065SDavid Gibson 
192f6f242c7SDavid Gibson     cpu_synchronize_state(CPU(cpu));
193f6f242c7SDavid Gibson 
1945dfaa532SGreg Kurz     if (kvm_enabled() && cpu->compat_pvr != compat_pvr) {
195e4f0c6bbSGreg Kurz         int ret = kvmppc_set_compat(cpu, compat_pvr);
1969d6f1065SDavid Gibson         if (ret < 0) {
1979d6f1065SDavid Gibson             error_setg_errno(errp, -ret,
1989d6f1065SDavid Gibson                              "Unable to set CPU compatibility mode in KVM");
1992c82e8dfSGreg Kurz             return ret;
2009d6f1065SDavid Gibson         }
2019d6f1065SDavid Gibson     }
2025dfaa532SGreg Kurz 
2035dfaa532SGreg Kurz     cpu->compat_pvr = compat_pvr;
2045dfaa532SGreg Kurz     env->spr[SPR_PCR] = pcr & pcc->pcr_mask;
2052c82e8dfSGreg Kurz     return 0;
2069d6f1065SDavid Gibson }
20712dbeb16SDavid Gibson 
208f6f242c7SDavid Gibson typedef struct {
209f6f242c7SDavid Gibson     uint32_t compat_pvr;
2102c82e8dfSGreg Kurz     Error **errp;
2112c82e8dfSGreg Kurz     int ret;
212f6f242c7SDavid Gibson } SetCompatState;
213f6f242c7SDavid Gibson 
do_set_compat(CPUState * cs,run_on_cpu_data arg)214f6f242c7SDavid Gibson static void do_set_compat(CPUState *cs, run_on_cpu_data arg)
215f6f242c7SDavid Gibson {
216f6f242c7SDavid Gibson     PowerPCCPU *cpu = POWERPC_CPU(cs);
217f6f242c7SDavid Gibson     SetCompatState *s = arg.host_ptr;
218f6f242c7SDavid Gibson 
2192c82e8dfSGreg Kurz     s->ret = ppc_set_compat(cpu, s->compat_pvr, s->errp);
220f6f242c7SDavid Gibson }
221f6f242c7SDavid Gibson 
ppc_set_compat_all(uint32_t compat_pvr,Error ** errp)2222c82e8dfSGreg Kurz int ppc_set_compat_all(uint32_t compat_pvr, Error **errp)
223f6f242c7SDavid Gibson {
224f6f242c7SDavid Gibson     CPUState *cs;
225f6f242c7SDavid Gibson 
226f6f242c7SDavid Gibson     CPU_FOREACH(cs) {
227f6f242c7SDavid Gibson         SetCompatState s = {
228f6f242c7SDavid Gibson             .compat_pvr = compat_pvr,
2292c82e8dfSGreg Kurz             .errp = errp,
2302c82e8dfSGreg Kurz             .ret = 0,
231f6f242c7SDavid Gibson         };
232f6f242c7SDavid Gibson 
233f6f242c7SDavid Gibson         run_on_cpu(cs, do_set_compat, RUN_ON_CPU_HOST_PTR(&s));
234f6f242c7SDavid Gibson 
2352c82e8dfSGreg Kurz         if (s.ret < 0) {
2362c82e8dfSGreg Kurz             return s.ret;
237f6f242c7SDavid Gibson         }
238f6f242c7SDavid Gibson     }
2392c82e8dfSGreg Kurz 
2402c82e8dfSGreg Kurz     return 0;
241f6f242c7SDavid Gibson }
242f6f242c7SDavid Gibson 
2439c7b7f01SNicholas Piggin /* To be used when the machine is not running */
ppc_init_compat_all(uint32_t compat_pvr,Error ** errp)2449c7b7f01SNicholas Piggin int ppc_init_compat_all(uint32_t compat_pvr, Error **errp)
2459c7b7f01SNicholas Piggin {
2469c7b7f01SNicholas Piggin     CPUState *cs;
2479c7b7f01SNicholas Piggin 
2489c7b7f01SNicholas Piggin     CPU_FOREACH(cs) {
2499c7b7f01SNicholas Piggin         PowerPCCPU *cpu = POWERPC_CPU(cs);
2509c7b7f01SNicholas Piggin         int ret;
2519c7b7f01SNicholas Piggin 
2529c7b7f01SNicholas Piggin         ret = ppc_set_compat(cpu, compat_pvr, errp);
2539c7b7f01SNicholas Piggin 
2549c7b7f01SNicholas Piggin         if (ret < 0) {
2559c7b7f01SNicholas Piggin             return ret;
2569c7b7f01SNicholas Piggin         }
2579c7b7f01SNicholas Piggin     }
2589c7b7f01SNicholas Piggin 
2599c7b7f01SNicholas Piggin     return 0;
2609c7b7f01SNicholas Piggin }
2619c7b7f01SNicholas Piggin 
ppc_compat_max_vthreads(PowerPCCPU * cpu)262abbc1247SDavid Gibson int ppc_compat_max_vthreads(PowerPCCPU *cpu)
26312dbeb16SDavid Gibson {
26412dbeb16SDavid Gibson     const CompatInfo *compat = compat_by_pvr(cpu->compat_pvr);
26512dbeb16SDavid Gibson     int n_threads = CPU(cpu)->nr_threads;
26612dbeb16SDavid Gibson 
26712dbeb16SDavid Gibson     if (cpu->compat_pvr) {
26812dbeb16SDavid Gibson         g_assert(compat);
269abbc1247SDavid Gibson         n_threads = MIN(n_threads, compat->max_vthreads);
27012dbeb16SDavid Gibson     }
27112dbeb16SDavid Gibson 
27212dbeb16SDavid Gibson     return n_threads;
27312dbeb16SDavid Gibson }
2747843c0d6SDavid Gibson 
ppc_compat_prop_get(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)2757843c0d6SDavid Gibson static void ppc_compat_prop_get(Object *obj, Visitor *v, const char *name,
2767843c0d6SDavid Gibson                                 void *opaque, Error **errp)
2777843c0d6SDavid Gibson {
2787843c0d6SDavid Gibson     uint32_t compat_pvr = *((uint32_t *)opaque);
2797843c0d6SDavid Gibson     const char *value;
2807843c0d6SDavid Gibson 
2817843c0d6SDavid Gibson     if (!compat_pvr) {
2827843c0d6SDavid Gibson         value = "";
2837843c0d6SDavid Gibson     } else {
2847843c0d6SDavid Gibson         const CompatInfo *compat = compat_by_pvr(compat_pvr);
2857843c0d6SDavid Gibson 
2867843c0d6SDavid Gibson         g_assert(compat);
2877843c0d6SDavid Gibson 
2887843c0d6SDavid Gibson         value = compat->name;
2897843c0d6SDavid Gibson     }
2907843c0d6SDavid Gibson 
2917843c0d6SDavid Gibson     visit_type_str(v, name, (char **)&value, errp);
2927843c0d6SDavid Gibson }
2937843c0d6SDavid Gibson 
ppc_compat_prop_set(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)2947843c0d6SDavid Gibson static void ppc_compat_prop_set(Object *obj, Visitor *v, const char *name,
2957843c0d6SDavid Gibson                                 void *opaque, Error **errp)
2967843c0d6SDavid Gibson {
2977843c0d6SDavid Gibson     char *value;
2987843c0d6SDavid Gibson     uint32_t compat_pvr;
2997843c0d6SDavid Gibson 
300668f62ecSMarkus Armbruster     if (!visit_type_str(v, name, &value, errp)) {
3017843c0d6SDavid Gibson         return;
3027843c0d6SDavid Gibson     }
3037843c0d6SDavid Gibson 
3047843c0d6SDavid Gibson     if (strcmp(value, "") == 0) {
3057843c0d6SDavid Gibson         compat_pvr = 0;
3067843c0d6SDavid Gibson     } else {
3077843c0d6SDavid Gibson         int i;
3087843c0d6SDavid Gibson         const CompatInfo *compat = NULL;
3097843c0d6SDavid Gibson 
3107843c0d6SDavid Gibson         for (i = 0; i < ARRAY_SIZE(compat_table); i++) {
3117843c0d6SDavid Gibson             if (strcmp(value, compat_table[i].name) == 0) {
3127843c0d6SDavid Gibson                 compat = &compat_table[i];
3137843c0d6SDavid Gibson                 break;
3147843c0d6SDavid Gibson 
3157843c0d6SDavid Gibson             }
3167843c0d6SDavid Gibson         }
3177843c0d6SDavid Gibson 
3187843c0d6SDavid Gibson         if (!compat) {
3197843c0d6SDavid Gibson             error_setg(errp, "Invalid compatibility mode \"%s\"", value);
3207843c0d6SDavid Gibson             goto out;
3217843c0d6SDavid Gibson         }
3227843c0d6SDavid Gibson         compat_pvr = compat->pvr;
3237843c0d6SDavid Gibson     }
3247843c0d6SDavid Gibson 
3257843c0d6SDavid Gibson     *((uint32_t *)opaque) = compat_pvr;
3267843c0d6SDavid Gibson 
3277843c0d6SDavid Gibson out:
3287843c0d6SDavid Gibson     g_free(value);
3297843c0d6SDavid Gibson }
3307843c0d6SDavid Gibson 
ppc_compat_add_property(Object * obj,const char * name,uint32_t * compat_pvr,const char * basedesc)3317843c0d6SDavid Gibson void ppc_compat_add_property(Object *obj, const char *name,
33240c2281cSMarkus Armbruster                              uint32_t *compat_pvr, const char *basedesc)
3337843c0d6SDavid Gibson {
3347843c0d6SDavid Gibson     gchar *namesv[ARRAY_SIZE(compat_table) + 1];
3357843c0d6SDavid Gibson     gchar *names, *desc;
3367843c0d6SDavid Gibson     int i;
3377843c0d6SDavid Gibson 
3387843c0d6SDavid Gibson     object_property_add(obj, name, "string",
3397843c0d6SDavid Gibson                         ppc_compat_prop_get, ppc_compat_prop_set, NULL,
340d2623129SMarkus Armbruster                         compat_pvr);
3417843c0d6SDavid Gibson 
3427843c0d6SDavid Gibson     for (i = 0; i < ARRAY_SIZE(compat_table); i++) {
3437843c0d6SDavid Gibson         /*
3447843c0d6SDavid Gibson          * Have to discard const here, because g_strjoinv() takes
3457843c0d6SDavid Gibson          * (gchar **), not (const gchar **) :(
3467843c0d6SDavid Gibson          */
3477843c0d6SDavid Gibson         namesv[i] = (gchar *)compat_table[i].name;
3487843c0d6SDavid Gibson     }
3497843c0d6SDavid Gibson     namesv[ARRAY_SIZE(compat_table)] = NULL;
3507843c0d6SDavid Gibson 
3517843c0d6SDavid Gibson     names = g_strjoinv(", ", namesv);
3527843c0d6SDavid Gibson     desc = g_strdup_printf("%s. Valid values are %s.", basedesc, names);
3537eecec7dSMarkus Armbruster     object_property_set_description(obj, name, desc);
3547843c0d6SDavid Gibson 
3557843c0d6SDavid Gibson     g_free(names);
3567843c0d6SDavid Gibson     g_free(desc);
3577843c0d6SDavid Gibson }
358