diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt index b9b6eabd08..1366228b2a 100644 --- a/docs/devel/qapi-code-gen.txt +++ b/docs/devel/qapi-code-gen.txt @@ -559,7 +559,7 @@ following example objects: Usage: { 'command': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT, '*returns': TYPE-NAME, '*boxed': true, '*gen': false, '*success-response': false, - '*allow-oob': true } + '*allow-oob': true, '*allow-preconfig': true } Commands are defined by using a dictionary containing several members, where three members are most common. The 'command' member is a @@ -683,6 +683,15 @@ OOB command handlers must satisfy the following conditions: If in doubt, do not implement OOB execution support. +A command may use the optional 'allow-preconfig' key to permit its execution +at early runtime configuration stage (preconfig runstate). +If not specified then a command defaults to 'allow-preconfig': false. + +An example of declaring a command that is enabled during preconfig: + { 'command': 'qmp_capabilities', + 'data': { '*enable': [ 'QMPCapability' ] }, + 'allow-preconfig': true } + === Events === Usage: { 'event': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT, diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index ffb4652f71..b366bb48bd 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -23,6 +23,7 @@ typedef enum QmpCommandOptions QCO_NO_OPTIONS = 0x0, QCO_NO_SUCCESS_RESP = (1U << 0), QCO_ALLOW_OOB = (1U << 1), + QCO_ALLOW_PRECONFIG = (1U << 2), } QmpCommandOptions; typedef struct QmpCommand diff --git a/monitor.c b/monitor.c index 9e50418afe..922cfc041e 100644 --- a/monitor.c +++ b/monitor.c @@ -1179,8 +1179,7 @@ static void monitor_init_qmp_commands(void) qmp_init_marshal(&qmp_commands); qmp_register_command(&qmp_commands, "query-qmp-schema", - qmp_query_qmp_schema, - QCO_NO_OPTIONS); + qmp_query_qmp_schema, QCO_ALLOW_PRECONFIG); qmp_register_command(&qmp_commands, "device_add", qmp_device_add, QCO_NO_OPTIONS); qmp_register_command(&qmp_commands, "netdev_add", qmp_netdev_add, @@ -1190,7 +1189,7 @@ static void monitor_init_qmp_commands(void) QTAILQ_INIT(&qmp_cap_negotiation_commands); qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities", - qmp_marshal_qmp_capabilities, QCO_NO_OPTIONS); + qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG); } static bool qmp_cap_enabled(Monitor *mon, QMPCapability cap) diff --git a/qapi/introspect.json b/qapi/introspect.json index c7f67b7d78..80a0a3e656 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -262,13 +262,16 @@ # @allow-oob: whether the command allows out-of-band execution. # (Since: 2.12) # +# @allow-preconfig: command can be executed in preconfig runstate, +# default: false (Since 3.0) +# # TODO: @success-response (currently irrelevant, because it's QGA, not QMP) # # Since: 2.5 ## { 'struct': 'SchemaInfoCommand', 'data': { 'arg-type': 'str', 'ret-type': 'str', - 'allow-oob': 'bool' } } + 'allow-oob': 'bool', 'allow-preconfig': 'bool' } } ## # @SchemaInfoEvent: diff --git a/qapi/misc.json b/qapi/misc.json index 99bcaacd62..ae2bb27b83 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -37,7 +37,8 @@ # ## { 'command': 'qmp_capabilities', - 'data': { '*enable': [ 'QMPCapability' ] } } + 'data': { '*enable': [ 'QMPCapability' ] }, + 'allow-preconfig': true } ## # @QMPCapability: @@ -155,7 +156,8 @@ # Note: This example has been shortened as the real response is too long. # ## -{ 'command': 'query-commands', 'returns': ['CommandInfo'] } +{ 'command': 'query-commands', 'returns': ['CommandInfo'], + 'allow-preconfig': true } ## # @LostTickPolicy: @@ -2648,7 +2650,8 @@ # ## {'command': 'query-command-line-options', 'data': { '*option': 'str' }, - 'returns': ['CommandLineOptionInfo'] } + 'returns': ['CommandLineOptionInfo'], + 'allow-preconfig': true } ## # @X86CPURegister32: diff --git a/qapi/run-state.json b/qapi/run-state.json index d3c2a9113b..332e44897b 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -94,7 +94,8 @@ # "status": "running" } } # ## -{ 'command': 'query-status', 'returns': 'StatusInfo' } +{ 'command': 'query-status', 'returns': 'StatusInfo', + 'allow-preconfig': true } ## # @SHUTDOWN: diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 0c5da3a54d..3b0867c14f 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -193,13 +193,15 @@ out: return ret -def gen_register_command(name, success_response, allow_oob): +def gen_register_command(name, success_response, allow_oob, allow_preconfig): options = [] if not success_response: options += ['QCO_NO_SUCCESS_RESP'] if allow_oob: options += ['QCO_ALLOW_OOB'] + if allow_preconfig: + options += ['QCO_ALLOW_PRECONFIG'] if not options: options = ['QCO_NO_OPTIONS'] @@ -275,8 +277,8 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); c_prefix=c_name(self._prefix, protect=False))) genc.add(gen_registry(self._regy, self._prefix)) - def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed, allow_oob): + def visit_command(self, name, info, arg_type, ret_type, gen, + success_response, boxed, allow_oob, allow_preconfig): if not gen: return self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type)) @@ -285,7 +287,8 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); self._genc.add(gen_marshal_output(ret_type)) self._genh.add(gen_marshal_decl(name)) self._genc.add(gen_marshal(name, arg_type, boxed, ret_type)) - self._regy += gen_register_command(name, success_response, allow_oob) + self._regy += gen_register_command(name, success_response, allow_oob, + allow_preconfig) def gen_commands(schema, output_dir, prefix): diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index a032cec375..e82990f0f2 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -872,7 +872,8 @@ def check_keys(expr_elem, meta, required, optional=[]): raise QAPISemError(info, "'%s' of %s '%s' should only use false value" % (key, meta, name)) - if (key == 'boxed' or key == 'allow-oob') and value is not True: + if (key == 'boxed' or key == 'allow-oob' or + key == 'allow-preconfig') and value is not True: raise QAPISemError(info, "'%s' of %s '%s' should only use true value" % (key, meta, name)) @@ -922,7 +923,7 @@ def check_exprs(exprs): meta = 'command' check_keys(expr_elem, 'command', [], ['data', 'returns', 'gen', 'success-response', - 'boxed', 'allow-oob']) + 'boxed', 'allow-oob', 'allow-preconfig']) elif 'event' in expr: meta = 'event' check_keys(expr_elem, 'event', [], ['data', 'boxed']) @@ -1044,8 +1045,8 @@ class QAPISchemaVisitor(object): def visit_alternate_type(self, name, info, variants): pass - def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed, allow_oob): + def visit_command(self, name, info, arg_type, ret_type, gen, + success_response, boxed, allow_oob, allow_preconfig): pass def visit_event(self, name, info, arg_type, boxed): @@ -1422,7 +1423,7 @@ class QAPISchemaAlternateType(QAPISchemaType): class QAPISchemaCommand(QAPISchemaEntity): def __init__(self, name, info, doc, arg_type, ret_type, - gen, success_response, boxed, allow_oob): + gen, success_response, boxed, allow_oob, allow_preconfig): QAPISchemaEntity.__init__(self, name, info, doc) assert not arg_type or isinstance(arg_type, str) assert not ret_type or isinstance(ret_type, str) @@ -1434,6 +1435,7 @@ class QAPISchemaCommand(QAPISchemaEntity): self.success_response = success_response self.boxed = boxed self.allow_oob = allow_oob + self.allow_preconfig = allow_preconfig def check(self, schema): if self._arg_type_name: @@ -1458,7 +1460,8 @@ class QAPISchemaCommand(QAPISchemaEntity): visitor.visit_command(self.name, self.info, self.arg_type, self.ret_type, self.gen, self.success_response, - self.boxed, self.allow_oob) + self.boxed, self.allow_oob, + self.allow_preconfig) class QAPISchemaEvent(QAPISchemaEntity): @@ -1678,6 +1681,7 @@ class QAPISchema(object): success_response = expr.get('success-response', True) boxed = expr.get('boxed', False) allow_oob = expr.get('allow-oob', False) + allow_preconfig = expr.get('allow-preconfig', False) if isinstance(data, OrderedDict): data = self._make_implicit_object_type( name, info, doc, 'arg', self._make_members(data, info)) @@ -1686,7 +1690,7 @@ class QAPISchema(object): rets = self._make_array_type(rets[0], info) self._def_entity(QAPISchemaCommand(name, info, doc, data, rets, gen, success_response, - boxed, allow_oob)) + boxed, allow_oob, allow_preconfig)) def _def_event(self, expr, info, doc): name = expr['event'] diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py index 9b312b2c51..b5630844f9 100644 --- a/scripts/qapi/doc.py +++ b/scripts/qapi/doc.py @@ -226,8 +226,8 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): name=doc.symbol, body=texi_entity(doc, 'Members'))) - def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed, allow_oob): + def visit_command(self, name, info, arg_type, ret_type, gen, + success_response, boxed, allow_oob, allow_preconfig): doc = self.cur_doc if boxed: body = texi_body(doc) diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index f9e67e8227..5b6c72c7b2 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -171,14 +171,15 @@ const QLitObject %(c_name)s = %(c_string)s; {'members': [{'type': self._use_type(m.type)} for m in variants.variants]}) - def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed, allow_oob): + def visit_command(self, name, info, arg_type, ret_type, gen, + success_response, boxed, allow_oob, allow_preconfig): arg_type = arg_type or self._schema.the_empty_object_type ret_type = ret_type or self._schema.the_empty_object_type self._gen_qlit(name, 'command', {'arg-type': self._use_type(arg_type), 'ret-type': self._use_type(ret_type), - 'allow-oob': allow_oob}) + 'allow-oob': allow_oob, + 'allow-preconfig': allow_preconfig}) def visit_event(self, name, info, arg_type, boxed): arg_type = arg_type or self._schema.the_empty_object_type diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index c1a144ba29..89b92eddd4 100644 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -41,8 +41,8 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): print('alternate %s' % name) self._print_variants(variants) - def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed, allow_oob): + def visit_command(self, name, info, arg_type, ret_type, gen, + success_response, boxed, allow_oob, allow_preconfig): print('command %s %s -> %s' % \ (name, arg_type and arg_type.name, ret_type and ret_type.name)) print(' gen=%s success_response=%s boxed=%s oob=%s' % \