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