]>
Commit | Line | Data |
---|---|---|
a404b612 | 1 | /* |
8d76bfe8 | 2 | * qdev property parsing |
a404b612 EH |
3 | * (parts specific for qemu-system-*) |
4 | * | |
5 | * This file is based on code from hw/qdev-properties.c from | |
6 | * commit 074a86fccd185616469dfcdc0e157f438aebba18, | |
7 | * Copyright (c) Gerd Hoffmann <kraxel@redhat.com> and other contributors. | |
8 | * | |
9 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
10 | * See the COPYING file in the top-level directory. | |
11 | */ | |
12 | ||
18c86e2b | 13 | #include "qemu/osdep.h" |
a27bd6c7 | 14 | #include "hw/qdev-properties.h" |
ce35e229 | 15 | #include "hw/qdev-properties-system.h" |
da34e65c | 16 | #include "qapi/error.h" |
aa1859cc PMD |
17 | #include "qapi/visitor.h" |
18 | #include "qapi/qapi-types-block.h" | |
19 | #include "qapi/qapi-types-machine.h" | |
20 | #include "qapi/qapi-types-migration.h" | |
501a7ce7 | 21 | #include "qapi/qmp/qerror.h" |
aa1859cc PMD |
22 | #include "qemu/ctype.h" |
23 | #include "qemu/cutils.h" | |
24 | #include "qemu/units.h" | |
38255eff | 25 | #include "qemu/uuid.h" |
aa1859cc PMD |
26 | #include "qemu/error-report.h" |
27 | #include "qdev-prop-internal.h" | |
28 | ||
29 | #include "audio/audio.h" | |
30 | #include "chardev/char-fe.h" | |
4be74634 | 31 | #include "sysemu/block-backend.h" |
501a7ce7 | 32 | #include "sysemu/blockdev.h" |
aa1859cc PMD |
33 | #include "net/net.h" |
34 | #include "hw/pci/pci.h" | |
edf5ca5d | 35 | #include "hw/pci/pcie.h" |
aae16721 | 36 | #include "hw/i386/x86.h" |
5937835a | 37 | #include "util/block-helpers.h" |
a404b612 | 38 | |
38148159 | 39 | static bool check_prop_still_unset(Object *obj, const char *name, |
1bc13336 | 40 | const void *old_val, const char *new_val, |
d1a58c17 | 41 | bool allow_override, Error **errp) |
1bc13336 | 42 | { |
38148159 | 43 | const GlobalProperty *prop = qdev_find_global_prop(obj, name); |
1bc13336 | 44 | |
d1a58c17 | 45 | if (!old_val || (!prop && allow_override)) { |
1bc13336 MA |
46 | return true; |
47 | } | |
48 | ||
49 | if (prop) { | |
50 | error_setg(errp, "-global %s.%s=... conflicts with %s=%s", | |
51 | prop->driver, prop->property, name, new_val); | |
52 | } else { | |
53 | /* Error message is vague, but a better one would be hard */ | |
54 | error_setg(errp, "%s=%s conflicts, and override is not implemented", | |
55 | name, new_val); | |
56 | } | |
57 | return false; | |
58 | } | |
59 | ||
60 | ||
466c2983 MA |
61 | /* --- drive --- */ |
62 | ||
63 | static void get_drive(Object *obj, Visitor *v, const char *name, void *opaque, | |
64 | Error **errp) | |
a404b612 | 65 | { |
466c2983 | 66 | Property *prop = opaque; |
1e198715 | 67 | void **ptr = object_field_prop_ptr(obj, prop); |
466c2983 | 68 | const char *value; |
a404b612 EH |
69 | char *p; |
70 | ||
466c2983 MA |
71 | if (*ptr) { |
72 | value = blk_name(*ptr); | |
73 | if (!*value) { | |
74 | BlockDriverState *bs = blk_bs(*ptr); | |
75 | if (bs) { | |
76 | value = bdrv_get_node_name(bs); | |
77 | } | |
78 | } | |
79 | } else { | |
80 | value = ""; | |
81 | } | |
82 | ||
83 | p = g_strdup(value); | |
51e72bc1 | 84 | visit_type_str(v, name, &p, errp); |
7d1de464 | 85 | g_free(p); |
a404b612 EH |
86 | } |
87 | ||
466c2983 MA |
88 | static void set_drive_helper(Object *obj, Visitor *v, const char *name, |
89 | void *opaque, bool iothread, Error **errp) | |
a404b612 EH |
90 | { |
91 | DeviceState *dev = DEVICE(obj); | |
466c2983 | 92 | Property *prop = opaque; |
1e198715 | 93 | void **ptr = object_field_prop_ptr(obj, prop); |
a404b612 | 94 | char *str; |
466c2983 MA |
95 | BlockBackend *blk; |
96 | bool blk_created = false; | |
97 | int ret; | |
d1a58c17 VSO |
98 | BlockDriverState *bs; |
99 | AioContext *ctx; | |
a404b612 | 100 | |
668f62ec | 101 | if (!visit_type_str(v, name, &str, errp)) { |
a404b612 EH |
102 | return; |
103 | } | |
466c2983 | 104 | |
d1a58c17 VSO |
105 | if (!check_prop_still_unset(obj, name, *ptr, str, true, errp)) { |
106 | return; | |
107 | } | |
108 | ||
109 | if (*ptr) { | |
9b4b4e51 | 110 | /* BlockBackend already exists. So, we want to change attached node */ |
d1a58c17 VSO |
111 | blk = *ptr; |
112 | ctx = blk_get_aio_context(blk); | |
113 | bs = bdrv_lookup_bs(NULL, str, errp); | |
114 | if (!bs) { | |
115 | return; | |
116 | } | |
117 | ||
118 | if (ctx != bdrv_get_aio_context(bs)) { | |
119 | error_setg(errp, "Different aio context is not supported for new " | |
120 | "node"); | |
121 | } | |
122 | ||
123 | aio_context_acquire(ctx); | |
124 | blk_replace_bs(blk, bs, errp); | |
125 | aio_context_release(ctx); | |
84b0475c MA |
126 | return; |
127 | } | |
128 | ||
a404b612 EH |
129 | if (!*str) { |
130 | g_free(str); | |
131 | *ptr = NULL; | |
132 | return; | |
133 | } | |
a404b612 | 134 | |
4be74634 | 135 | blk = blk_by_name(str); |
8daea510 | 136 | if (!blk) { |
d1a58c17 | 137 | bs = bdrv_lookup_bs(NULL, str, NULL); |
8daea510 | 138 | if (bs) { |
307a5f60 KW |
139 | /* |
140 | * If the device supports iothreads, it will make sure to move the | |
141 | * block node to the right AioContext if necessary (or fail if this | |
142 | * isn't possible because of other users). Devices that are not | |
143 | * aware of iothreads require their BlockBackends to be in the main | |
144 | * AioContext. | |
145 | */ | |
415275ae KW |
146 | ctx = bdrv_get_aio_context(bs); |
147 | blk = blk_new(iothread ? ctx : qemu_get_aio_context(), | |
148 | 0, BLK_PERM_ALL); | |
8daea510 | 149 | blk_created = true; |
d7086422 | 150 | |
415275ae | 151 | aio_context_acquire(ctx); |
d7086422 | 152 | ret = blk_insert_bs(blk, bs, errp); |
415275ae KW |
153 | aio_context_release(ctx); |
154 | ||
d7086422 KW |
155 | if (ret < 0) { |
156 | goto fail; | |
157 | } | |
8daea510 KW |
158 | } |
159 | } | |
4be74634 | 160 | if (!blk) { |
f1fb9f0d | 161 | error_setg(errp, "Property '%s.%s' can't find value '%s'", |
991f0ac9 | 162 | object_get_typename(OBJECT(dev)), name, str); |
8daea510 | 163 | goto fail; |
a404b612 | 164 | } |
4be74634 | 165 | if (blk_attach_dev(blk, dev) < 0) { |
62f7dbde PM |
166 | DriveInfo *dinfo = blk_legacy_dinfo(blk); |
167 | ||
a9d52a75 | 168 | if (dinfo && dinfo->type != IF_NONE) { |
62f7dbde PM |
169 | error_setg(errp, "Drive '%s' is already in use because " |
170 | "it has been automatically connected to another " | |
171 | "device (did you need 'if=none' in the drive options?)", | |
172 | str); | |
173 | } else { | |
174 | error_setg(errp, "Drive '%s' is already in use by another device", | |
175 | str); | |
176 | } | |
8daea510 | 177 | goto fail; |
a404b612 | 178 | } |
8daea510 | 179 | |
4be74634 | 180 | *ptr = blk; |
8daea510 KW |
181 | |
182 | fail: | |
183 | if (blk_created) { | |
184 | /* If we need to keep a reference, blk_attach_dev() took it */ | |
185 | blk_unref(blk); | |
186 | } | |
466c2983 MA |
187 | |
188 | g_free(str); | |
a404b612 EH |
189 | } |
190 | ||
466c2983 MA |
191 | static void set_drive(Object *obj, Visitor *v, const char *name, void *opaque, |
192 | Error **errp) | |
307a5f60 | 193 | { |
466c2983 | 194 | set_drive_helper(obj, v, name, opaque, false, errp); |
307a5f60 KW |
195 | } |
196 | ||
466c2983 MA |
197 | static void set_drive_iothread(Object *obj, Visitor *v, const char *name, |
198 | void *opaque, Error **errp) | |
307a5f60 | 199 | { |
466c2983 | 200 | set_drive_helper(obj, v, name, opaque, true, errp); |
307a5f60 KW |
201 | } |
202 | ||
a404b612 EH |
203 | static void release_drive(Object *obj, const char *name, void *opaque) |
204 | { | |
205 | DeviceState *dev = DEVICE(obj); | |
206 | Property *prop = opaque; | |
1e198715 | 207 | BlockBackend **ptr = object_field_prop_ptr(obj, prop); |
a404b612 EH |
208 | |
209 | if (*ptr) { | |
9588c589 DL |
210 | AioContext *ctx = blk_get_aio_context(*ptr); |
211 | ||
212 | aio_context_acquire(ctx); | |
a404b612 | 213 | blockdev_auto_del(*ptr); |
8daea510 | 214 | blk_detach_dev(*ptr, dev); |
9588c589 | 215 | aio_context_release(ctx); |
a404b612 EH |
216 | } |
217 | } | |
218 | ||
1b6b7d10 | 219 | const PropertyInfo qdev_prop_drive = { |
85ca1202 | 220 | .name = "str", |
8daea510 | 221 | .description = "Node name or ID of a block device to use as a backend", |
d1a58c17 | 222 | .realized_set_allowed = true, |
a404b612 EH |
223 | .get = get_drive, |
224 | .set = set_drive, | |
225 | .release = release_drive, | |
226 | }; | |
227 | ||
307a5f60 KW |
228 | const PropertyInfo qdev_prop_drive_iothread = { |
229 | .name = "str", | |
230 | .description = "Node name or ID of a block device to use as a backend", | |
d1a58c17 | 231 | .realized_set_allowed = true, |
307a5f60 KW |
232 | .get = get_drive, |
233 | .set = set_drive_iothread, | |
234 | .release = release_drive, | |
235 | }; | |
236 | ||
a404b612 EH |
237 | /* --- character device --- */ |
238 | ||
becdfa00 MAL |
239 | static void get_chr(Object *obj, Visitor *v, const char *name, void *opaque, |
240 | Error **errp) | |
a404b612 | 241 | { |
1e198715 | 242 | CharBackend *be = object_field_prop_ptr(obj, opaque); |
becdfa00 MAL |
243 | char *p; |
244 | ||
245 | p = g_strdup(be->chr && be->chr->label ? be->chr->label : ""); | |
246 | visit_type_str(v, name, &p, errp); | |
247 | g_free(p); | |
a404b612 EH |
248 | } |
249 | ||
becdfa00 MAL |
250 | static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque, |
251 | Error **errp) | |
a404b612 | 252 | { |
a404b612 | 253 | Property *prop = opaque; |
1e198715 | 254 | CharBackend *be = object_field_prop_ptr(obj, prop); |
0ec7b3e7 | 255 | Chardev *s; |
becdfa00 | 256 | char *str; |
a404b612 | 257 | |
668f62ec | 258 | if (!visit_type_str(v, name, &str, errp)) { |
becdfa00 MAL |
259 | return; |
260 | } | |
a404b612 | 261 | |
9572a787 MA |
262 | /* |
263 | * TODO Should this really be an error? If no, the old value | |
264 | * needs to be released before we store the new one. | |
265 | */ | |
d1a58c17 | 266 | if (!check_prop_still_unset(obj, name, be->chr, str, false, errp)) { |
9572a787 MA |
267 | return; |
268 | } | |
269 | ||
becdfa00 MAL |
270 | if (!*str) { |
271 | g_free(str); | |
272 | be->chr = NULL; | |
273 | return; | |
274 | } | |
a404b612 | 275 | |
becdfa00 | 276 | s = qemu_chr_find(str); |
becdfa00 MAL |
277 | if (s == NULL) { |
278 | error_setg(errp, "Property '%s.%s' can't find value '%s'", | |
991f0ac9 | 279 | object_get_typename(obj), name, str); |
2209401f | 280 | } else if (!qemu_chr_fe_init(be, s, errp)) { |
c39860e6 | 281 | error_prepend(errp, "Property '%s.%s' can't take value '%s': ", |
991f0ac9 | 282 | object_get_typename(obj), name, str); |
becdfa00 | 283 | } |
2209401f | 284 | g_free(str); |
a404b612 EH |
285 | } |
286 | ||
becdfa00 | 287 | static void release_chr(Object *obj, const char *name, void *opaque) |
a404b612 | 288 | { |
becdfa00 | 289 | Property *prop = opaque; |
1e198715 | 290 | CharBackend *be = object_field_prop_ptr(obj, prop); |
becdfa00 | 291 | |
1ce2610c | 292 | qemu_chr_fe_deinit(be, false); |
a404b612 EH |
293 | } |
294 | ||
1b6b7d10 | 295 | const PropertyInfo qdev_prop_chr = { |
85ca1202 | 296 | .name = "str", |
51b2e8c3 | 297 | .description = "ID of a chardev to use as a backend", |
a404b612 EH |
298 | .get = get_chr, |
299 | .set = set_chr, | |
300 | .release = release_chr, | |
301 | }; | |
302 | ||
aa1859cc PMD |
303 | /* --- mac address --- */ |
304 | ||
305 | /* | |
306 | * accepted syntax versions: | |
307 | * 01:02:03:04:05:06 | |
308 | * 01-02-03-04-05-06 | |
309 | */ | |
310 | static void get_mac(Object *obj, Visitor *v, const char *name, void *opaque, | |
311 | Error **errp) | |
312 | { | |
aa1859cc | 313 | Property *prop = opaque; |
1e198715 | 314 | MACAddr *mac = object_field_prop_ptr(obj, prop); |
aa1859cc PMD |
315 | char buffer[2 * 6 + 5 + 1]; |
316 | char *p = buffer; | |
317 | ||
318 | snprintf(buffer, sizeof(buffer), "%02x:%02x:%02x:%02x:%02x:%02x", | |
319 | mac->a[0], mac->a[1], mac->a[2], | |
320 | mac->a[3], mac->a[4], mac->a[5]); | |
321 | ||
322 | visit_type_str(v, name, &p, errp); | |
323 | } | |
324 | ||
325 | static void set_mac(Object *obj, Visitor *v, const char *name, void *opaque, | |
326 | Error **errp) | |
327 | { | |
aa1859cc | 328 | Property *prop = opaque; |
1e198715 | 329 | MACAddr *mac = object_field_prop_ptr(obj, prop); |
aa1859cc PMD |
330 | int i, pos; |
331 | char *str; | |
332 | const char *p; | |
333 | ||
aa1859cc PMD |
334 | if (!visit_type_str(v, name, &str, errp)) { |
335 | return; | |
336 | } | |
337 | ||
338 | for (i = 0, pos = 0; i < 6; i++, pos += 3) { | |
339 | long val; | |
340 | ||
341 | if (!qemu_isxdigit(str[pos])) { | |
342 | goto inval; | |
343 | } | |
344 | if (!qemu_isxdigit(str[pos + 1])) { | |
345 | goto inval; | |
346 | } | |
347 | if (i == 5) { | |
348 | if (str[pos + 2] != '\0') { | |
349 | goto inval; | |
350 | } | |
351 | } else { | |
352 | if (str[pos + 2] != ':' && str[pos + 2] != '-') { | |
353 | goto inval; | |
354 | } | |
355 | } | |
356 | if (qemu_strtol(str + pos, &p, 16, &val) < 0 || val > 0xff) { | |
357 | goto inval; | |
358 | } | |
359 | mac->a[i] = val; | |
360 | } | |
361 | g_free(str); | |
362 | return; | |
363 | ||
364 | inval: | |
e68c2cb7 | 365 | error_set_from_qdev_prop_error(errp, EINVAL, obj, name, str); |
aa1859cc PMD |
366 | g_free(str); |
367 | } | |
368 | ||
369 | const PropertyInfo qdev_prop_macaddr = { | |
370 | .name = "str", | |
371 | .description = "Ethernet 6-byte MAC Address, example: 52:54:00:12:34:56", | |
372 | .get = get_mac, | |
373 | .set = set_mac, | |
374 | }; | |
375 | ||
376 | void qdev_prop_set_macaddr(DeviceState *dev, const char *name, | |
377 | const uint8_t *value) | |
378 | { | |
379 | char str[2 * 6 + 5 + 1]; | |
380 | snprintf(str, sizeof(str), "%02x:%02x:%02x:%02x:%02x:%02x", | |
381 | value[0], value[1], value[2], value[3], value[4], value[5]); | |
382 | ||
383 | object_property_set_str(OBJECT(dev), name, str, &error_abort); | |
384 | } | |
385 | ||
a404b612 | 386 | /* --- netdev device --- */ |
d7bce999 EB |
387 | static void get_netdev(Object *obj, Visitor *v, const char *name, |
388 | void *opaque, Error **errp) | |
23120b13 | 389 | { |
23120b13 | 390 | Property *prop = opaque; |
1e198715 | 391 | NICPeers *peers_ptr = object_field_prop_ptr(obj, prop); |
23120b13 JW |
392 | char *p = g_strdup(peers_ptr->ncs[0] ? peers_ptr->ncs[0]->name : ""); |
393 | ||
51e72bc1 | 394 | visit_type_str(v, name, &p, errp); |
23120b13 JW |
395 | g_free(p); |
396 | } | |
a404b612 | 397 | |
d7bce999 EB |
398 | static void set_netdev(Object *obj, Visitor *v, const char *name, |
399 | void *opaque, Error **errp) | |
a404b612 | 400 | { |
23120b13 | 401 | Property *prop = opaque; |
1e198715 | 402 | NICPeers *peers_ptr = object_field_prop_ptr(obj, prop); |
1ceef9f2 JW |
403 | NetClientState **ncs = peers_ptr->ncs; |
404 | NetClientState *peers[MAX_QUEUE_NUM]; | |
23120b13 JW |
405 | int queues, err = 0, i = 0; |
406 | char *str; | |
407 | ||
668f62ec | 408 | if (!visit_type_str(v, name, &str, errp)) { |
23120b13 JW |
409 | return; |
410 | } | |
a404b612 | 411 | |
1ceef9f2 | 412 | queues = qemu_find_net_clients_except(str, peers, |
f394b2e2 | 413 | NET_CLIENT_DRIVER_NIC, |
1ceef9f2 JW |
414 | MAX_QUEUE_NUM); |
415 | if (queues == 0) { | |
23120b13 JW |
416 | err = -ENOENT; |
417 | goto out; | |
a404b612 | 418 | } |
1ceef9f2 JW |
419 | |
420 | if (queues > MAX_QUEUE_NUM) { | |
23120b13 JW |
421 | error_setg(errp, "queues of backend '%s'(%d) exceeds QEMU limitation(%d)", |
422 | str, queues, MAX_QUEUE_NUM); | |
423 | goto out; | |
1ceef9f2 JW |
424 | } |
425 | ||
426 | for (i = 0; i < queues; i++) { | |
1ceef9f2 | 427 | if (peers[i]->peer) { |
23120b13 JW |
428 | err = -EEXIST; |
429 | goto out; | |
1ceef9f2 JW |
430 | } |
431 | ||
1bc13336 MA |
432 | /* |
433 | * TODO Should this really be an error? If no, the old value | |
434 | * needs to be released before we store the new one. | |
435 | */ | |
d1a58c17 | 436 | if (!check_prop_still_unset(obj, name, ncs[i], str, false, errp)) { |
23120b13 | 437 | goto out; |
30c367ed VY |
438 | } |
439 | ||
e287bf7b KW |
440 | if (peers[i]->info->check_peer_type) { |
441 | if (!peers[i]->info->check_peer_type(peers[i], obj->class, errp)) { | |
442 | goto out; | |
443 | } | |
444 | } | |
445 | ||
1ceef9f2 JW |
446 | ncs[i] = peers[i]; |
447 | ncs[i]->queue_index = i; | |
a404b612 | 448 | } |
1ceef9f2 | 449 | |
575a1c0e | 450 | peers_ptr->queues = queues; |
1ceef9f2 | 451 | |
23120b13 | 452 | out: |
3257b854 | 453 | error_set_from_qdev_prop_error(errp, err, obj, prop->name, str); |
23120b13 | 454 | g_free(str); |
a404b612 EH |
455 | } |
456 | ||
1b6b7d10 | 457 | const PropertyInfo qdev_prop_netdev = { |
85ca1202 | 458 | .name = "str", |
51b2e8c3 | 459 | .description = "ID of a netdev to use as a backend", |
a404b612 EH |
460 | .get = get_netdev, |
461 | .set = set_netdev, | |
462 | }; | |
463 | ||
a404b612 | 464 | |
88e47b9a KZ |
465 | /* --- audiodev --- */ |
466 | static void get_audiodev(Object *obj, Visitor *v, const char* name, | |
467 | void *opaque, Error **errp) | |
468 | { | |
88e47b9a | 469 | Property *prop = opaque; |
1e198715 | 470 | QEMUSoundCard *card = object_field_prop_ptr(obj, prop); |
88e47b9a KZ |
471 | char *p = g_strdup(audio_get_id(card)); |
472 | ||
473 | visit_type_str(v, name, &p, errp); | |
474 | g_free(p); | |
475 | } | |
476 | ||
477 | static void set_audiodev(Object *obj, Visitor *v, const char* name, | |
478 | void *opaque, Error **errp) | |
479 | { | |
88e47b9a | 480 | Property *prop = opaque; |
1e198715 | 481 | QEMUSoundCard *card = object_field_prop_ptr(obj, prop); |
88e47b9a | 482 | AudioState *state; |
176adafc | 483 | g_autofree char *str = NULL; |
88e47b9a | 484 | |
668f62ec | 485 | if (!visit_type_str(v, name, &str, errp)) { |
88e47b9a KZ |
486 | return; |
487 | } | |
488 | ||
176adafc PB |
489 | state = audio_state_by_name(str, errp); |
490 | if (state) { | |
491 | card->state = state; | |
88e47b9a | 492 | } |
88e47b9a KZ |
493 | } |
494 | ||
495 | const PropertyInfo qdev_prop_audiodev = { | |
496 | .name = "str", | |
497 | .description = "ID of an audiodev to use as a backend", | |
498 | /* release done on shutdown */ | |
499 | .get = get_audiodev, | |
500 | .set = set_audiodev, | |
501 | }; | |
502 | ||
73ac1aac | 503 | bool qdev_prop_set_drive_err(DeviceState *dev, const char *name, |
934df912 | 504 | BlockBackend *value, Error **errp) |
a404b612 | 505 | { |
8daea510 KW |
506 | const char *ref = ""; |
507 | ||
508 | if (value) { | |
509 | ref = blk_name(value); | |
510 | if (!*ref) { | |
be9721f4 | 511 | const BlockDriverState *bs = blk_bs(value); |
8daea510 KW |
512 | if (bs) { |
513 | ref = bdrv_get_node_name(bs); | |
514 | } | |
515 | } | |
516 | } | |
517 | ||
73ac1aac | 518 | return object_property_set_str(OBJECT(dev), name, ref, errp); |
a404b612 EH |
519 | } |
520 | ||
934df912 MA |
521 | void qdev_prop_set_drive(DeviceState *dev, const char *name, |
522 | BlockBackend *value) | |
523 | { | |
524 | qdev_prop_set_drive_err(dev, name, value, &error_abort); | |
525 | } | |
526 | ||
a404b612 | 527 | void qdev_prop_set_chr(DeviceState *dev, const char *name, |
0ec7b3e7 | 528 | Chardev *value) |
a404b612 | 529 | { |
a404b612 | 530 | assert(!value || value->label); |
5325cc34 MA |
531 | object_property_set_str(OBJECT(dev), name, value ? value->label : "", |
532 | &error_abort); | |
a404b612 EH |
533 | } |
534 | ||
535 | void qdev_prop_set_netdev(DeviceState *dev, const char *name, | |
536 | NetClientState *value) | |
537 | { | |
a404b612 | 538 | assert(!value || value->name); |
5325cc34 MA |
539 | object_property_set_str(OBJECT(dev), name, value ? value->name : "", |
540 | &error_abort); | |
a404b612 EH |
541 | } |
542 | ||
543 | void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd) | |
544 | { | |
545 | qdev_prop_set_macaddr(dev, "mac", nd->macaddr.a); | |
546 | if (nd->netdev) { | |
547 | qdev_prop_set_netdev(dev, "netdev", nd->netdev); | |
548 | } | |
549 | if (nd->nvectors != DEV_NVECTORS_UNSPECIFIED && | |
efba1595 | 550 | object_property_find(OBJECT(dev), "vectors")) { |
a404b612 EH |
551 | qdev_prop_set_uint32(dev, "vectors", nd->nvectors); |
552 | } | |
553 | nd->instantiated = 1; | |
554 | } | |
aa1859cc PMD |
555 | |
556 | /* --- lost tick policy --- */ | |
557 | ||
aae16721 TH |
558 | static void qdev_propinfo_set_losttickpolicy(Object *obj, Visitor *v, |
559 | const char *name, void *opaque, | |
560 | Error **errp) | |
561 | { | |
562 | Property *prop = opaque; | |
563 | int *ptr = object_field_prop_ptr(obj, prop); | |
564 | int value; | |
565 | ||
566 | if (!visit_type_enum(v, name, &value, prop->info->enum_table, errp)) { | |
567 | return; | |
568 | } | |
569 | ||
570 | if (value == LOST_TICK_POLICY_SLEW) { | |
571 | MachineState *ms = MACHINE(qdev_get_machine()); | |
572 | ||
573 | if (!object_dynamic_cast(OBJECT(ms), TYPE_X86_MACHINE)) { | |
574 | error_setg(errp, | |
575 | "the 'slew' policy is only available for x86 machines"); | |
576 | return; | |
577 | } | |
578 | } | |
579 | ||
580 | *ptr = value; | |
581 | } | |
582 | ||
aa1859cc PMD |
583 | QEMU_BUILD_BUG_ON(sizeof(LostTickPolicy) != sizeof(int)); |
584 | ||
585 | const PropertyInfo qdev_prop_losttickpolicy = { | |
586 | .name = "LostTickPolicy", | |
587 | .enum_table = &LostTickPolicy_lookup, | |
588 | .get = qdev_propinfo_get_enum, | |
aae16721 | 589 | .set = qdev_propinfo_set_losttickpolicy, |
aa1859cc PMD |
590 | .set_default_value = qdev_propinfo_set_default_value_enum, |
591 | }; | |
592 | ||
593 | /* --- blocksize --- */ | |
594 | ||
aa1859cc PMD |
595 | static void set_blocksize(Object *obj, Visitor *v, const char *name, |
596 | void *opaque, Error **errp) | |
597 | { | |
598 | DeviceState *dev = DEVICE(obj); | |
599 | Property *prop = opaque; | |
1e198715 | 600 | uint32_t *ptr = object_field_prop_ptr(obj, prop); |
aa1859cc | 601 | uint64_t value; |
5937835a | 602 | Error *local_err = NULL; |
aa1859cc | 603 | |
aa1859cc PMD |
604 | if (!visit_type_size(v, name, &value, errp)) { |
605 | return; | |
606 | } | |
5937835a CX |
607 | check_block_size(dev->id ? : "", name, value, &local_err); |
608 | if (local_err) { | |
609 | error_propagate(errp, local_err); | |
aa1859cc PMD |
610 | return; |
611 | } | |
aa1859cc PMD |
612 | *ptr = value; |
613 | } | |
614 | ||
615 | const PropertyInfo qdev_prop_blocksize = { | |
616 | .name = "size", | |
617 | .description = "A power of two between " MIN_BLOCK_SIZE_STR | |
618 | " and " MAX_BLOCK_SIZE_STR, | |
619 | .get = qdev_propinfo_get_size32, | |
620 | .set = set_blocksize, | |
621 | .set_default_value = qdev_propinfo_set_default_value_uint, | |
622 | }; | |
623 | ||
624 | /* --- Block device error handling policy --- */ | |
625 | ||
626 | QEMU_BUILD_BUG_ON(sizeof(BlockdevOnError) != sizeof(int)); | |
627 | ||
628 | const PropertyInfo qdev_prop_blockdev_on_error = { | |
629 | .name = "BlockdevOnError", | |
630 | .description = "Error handling policy, " | |
631 | "report/ignore/enospc/stop/auto", | |
632 | .enum_table = &BlockdevOnError_lookup, | |
633 | .get = qdev_propinfo_get_enum, | |
634 | .set = qdev_propinfo_set_enum, | |
635 | .set_default_value = qdev_propinfo_set_default_value_enum, | |
636 | }; | |
637 | ||
638 | /* --- BIOS CHS translation */ | |
639 | ||
640 | QEMU_BUILD_BUG_ON(sizeof(BiosAtaTranslation) != sizeof(int)); | |
641 | ||
642 | const PropertyInfo qdev_prop_bios_chs_trans = { | |
643 | .name = "BiosAtaTranslation", | |
644 | .description = "Logical CHS translation algorithm, " | |
645 | "auto/none/lba/large/rechs", | |
646 | .enum_table = &BiosAtaTranslation_lookup, | |
647 | .get = qdev_propinfo_get_enum, | |
648 | .set = qdev_propinfo_set_enum, | |
649 | .set_default_value = qdev_propinfo_set_default_value_enum, | |
650 | }; | |
651 | ||
652 | /* --- FDC default drive types */ | |
653 | ||
654 | const PropertyInfo qdev_prop_fdc_drive_type = { | |
655 | .name = "FdcDriveType", | |
656 | .description = "FDC drive type, " | |
657 | "144/288/120/none/auto", | |
658 | .enum_table = &FloppyDriveType_lookup, | |
659 | .get = qdev_propinfo_get_enum, | |
660 | .set = qdev_propinfo_set_enum, | |
661 | .set_default_value = qdev_propinfo_set_default_value_enum, | |
662 | }; | |
663 | ||
664 | /* --- MultiFDCompression --- */ | |
665 | ||
666 | const PropertyInfo qdev_prop_multifd_compression = { | |
667 | .name = "MultiFDCompression", | |
668 | .description = "multifd_compression values, " | |
669 | "none/zlib/zstd", | |
670 | .enum_table = &MultiFDCompression_lookup, | |
671 | .get = qdev_propinfo_get_enum, | |
672 | .set = qdev_propinfo_set_enum, | |
673 | .set_default_value = qdev_propinfo_set_default_value_enum, | |
674 | }; | |
675 | ||
eea1e5c9 SS |
676 | /* --- MigMode --- */ |
677 | ||
678 | QEMU_BUILD_BUG_ON(sizeof(MigMode) != sizeof(int)); | |
679 | ||
680 | const PropertyInfo qdev_prop_mig_mode = { | |
681 | .name = "MigMode", | |
682 | .description = "mig_mode values, " | |
a87e6451 | 683 | "normal,cpr-reboot", |
eea1e5c9 SS |
684 | .enum_table = &MigMode_lookup, |
685 | .get = qdev_propinfo_get_enum, | |
686 | .set = qdev_propinfo_set_enum, | |
687 | .set_default_value = qdev_propinfo_set_default_value_enum, | |
688 | }; | |
689 | ||
aa1859cc PMD |
690 | /* --- Reserved Region --- */ |
691 | ||
692 | /* | |
693 | * Accepted syntax: | |
694 | * <low address>:<high address>:<type> | |
695 | * where low/high addresses are uint64_t in hexadecimal | |
696 | * and type is a non-negative decimal integer | |
697 | */ | |
698 | static void get_reserved_region(Object *obj, Visitor *v, const char *name, | |
699 | void *opaque, Error **errp) | |
700 | { | |
aa1859cc | 701 | Property *prop = opaque; |
1e198715 | 702 | ReservedRegion *rr = object_field_prop_ptr(obj, prop); |
aa1859cc PMD |
703 | char buffer[64]; |
704 | char *p = buffer; | |
705 | int rc; | |
706 | ||
707 | rc = snprintf(buffer, sizeof(buffer), "0x%"PRIx64":0x%"PRIx64":%u", | |
e8f433f8 | 708 | range_lob(&rr->range), range_upb(&rr->range), rr->type); |
aa1859cc PMD |
709 | assert(rc < sizeof(buffer)); |
710 | ||
711 | visit_type_str(v, name, &p, errp); | |
712 | } | |
713 | ||
714 | static void set_reserved_region(Object *obj, Visitor *v, const char *name, | |
715 | void *opaque, Error **errp) | |
716 | { | |
aa1859cc | 717 | Property *prop = opaque; |
1e198715 | 718 | ReservedRegion *rr = object_field_prop_ptr(obj, prop); |
aa1859cc | 719 | const char *endptr; |
e8f433f8 | 720 | uint64_t lob, upb; |
aa1859cc PMD |
721 | char *str; |
722 | int ret; | |
723 | ||
d1c81c34 | 724 | if (!visit_type_str(v, name, &str, errp)) { |
aa1859cc PMD |
725 | return; |
726 | } | |
727 | ||
e8f433f8 | 728 | ret = qemu_strtou64(str, &endptr, 16, &lob); |
aa1859cc PMD |
729 | if (ret) { |
730 | error_setg(errp, "start address of '%s'" | |
731 | " must be a hexadecimal integer", name); | |
732 | goto out; | |
733 | } | |
734 | if (*endptr != ':') { | |
735 | goto separator_error; | |
736 | } | |
737 | ||
e8f433f8 | 738 | ret = qemu_strtou64(endptr + 1, &endptr, 16, &upb); |
aa1859cc PMD |
739 | if (ret) { |
740 | error_setg(errp, "end address of '%s'" | |
741 | " must be a hexadecimal integer", name); | |
742 | goto out; | |
743 | } | |
744 | if (*endptr != ':') { | |
745 | goto separator_error; | |
746 | } | |
747 | ||
e8f433f8 EA |
748 | range_set_bounds(&rr->range, lob, upb); |
749 | ||
aa1859cc PMD |
750 | ret = qemu_strtoui(endptr + 1, &endptr, 10, &rr->type); |
751 | if (ret) { | |
752 | error_setg(errp, "type of '%s'" | |
753 | " must be a non-negative decimal integer", name); | |
754 | } | |
755 | goto out; | |
756 | ||
757 | separator_error: | |
758 | error_setg(errp, "reserved region fields must be separated with ':'"); | |
759 | out: | |
760 | g_free(str); | |
761 | return; | |
762 | } | |
763 | ||
764 | const PropertyInfo qdev_prop_reserved_region = { | |
765 | .name = "reserved_region", | |
766 | .description = "Reserved Region, example: 0xFEE00000:0xFEEFFFFF:0", | |
767 | .get = get_reserved_region, | |
768 | .set = set_reserved_region, | |
769 | }; | |
770 | ||
771 | /* --- pci address --- */ | |
772 | ||
773 | /* | |
774 | * bus-local address, i.e. "$slot" or "$slot.$fn" | |
775 | */ | |
776 | static void set_pci_devfn(Object *obj, Visitor *v, const char *name, | |
777 | void *opaque, Error **errp) | |
778 | { | |
aa1859cc | 779 | Property *prop = opaque; |
1e198715 | 780 | int32_t value, *ptr = object_field_prop_ptr(obj, prop); |
aa1859cc PMD |
781 | unsigned int slot, fn, n; |
782 | char *str; | |
783 | ||
aa1859cc PMD |
784 | if (!visit_type_str(v, name, &str, NULL)) { |
785 | if (!visit_type_int32(v, name, &value, errp)) { | |
786 | return; | |
787 | } | |
788 | if (value < -1 || value > 255) { | |
789 | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, | |
6cc0667d | 790 | name ? name : "null", "a value between -1 and 255"); |
aa1859cc PMD |
791 | return; |
792 | } | |
793 | *ptr = value; | |
794 | return; | |
795 | } | |
796 | ||
797 | if (sscanf(str, "%x.%x%n", &slot, &fn, &n) != 2) { | |
798 | fn = 0; | |
799 | if (sscanf(str, "%x%n", &slot, &n) != 1) { | |
800 | goto invalid; | |
801 | } | |
802 | } | |
803 | if (str[n] != '\0' || fn > 7 || slot > 31) { | |
804 | goto invalid; | |
805 | } | |
806 | *ptr = slot << 3 | fn; | |
807 | g_free(str); | |
808 | return; | |
809 | ||
810 | invalid: | |
e68c2cb7 | 811 | error_set_from_qdev_prop_error(errp, EINVAL, obj, name, str); |
aa1859cc PMD |
812 | g_free(str); |
813 | } | |
814 | ||
40ea00b0 | 815 | static int print_pci_devfn(Object *obj, Property *prop, char *dest, |
aa1859cc PMD |
816 | size_t len) |
817 | { | |
1e198715 | 818 | int32_t *ptr = object_field_prop_ptr(obj, prop); |
aa1859cc PMD |
819 | |
820 | if (*ptr == -1) { | |
821 | return snprintf(dest, len, "<unset>"); | |
822 | } else { | |
823 | return snprintf(dest, len, "%02x.%x", *ptr >> 3, *ptr & 7); | |
824 | } | |
825 | } | |
826 | ||
827 | const PropertyInfo qdev_prop_pci_devfn = { | |
828 | .name = "int32", | |
829 | .description = "Slot and optional function number, example: 06.0 or 06", | |
830 | .print = print_pci_devfn, | |
831 | .get = qdev_propinfo_get_int32, | |
832 | .set = set_pci_devfn, | |
833 | .set_default_value = qdev_propinfo_set_default_value_int, | |
834 | }; | |
835 | ||
836 | /* --- pci host address --- */ | |
837 | ||
838 | static void get_pci_host_devaddr(Object *obj, Visitor *v, const char *name, | |
839 | void *opaque, Error **errp) | |
840 | { | |
aa1859cc | 841 | Property *prop = opaque; |
1e198715 | 842 | PCIHostDeviceAddress *addr = object_field_prop_ptr(obj, prop); |
aa1859cc PMD |
843 | char buffer[] = "ffff:ff:ff.f"; |
844 | char *p = buffer; | |
845 | int rc = 0; | |
846 | ||
847 | /* | |
848 | * Catch "invalid" device reference from vfio-pci and allow the | |
849 | * default buffer representing the non-existent device to be used. | |
850 | */ | |
851 | if (~addr->domain || ~addr->bus || ~addr->slot || ~addr->function) { | |
852 | rc = snprintf(buffer, sizeof(buffer), "%04x:%02x:%02x.%0d", | |
853 | addr->domain, addr->bus, addr->slot, addr->function); | |
854 | assert(rc == sizeof(buffer) - 1); | |
855 | } | |
856 | ||
857 | visit_type_str(v, name, &p, errp); | |
858 | } | |
859 | ||
860 | /* | |
861 | * Parse [<domain>:]<bus>:<slot>.<func> | |
862 | * if <domain> is not supplied, it's assumed to be 0. | |
863 | */ | |
864 | static void set_pci_host_devaddr(Object *obj, Visitor *v, const char *name, | |
865 | void *opaque, Error **errp) | |
866 | { | |
aa1859cc | 867 | Property *prop = opaque; |
1e198715 | 868 | PCIHostDeviceAddress *addr = object_field_prop_ptr(obj, prop); |
aa1859cc | 869 | char *str, *p; |
28afbc1f | 870 | char *e; |
aa1859cc PMD |
871 | unsigned long val; |
872 | unsigned long dom = 0, bus = 0; | |
873 | unsigned int slot = 0, func = 0; | |
874 | ||
aa1859cc PMD |
875 | if (!visit_type_str(v, name, &str, errp)) { |
876 | return; | |
877 | } | |
878 | ||
879 | p = str; | |
28afbc1f MT |
880 | val = strtoul(p, &e, 16); |
881 | if (e == p || *e != ':') { | |
aa1859cc PMD |
882 | goto inval; |
883 | } | |
884 | bus = val; | |
885 | ||
28afbc1f MT |
886 | p = e + 1; |
887 | val = strtoul(p, &e, 16); | |
888 | if (e == p) { | |
aa1859cc PMD |
889 | goto inval; |
890 | } | |
891 | if (*e == ':') { | |
892 | dom = bus; | |
893 | bus = val; | |
28afbc1f MT |
894 | p = e + 1; |
895 | val = strtoul(p, &e, 16); | |
896 | if (e == p) { | |
aa1859cc PMD |
897 | goto inval; |
898 | } | |
899 | } | |
900 | slot = val; | |
901 | ||
902 | if (*e != '.') { | |
903 | goto inval; | |
904 | } | |
28afbc1f MT |
905 | p = e + 1; |
906 | val = strtoul(p, &e, 10); | |
907 | if (e == p) { | |
aa1859cc PMD |
908 | goto inval; |
909 | } | |
910 | func = val; | |
911 | ||
28afbc1f | 912 | if (dom > 0xffff || bus > 0xff || slot > 0x1f || func > 7) { |
aa1859cc PMD |
913 | goto inval; |
914 | } | |
915 | ||
916 | if (*e) { | |
917 | goto inval; | |
918 | } | |
919 | ||
920 | addr->domain = dom; | |
921 | addr->bus = bus; | |
922 | addr->slot = slot; | |
923 | addr->function = func; | |
924 | ||
925 | g_free(str); | |
926 | return; | |
927 | ||
928 | inval: | |
e68c2cb7 | 929 | error_set_from_qdev_prop_error(errp, EINVAL, obj, name, str); |
aa1859cc PMD |
930 | g_free(str); |
931 | } | |
932 | ||
933 | const PropertyInfo qdev_prop_pci_host_devaddr = { | |
934 | .name = "str", | |
935 | .description = "Address (bus/device/function) of " | |
936 | "the host device, example: 04:10.0", | |
937 | .get = get_pci_host_devaddr, | |
938 | .set = set_pci_host_devaddr, | |
939 | }; | |
940 | ||
941 | /* --- OffAutoPCIBAR off/auto/bar0/bar1/bar2/bar3/bar4/bar5 --- */ | |
942 | ||
943 | const PropertyInfo qdev_prop_off_auto_pcibar = { | |
944 | .name = "OffAutoPCIBAR", | |
945 | .description = "off/auto/bar0/bar1/bar2/bar3/bar4/bar5", | |
946 | .enum_table = &OffAutoPCIBAR_lookup, | |
947 | .get = qdev_propinfo_get_enum, | |
948 | .set = qdev_propinfo_set_enum, | |
949 | .set_default_value = qdev_propinfo_set_default_value_enum, | |
950 | }; | |
951 | ||
952 | /* --- PCIELinkSpeed 2_5/5/8/16 -- */ | |
953 | ||
954 | static void get_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name, | |
955 | void *opaque, Error **errp) | |
956 | { | |
aa1859cc | 957 | Property *prop = opaque; |
1e198715 | 958 | PCIExpLinkSpeed *p = object_field_prop_ptr(obj, prop); |
aa1859cc PMD |
959 | int speed; |
960 | ||
961 | switch (*p) { | |
962 | case QEMU_PCI_EXP_LNK_2_5GT: | |
963 | speed = PCIE_LINK_SPEED_2_5; | |
964 | break; | |
965 | case QEMU_PCI_EXP_LNK_5GT: | |
966 | speed = PCIE_LINK_SPEED_5; | |
967 | break; | |
968 | case QEMU_PCI_EXP_LNK_8GT: | |
969 | speed = PCIE_LINK_SPEED_8; | |
970 | break; | |
971 | case QEMU_PCI_EXP_LNK_16GT: | |
972 | speed = PCIE_LINK_SPEED_16; | |
973 | break; | |
974 | default: | |
975 | /* Unreachable */ | |
976 | abort(); | |
977 | } | |
978 | ||
991f0ac9 | 979 | visit_type_enum(v, name, &speed, prop->info->enum_table, errp); |
aa1859cc PMD |
980 | } |
981 | ||
982 | static void set_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name, | |
983 | void *opaque, Error **errp) | |
984 | { | |
aa1859cc | 985 | Property *prop = opaque; |
1e198715 | 986 | PCIExpLinkSpeed *p = object_field_prop_ptr(obj, prop); |
aa1859cc PMD |
987 | int speed; |
988 | ||
991f0ac9 | 989 | if (!visit_type_enum(v, name, &speed, prop->info->enum_table, |
aa1859cc PMD |
990 | errp)) { |
991 | return; | |
992 | } | |
993 | ||
994 | switch (speed) { | |
995 | case PCIE_LINK_SPEED_2_5: | |
996 | *p = QEMU_PCI_EXP_LNK_2_5GT; | |
997 | break; | |
998 | case PCIE_LINK_SPEED_5: | |
999 | *p = QEMU_PCI_EXP_LNK_5GT; | |
1000 | break; | |
1001 | case PCIE_LINK_SPEED_8: | |
1002 | *p = QEMU_PCI_EXP_LNK_8GT; | |
1003 | break; | |
1004 | case PCIE_LINK_SPEED_16: | |
1005 | *p = QEMU_PCI_EXP_LNK_16GT; | |
1006 | break; | |
1007 | default: | |
1008 | /* Unreachable */ | |
1009 | abort(); | |
1010 | } | |
1011 | } | |
1012 | ||
1013 | const PropertyInfo qdev_prop_pcie_link_speed = { | |
1014 | .name = "PCIELinkSpeed", | |
1015 | .description = "2_5/5/8/16", | |
1016 | .enum_table = &PCIELinkSpeed_lookup, | |
1017 | .get = get_prop_pcielinkspeed, | |
1018 | .set = set_prop_pcielinkspeed, | |
1019 | .set_default_value = qdev_propinfo_set_default_value_enum, | |
1020 | }; | |
1021 | ||
1022 | /* --- PCIELinkWidth 1/2/4/8/12/16/32 -- */ | |
1023 | ||
1024 | static void get_prop_pcielinkwidth(Object *obj, Visitor *v, const char *name, | |
1025 | void *opaque, Error **errp) | |
1026 | { | |
aa1859cc | 1027 | Property *prop = opaque; |
1e198715 | 1028 | PCIExpLinkWidth *p = object_field_prop_ptr(obj, prop); |
aa1859cc PMD |
1029 | int width; |
1030 | ||
1031 | switch (*p) { | |
1032 | case QEMU_PCI_EXP_LNK_X1: | |
1033 | width = PCIE_LINK_WIDTH_1; | |
1034 | break; | |
1035 | case QEMU_PCI_EXP_LNK_X2: | |
1036 | width = PCIE_LINK_WIDTH_2; | |
1037 | break; | |
1038 | case QEMU_PCI_EXP_LNK_X4: | |
1039 | width = PCIE_LINK_WIDTH_4; | |
1040 | break; | |
1041 | case QEMU_PCI_EXP_LNK_X8: | |
1042 | width = PCIE_LINK_WIDTH_8; | |
1043 | break; | |
1044 | case QEMU_PCI_EXP_LNK_X12: | |
1045 | width = PCIE_LINK_WIDTH_12; | |
1046 | break; | |
1047 | case QEMU_PCI_EXP_LNK_X16: | |
1048 | width = PCIE_LINK_WIDTH_16; | |
1049 | break; | |
1050 | case QEMU_PCI_EXP_LNK_X32: | |
1051 | width = PCIE_LINK_WIDTH_32; | |
1052 | break; | |
1053 | default: | |
1054 | /* Unreachable */ | |
1055 | abort(); | |
1056 | } | |
1057 | ||
991f0ac9 | 1058 | visit_type_enum(v, name, &width, prop->info->enum_table, errp); |
aa1859cc PMD |
1059 | } |
1060 | ||
1061 | static void set_prop_pcielinkwidth(Object *obj, Visitor *v, const char *name, | |
1062 | void *opaque, Error **errp) | |
1063 | { | |
aa1859cc | 1064 | Property *prop = opaque; |
1e198715 | 1065 | PCIExpLinkWidth *p = object_field_prop_ptr(obj, prop); |
aa1859cc PMD |
1066 | int width; |
1067 | ||
991f0ac9 | 1068 | if (!visit_type_enum(v, name, &width, prop->info->enum_table, |
aa1859cc PMD |
1069 | errp)) { |
1070 | return; | |
1071 | } | |
1072 | ||
1073 | switch (width) { | |
1074 | case PCIE_LINK_WIDTH_1: | |
1075 | *p = QEMU_PCI_EXP_LNK_X1; | |
1076 | break; | |
1077 | case PCIE_LINK_WIDTH_2: | |
1078 | *p = QEMU_PCI_EXP_LNK_X2; | |
1079 | break; | |
1080 | case PCIE_LINK_WIDTH_4: | |
1081 | *p = QEMU_PCI_EXP_LNK_X4; | |
1082 | break; | |
1083 | case PCIE_LINK_WIDTH_8: | |
1084 | *p = QEMU_PCI_EXP_LNK_X8; | |
1085 | break; | |
1086 | case PCIE_LINK_WIDTH_12: | |
1087 | *p = QEMU_PCI_EXP_LNK_X12; | |
1088 | break; | |
1089 | case PCIE_LINK_WIDTH_16: | |
1090 | *p = QEMU_PCI_EXP_LNK_X16; | |
1091 | break; | |
1092 | case PCIE_LINK_WIDTH_32: | |
1093 | *p = QEMU_PCI_EXP_LNK_X32; | |
1094 | break; | |
1095 | default: | |
1096 | /* Unreachable */ | |
1097 | abort(); | |
1098 | } | |
1099 | } | |
1100 | ||
1101 | const PropertyInfo qdev_prop_pcie_link_width = { | |
1102 | .name = "PCIELinkWidth", | |
1103 | .description = "1/2/4/8/12/16/32", | |
1104 | .enum_table = &PCIELinkWidth_lookup, | |
1105 | .get = get_prop_pcielinkwidth, | |
1106 | .set = set_prop_pcielinkwidth, | |
1107 | .set_default_value = qdev_propinfo_set_default_value_enum, | |
1108 | }; | |
38255eff EH |
1109 | |
1110 | /* --- UUID --- */ | |
1111 | ||
1112 | static void get_uuid(Object *obj, Visitor *v, const char *name, void *opaque, | |
1113 | Error **errp) | |
1114 | { | |
1115 | Property *prop = opaque; | |
1e198715 | 1116 | QemuUUID *uuid = object_field_prop_ptr(obj, prop); |
721da039 | 1117 | char buffer[UUID_STR_LEN]; |
38255eff EH |
1118 | char *p = buffer; |
1119 | ||
1120 | qemu_uuid_unparse(uuid, buffer); | |
1121 | ||
1122 | visit_type_str(v, name, &p, errp); | |
1123 | } | |
1124 | ||
1125 | #define UUID_VALUE_AUTO "auto" | |
1126 | ||
1127 | static void set_uuid(Object *obj, Visitor *v, const char *name, void *opaque, | |
1128 | Error **errp) | |
1129 | { | |
38255eff | 1130 | Property *prop = opaque; |
1e198715 | 1131 | QemuUUID *uuid = object_field_prop_ptr(obj, prop); |
38255eff EH |
1132 | char *str; |
1133 | ||
38255eff EH |
1134 | if (!visit_type_str(v, name, &str, errp)) { |
1135 | return; | |
1136 | } | |
1137 | ||
1138 | if (!strcmp(str, UUID_VALUE_AUTO)) { | |
1139 | qemu_uuid_generate(uuid); | |
1140 | } else if (qemu_uuid_parse(str, uuid) < 0) { | |
e68c2cb7 | 1141 | error_set_from_qdev_prop_error(errp, EINVAL, obj, name, str); |
38255eff EH |
1142 | } |
1143 | g_free(str); | |
1144 | } | |
1145 | ||
1146 | static void set_default_uuid_auto(ObjectProperty *op, const Property *prop) | |
1147 | { | |
1148 | object_property_set_default_str(op, UUID_VALUE_AUTO); | |
1149 | } | |
1150 | ||
1151 | const PropertyInfo qdev_prop_uuid = { | |
1152 | .name = "str", | |
1153 | .description = "UUID (aka GUID) or \"" UUID_VALUE_AUTO | |
1154 | "\" for random value (default)", | |
1155 | .get = get_uuid, | |
1156 | .set = set_uuid, | |
1157 | .set_default_value = set_default_uuid_auto, | |
1158 | }; | |
5de1aff2 PM |
1159 | |
1160 | /* --- s390 cpu entitlement policy --- */ | |
1161 | ||
1162 | QEMU_BUILD_BUG_ON(sizeof(CpuS390Entitlement) != sizeof(int)); | |
1163 | ||
1164 | const PropertyInfo qdev_prop_cpus390entitlement = { | |
1165 | .name = "CpuS390Entitlement", | |
1166 | .description = "low/medium (default)/high", | |
1167 | .enum_table = &CpuS390Entitlement_lookup, | |
1168 | .get = qdev_propinfo_get_enum, | |
1169 | .set = qdev_propinfo_set_enum, | |
1170 | .set_default_value = qdev_propinfo_set_default_value_enum, | |
1171 | }; |