Skip to content

Commit 5dacda5

Browse files
committed
vl: Enable JSON syntax for -device
Like we already do for -object, introduce support for JSON syntax in -device, which can be kept stable in the long term and guarantees that a single code path with identical behaviour is used for both QMP and the command line. Compared to the QemuOpts based code, the parser contains less surprises and has support for non-scalar options (lists and structs). Switching management tools to JSON means that we can more easily change the "human" CLI syntax from QemuOpts to the keyval parser later. In the QAPI schema, a feature flag is added to the device-add command to allow management tools to detect support for this. Signed-off-by: Kevin Wolf <[email protected]> Reviewed-by: Eric Blake <[email protected]> Message-Id: <[email protected]> Reviewed-by: Michael S. Tsirkin <[email protected]> Tested-by: Peter Krempa <[email protected]> Signed-off-by: Kevin Wolf <[email protected]>
1 parent f3558b1 commit 5dacda5

File tree

2 files changed

+67
-11
lines changed

2 files changed

+67
-11
lines changed

qapi/qdev.json

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,23 @@
3232
##
3333
# @device_add:
3434
#
35+
# Add a device.
36+
#
3537
# @driver: the name of the new device's driver
3638
#
3739
# @bus: the device's parent bus (device tree path)
3840
#
3941
# @id: the device's ID, must be unique
4042
#
41-
# Additional arguments depend on the type.
42-
#
43-
# Add a device.
43+
# Features:
44+
# @json-cli: If present, the "-device" command line option supports JSON
45+
# syntax with a structure identical to the arguments of this
46+
# command.
4447
#
4548
# Notes:
49+
#
50+
# Additional arguments depend on the type.
51+
#
4652
# 1. For detailed information about this command, please refer to the
4753
# 'docs/qdev-device-use.txt' file.
4854
#
@@ -67,7 +73,8 @@
6773
##
6874
{ 'command': 'device_add',
6975
'data': {'driver': 'str', '*bus': 'str', '*id': 'str'},
70-
'gen': false } # so we can get the additional arguments
76+
'gen': false, # so we can get the additional arguments
77+
'features': ['json-cli'] }
7178

7279
##
7380
# @device_del:

softmmu/vl.c

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -144,13 +144,20 @@ typedef struct ObjectOption {
144144
QTAILQ_ENTRY(ObjectOption) next;
145145
} ObjectOption;
146146

147+
typedef struct DeviceOption {
148+
QDict *opts;
149+
Location loc;
150+
QTAILQ_ENTRY(DeviceOption) next;
151+
} DeviceOption;
152+
147153
static const char *cpu_option;
148154
static const char *mem_path;
149155
static const char *incoming;
150156
static const char *loadvm;
151157
static const char *accelerators;
152158
static QDict *machine_opts_dict;
153159
static QTAILQ_HEAD(, ObjectOption) object_opts = QTAILQ_HEAD_INITIALIZER(object_opts);
160+
static QTAILQ_HEAD(, DeviceOption) device_opts = QTAILQ_HEAD_INITIALIZER(device_opts);
154161
static ram_addr_t maxram_size;
155162
static uint64_t ram_slots;
156163
static int display_remote;
@@ -494,21 +501,39 @@ const char *qemu_get_vm_name(void)
494501
return qemu_name;
495502
}
496503

497-
static int default_driver_check(void *opaque, QemuOpts *opts, Error **errp)
504+
static void default_driver_disable(const char *driver)
498505
{
499-
const char *driver = qemu_opt_get(opts, "driver");
500506
int i;
501507

502-
if (!driver)
503-
return 0;
508+
if (!driver) {
509+
return;
510+
}
511+
504512
for (i = 0; i < ARRAY_SIZE(default_list); i++) {
505513
if (strcmp(default_list[i].driver, driver) != 0)
506514
continue;
507515
*(default_list[i].flag) = 0;
508516
}
517+
}
518+
519+
static int default_driver_check(void *opaque, QemuOpts *opts, Error **errp)
520+
{
521+
const char *driver = qemu_opt_get(opts, "driver");
522+
523+
default_driver_disable(driver);
509524
return 0;
510525
}
511526

527+
static void default_driver_check_json(void)
528+
{
529+
DeviceOption *opt;
530+
531+
QTAILQ_FOREACH(opt, &device_opts, next) {
532+
const char *driver = qdict_get_try_str(opt->opts, "driver");
533+
default_driver_disable(driver);
534+
}
535+
}
536+
512537
static int parse_name(void *opaque, QemuOpts *opts, Error **errp)
513538
{
514539
const char *proc_name;
@@ -1311,6 +1336,7 @@ static void qemu_disable_default_devices(void)
13111336
{
13121337
MachineClass *machine_class = MACHINE_GET_CLASS(current_machine);
13131338

1339+
default_driver_check_json();
13141340
qemu_opts_foreach(qemu_find_opts("device"),
13151341
default_driver_check, NULL, NULL);
13161342
qemu_opts_foreach(qemu_find_opts("global"),
@@ -2637,6 +2663,8 @@ static void qemu_init_board(void)
26372663

26382664
static void qemu_create_cli_devices(void)
26392665
{
2666+
DeviceOption *opt;
2667+
26402668
soundhw_init();
26412669

26422670
qemu_opts_foreach(qemu_find_opts("fw_cfg"),
@@ -2652,6 +2680,18 @@ static void qemu_create_cli_devices(void)
26522680
rom_set_order_override(FW_CFG_ORDER_OVERRIDE_DEVICE);
26532681
qemu_opts_foreach(qemu_find_opts("device"),
26542682
device_init_func, NULL, &error_fatal);
2683+
QTAILQ_FOREACH(opt, &device_opts, next) {
2684+
loc_push_restore(&opt->loc);
2685+
/*
2686+
* TODO Eventually we should call qmp_device_add() here to make sure it
2687+
* behaves the same, but QMP still has to accept incorrectly typed
2688+
* options until libvirt is fixed and we want to be strict on the CLI
2689+
* from the start, so call qdev_device_add_from_qdict() directly for
2690+
* now.
2691+
*/
2692+
qdev_device_add_from_qdict(opt->opts, true, &error_fatal);
2693+
loc_pop(&opt->loc);
2694+
}
26552695
rom_reset_order_override();
26562696
}
26572697

@@ -3352,9 +3392,18 @@ void qemu_init(int argc, char **argv, char **envp)
33523392
add_device_config(DEV_USB, optarg);
33533393
break;
33543394
case QEMU_OPTION_device:
3355-
if (!qemu_opts_parse_noisily(qemu_find_opts("device"),
3356-
optarg, true)) {
3357-
exit(1);
3395+
if (optarg[0] == '{') {
3396+
QObject *obj = qobject_from_json(optarg, &error_fatal);
3397+
DeviceOption *opt = g_new0(DeviceOption, 1);
3398+
opt->opts = qobject_to(QDict, obj);
3399+
loc_save(&opt->loc);
3400+
assert(opt->opts != NULL);
3401+
QTAILQ_INSERT_TAIL(&device_opts, opt, next);
3402+
} else {
3403+
if (!qemu_opts_parse_noisily(qemu_find_opts("device"),
3404+
optarg, true)) {
3405+
exit(1);
3406+
}
33583407
}
33593408
break;
33603409
case QEMU_OPTION_smp:

0 commit comments

Comments
 (0)