2006-05-04 00:02:44 +02:00
|
|
|
/*
|
|
|
|
* ACPI implementation
|
2007-09-16 23:08:06 +02:00
|
|
|
*
|
2006-05-04 00:02:44 +02:00
|
|
|
* Copyright (c) 2006 Fabrice Bellard
|
2007-09-16 23:08:06 +02:00
|
|
|
*
|
2006-05-04 00:02:44 +02:00
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
2020-10-23 14:44:24 +02:00
|
|
|
* License version 2.1 as published by the Free Software Foundation.
|
2006-05-04 00:02:44 +02:00
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
2009-07-16 22:47:01 +02:00
|
|
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
2012-01-13 17:44:23 +01:00
|
|
|
*
|
|
|
|
* Contributions after 2012-01-13 are licensed under the terms of the
|
|
|
|
* GNU GPL, version 2 or (at your option) any later version.
|
2006-05-04 00:02:44 +02:00
|
|
|
*/
|
2018-02-01 12:18:31 +01:00
|
|
|
|
2016-01-26 19:17:03 +01:00
|
|
|
#include "qemu/osdep.h"
|
2019-08-12 07:23:42 +02:00
|
|
|
#include "hw/irq.h"
|
2013-02-05 17:06:20 +01:00
|
|
|
#include "hw/acpi/acpi.h"
|
2015-04-29 15:20:15 +02:00
|
|
|
#include "hw/nvram/fw_cfg.h"
|
2013-03-21 00:23:17 +01:00
|
|
|
#include "qemu/config-file.h"
|
2018-02-01 12:18:31 +01:00
|
|
|
#include "qapi/error.h"
|
2013-03-21 00:23:17 +01:00
|
|
|
#include "qapi/opts-visitor.h"
|
2018-02-11 10:36:01 +01:00
|
|
|
#include "qapi/qapi-events-run-state.h"
|
2020-09-13 21:53:47 +02:00
|
|
|
#include "qapi/qapi-visit-acpi.h"
|
2017-09-11 21:52:50 +02:00
|
|
|
#include "qemu/error-report.h"
|
2019-05-23 16:35:07 +02:00
|
|
|
#include "qemu/module.h"
|
2018-02-01 12:18:46 +01:00
|
|
|
#include "qemu/option.h"
|
2019-08-12 07:23:59 +02:00
|
|
|
#include "sysemu/runstate.h"
|
2023-09-08 10:42:33 +02:00
|
|
|
#include "trace.h"
|
2006-05-04 00:02:44 +02:00
|
|
|
|
2011-05-12 16:44:17 +02:00
|
|
|
struct acpi_table_header {
|
|
|
|
uint16_t _length; /* our length, not actual part of the hdr */
|
2013-03-21 00:23:18 +01:00
|
|
|
/* allows easier parsing for fw_cfg clients */
|
2019-01-03 09:56:36 +01:00
|
|
|
char sig[4]
|
|
|
|
QEMU_NONSTRING; /* ACPI signature (4 ASCII characters) */
|
2009-02-27 21:12:36 +01:00
|
|
|
uint32_t length; /* Length of table, in bytes, including header */
|
|
|
|
uint8_t revision; /* ACPI Specification minor version # */
|
|
|
|
uint8_t checksum; /* To make sum of entire table == 0 */
|
2019-01-03 09:56:36 +01:00
|
|
|
char oem_id[6]
|
|
|
|
QEMU_NONSTRING; /* OEM identification */
|
|
|
|
char oem_table_id[8]
|
|
|
|
QEMU_NONSTRING; /* OEM table identification */
|
2009-02-27 21:12:36 +01:00
|
|
|
uint32_t oem_revision; /* OEM revision number */
|
2019-01-03 09:56:36 +01:00
|
|
|
char asl_compiler_id[4]
|
|
|
|
QEMU_NONSTRING; /* ASL compiler vendor ID */
|
2009-02-27 21:12:36 +01:00
|
|
|
uint32_t asl_compiler_revision; /* ASL compiler revision number */
|
2011-08-31 12:38:01 +02:00
|
|
|
} QEMU_PACKED;
|
2009-02-27 21:12:36 +01:00
|
|
|
|
2011-05-12 16:44:17 +02:00
|
|
|
#define ACPI_TABLE_HDR_SIZE sizeof(struct acpi_table_header)
|
|
|
|
#define ACPI_TABLE_PFX_SIZE sizeof(uint16_t) /* size of the extra prefix */
|
|
|
|
|
2013-03-21 00:23:18 +01:00
|
|
|
static const char unsigned dfl_hdr[ACPI_TABLE_HDR_SIZE - ACPI_TABLE_PFX_SIZE] =
|
2011-05-12 16:44:17 +02:00
|
|
|
"QEMU\0\0\0\0\1\0" /* sig (4), len(4), revno (1), csum (1) */
|
|
|
|
"QEMUQEQEMUQEMU\1\0\0\0" /* OEM id (6), table (8), revno (4) */
|
|
|
|
"QEMU\1\0\0\0" /* ASL compiler ID (4), version (4) */
|
|
|
|
;
|
|
|
|
|
2013-03-21 00:23:14 +01:00
|
|
|
char unsigned *acpi_tables;
|
2009-02-27 21:12:36 +01:00
|
|
|
size_t acpi_tables_len;
|
|
|
|
|
2013-03-21 00:23:17 +01:00
|
|
|
static QemuOptsList qemu_acpi_opts = {
|
|
|
|
.name = "acpi",
|
|
|
|
.implied_opt_name = "data",
|
|
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_acpi_opts.head),
|
|
|
|
.desc = { { 0 } } /* validated with OptsVisitor */
|
|
|
|
};
|
|
|
|
|
|
|
|
static void acpi_register_config(void)
|
|
|
|
{
|
|
|
|
qemu_add_opts(&qemu_acpi_opts);
|
|
|
|
}
|
|
|
|
|
2016-02-16 21:59:07 +01:00
|
|
|
opts_init(acpi_register_config);
|
2013-03-21 00:23:17 +01:00
|
|
|
|
2009-02-27 21:12:36 +01:00
|
|
|
static int acpi_checksum(const uint8_t *data, int len)
|
|
|
|
{
|
|
|
|
int sum, i;
|
|
|
|
sum = 0;
|
2011-05-12 16:44:17 +02:00
|
|
|
for (i = 0; i < len; i++) {
|
2009-02-27 21:12:36 +01:00
|
|
|
sum += data[i];
|
2011-05-12 16:44:17 +02:00
|
|
|
}
|
2009-02-27 21:12:36 +01:00
|
|
|
return (-sum) & 0xff;
|
|
|
|
}
|
|
|
|
|
2013-03-21 00:23:18 +01:00
|
|
|
|
|
|
|
/* Install a copy of the ACPI table specified in @blob.
|
|
|
|
*
|
|
|
|
* If @has_header is set, @blob starts with the System Description Table Header
|
|
|
|
* structure. Otherwise, "dfl_hdr" is prepended. In any case, each header field
|
|
|
|
* is optionally overwritten from @hdrs.
|
|
|
|
*
|
|
|
|
* It is valid to call this function with
|
|
|
|
* (@blob == NULL && bloblen == 0 && !has_header).
|
|
|
|
*
|
|
|
|
* @hdrs->file and @hdrs->data are ignored.
|
|
|
|
*
|
|
|
|
* SIZE_MAX is considered "infinity" in this function.
|
|
|
|
*
|
|
|
|
* The number of tables that can be installed is not limited, but the 16-bit
|
|
|
|
* counter at the beginning of "acpi_tables" wraps around after UINT16_MAX.
|
|
|
|
*/
|
|
|
|
static void acpi_table_install(const char unsigned *blob, size_t bloblen,
|
|
|
|
bool has_header,
|
|
|
|
const struct AcpiTableOptions *hdrs,
|
|
|
|
Error **errp)
|
|
|
|
{
|
|
|
|
size_t body_start;
|
|
|
|
const char unsigned *hdr_src;
|
|
|
|
size_t body_size, acpi_payload_size;
|
|
|
|
struct acpi_table_header *ext_hdr;
|
|
|
|
unsigned changed_fields;
|
|
|
|
|
|
|
|
/* Calculate where the ACPI table body starts within the blob, plus where
|
|
|
|
* to copy the ACPI table header from.
|
|
|
|
*/
|
|
|
|
if (has_header) {
|
|
|
|
/* _length | ACPI header in blob | blob body
|
|
|
|
* ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^
|
|
|
|
* ACPI_TABLE_PFX_SIZE sizeof dfl_hdr body_size
|
|
|
|
* == body_start
|
|
|
|
*
|
|
|
|
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
* acpi_payload_size == bloblen
|
|
|
|
*/
|
|
|
|
body_start = sizeof dfl_hdr;
|
|
|
|
|
|
|
|
if (bloblen < body_start) {
|
|
|
|
error_setg(errp, "ACPI table claiming to have header is too "
|
|
|
|
"short, available: %zu, expected: %zu", bloblen,
|
|
|
|
body_start);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
hdr_src = blob;
|
|
|
|
} else {
|
|
|
|
/* _length | ACPI header in template | blob body
|
|
|
|
* ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^
|
|
|
|
* ACPI_TABLE_PFX_SIZE sizeof dfl_hdr body_size
|
|
|
|
* == bloblen
|
|
|
|
*
|
|
|
|
* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
* acpi_payload_size
|
|
|
|
*/
|
|
|
|
body_start = 0;
|
|
|
|
hdr_src = dfl_hdr;
|
|
|
|
}
|
|
|
|
body_size = bloblen - body_start;
|
|
|
|
acpi_payload_size = sizeof dfl_hdr + body_size;
|
|
|
|
|
|
|
|
if (acpi_payload_size > UINT16_MAX) {
|
|
|
|
error_setg(errp, "ACPI table too big, requested: %zu, max: %u",
|
|
|
|
acpi_payload_size, (unsigned)UINT16_MAX);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We won't fail from here on. Initialize / extend the globals. */
|
|
|
|
if (acpi_tables == NULL) {
|
|
|
|
acpi_tables_len = sizeof(uint16_t);
|
|
|
|
acpi_tables = g_malloc0(acpi_tables_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
acpi_tables = g_realloc(acpi_tables, acpi_tables_len +
|
|
|
|
ACPI_TABLE_PFX_SIZE +
|
|
|
|
sizeof dfl_hdr + body_size);
|
|
|
|
|
|
|
|
ext_hdr = (struct acpi_table_header *)(acpi_tables + acpi_tables_len);
|
|
|
|
acpi_tables_len += ACPI_TABLE_PFX_SIZE;
|
|
|
|
|
|
|
|
memcpy(acpi_tables + acpi_tables_len, hdr_src, sizeof dfl_hdr);
|
|
|
|
acpi_tables_len += sizeof dfl_hdr;
|
|
|
|
|
|
|
|
if (blob != NULL) {
|
|
|
|
memcpy(acpi_tables + acpi_tables_len, blob + body_start, body_size);
|
|
|
|
acpi_tables_len += body_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* increase number of tables */
|
2013-11-05 17:38:31 +01:00
|
|
|
stw_le_p(acpi_tables, lduw_le_p(acpi_tables) + 1u);
|
2013-03-21 00:23:18 +01:00
|
|
|
|
|
|
|
/* Update the header fields. The strings need not be NUL-terminated. */
|
|
|
|
changed_fields = 0;
|
|
|
|
ext_hdr->_length = cpu_to_le16(acpi_payload_size);
|
|
|
|
|
2022-11-04 17:06:48 +01:00
|
|
|
if (hdrs->sig) {
|
2013-03-21 00:23:18 +01:00
|
|
|
strncpy(ext_hdr->sig, hdrs->sig, sizeof ext_hdr->sig);
|
|
|
|
++changed_fields;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (has_header && le32_to_cpu(ext_hdr->length) != acpi_payload_size) {
|
2017-09-11 21:52:53 +02:00
|
|
|
warn_report("ACPI table has wrong length, header says "
|
|
|
|
"%" PRIu32 ", actual size %zu bytes",
|
|
|
|
le32_to_cpu(ext_hdr->length), acpi_payload_size);
|
2013-03-21 00:23:18 +01:00
|
|
|
}
|
|
|
|
ext_hdr->length = cpu_to_le32(acpi_payload_size);
|
|
|
|
|
|
|
|
if (hdrs->has_rev) {
|
|
|
|
ext_hdr->revision = hdrs->rev;
|
|
|
|
++changed_fields;
|
|
|
|
}
|
|
|
|
|
|
|
|
ext_hdr->checksum = 0;
|
|
|
|
|
2022-11-04 17:06:48 +01:00
|
|
|
if (hdrs->oem_id) {
|
2013-03-21 00:23:18 +01:00
|
|
|
strncpy(ext_hdr->oem_id, hdrs->oem_id, sizeof ext_hdr->oem_id);
|
|
|
|
++changed_fields;
|
|
|
|
}
|
2022-11-04 17:06:48 +01:00
|
|
|
if (hdrs->oem_table_id) {
|
2013-03-21 00:23:18 +01:00
|
|
|
strncpy(ext_hdr->oem_table_id, hdrs->oem_table_id,
|
|
|
|
sizeof ext_hdr->oem_table_id);
|
|
|
|
++changed_fields;
|
|
|
|
}
|
|
|
|
if (hdrs->has_oem_rev) {
|
|
|
|
ext_hdr->oem_revision = cpu_to_le32(hdrs->oem_rev);
|
|
|
|
++changed_fields;
|
|
|
|
}
|
2022-11-04 17:06:48 +01:00
|
|
|
if (hdrs->asl_compiler_id) {
|
2013-03-21 00:23:18 +01:00
|
|
|
strncpy(ext_hdr->asl_compiler_id, hdrs->asl_compiler_id,
|
|
|
|
sizeof ext_hdr->asl_compiler_id);
|
|
|
|
++changed_fields;
|
|
|
|
}
|
|
|
|
if (hdrs->has_asl_compiler_rev) {
|
|
|
|
ext_hdr->asl_compiler_revision = cpu_to_le32(hdrs->asl_compiler_rev);
|
|
|
|
++changed_fields;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!has_header && changed_fields == 0) {
|
2017-09-11 21:52:50 +02:00
|
|
|
warn_report("ACPI table: no headers are specified");
|
2013-03-21 00:23:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* recalculate checksum */
|
|
|
|
ext_hdr->checksum = acpi_checksum((const char unsigned *)ext_hdr +
|
|
|
|
ACPI_TABLE_PFX_SIZE, acpi_payload_size);
|
|
|
|
}
|
|
|
|
|
2013-03-21 00:23:19 +01:00
|
|
|
void acpi_table_add(const QemuOpts *opts, Error **errp)
|
2009-02-27 21:12:36 +01:00
|
|
|
{
|
2013-03-21 00:23:17 +01:00
|
|
|
AcpiTableOptions *hdrs = NULL;
|
|
|
|
char **pathnames = NULL;
|
|
|
|
char **cur;
|
2013-03-21 00:23:18 +01:00
|
|
|
size_t bloblen = 0;
|
|
|
|
char unsigned *blob = NULL;
|
2009-02-27 21:12:36 +01:00
|
|
|
|
2013-03-21 00:23:17 +01:00
|
|
|
{
|
2016-06-09 18:48:36 +02:00
|
|
|
Visitor *v;
|
2013-03-21 00:23:17 +01:00
|
|
|
|
2016-06-09 18:48:36 +02:00
|
|
|
v = opts_visitor_new(opts);
|
2020-07-07 18:06:07 +02:00
|
|
|
visit_type_AcpiTableOptions(v, NULL, &hdrs, errp);
|
2016-06-09 18:48:36 +02:00
|
|
|
visit_free(v);
|
2013-03-21 00:23:17 +01:00
|
|
|
}
|
|
|
|
|
2020-07-07 18:06:07 +02:00
|
|
|
if (!hdrs) {
|
2013-03-21 00:23:17 +01:00
|
|
|
goto out;
|
|
|
|
}
|
2022-11-04 17:06:48 +01:00
|
|
|
if (!hdrs->file == !hdrs->data) {
|
error: Avoid unnecessary error_propagate() after error_setg()
Replace
error_setg(&err, ...);
error_propagate(errp, err);
by
error_setg(errp, ...);
Related pattern:
if (...) {
error_setg(&err, ...);
goto out;
}
...
out:
error_propagate(errp, err);
return;
When all paths to label out are that way, replace by
if (...) {
error_setg(errp, ...);
return;
}
and delete the label along with the error_propagate().
When we have at most one other path that actually needs to propagate,
and maybe one at the end that where propagation is unnecessary, e.g.
foo(..., &err);
if (err) {
goto out;
}
...
bar(..., &err);
out:
error_propagate(errp, err);
return;
move the error_propagate() to where it's needed, like
if (...) {
foo(..., &err);
error_propagate(errp, err);
return;
}
...
bar(..., errp);
return;
and transform the error_setg() as above.
In some places, the transformation results in obviously unnecessary
error_propagate(). The next few commits will eliminate them.
Bonus: the elimination of gotos will make later patches in this series
easier to review.
Candidates for conversion tracked down with this Coccinelle script:
@@
identifier err, errp;
expression list args;
@@
- error_setg(&err, args);
+ error_setg(errp, args);
... when != err
error_propagate(errp, err);
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20200707160613.848843-34-armbru@redhat.com>
2020-07-07 18:06:01 +02:00
|
|
|
error_setg(errp, "'-acpitable' requires one of 'data' or 'file'");
|
2013-03-21 00:23:17 +01:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2022-11-04 17:06:48 +01:00
|
|
|
pathnames = g_strsplit(hdrs->file ?: hdrs->data, ":", 0);
|
2013-03-21 00:23:17 +01:00
|
|
|
if (pathnames == NULL || pathnames[0] == NULL) {
|
error: Avoid unnecessary error_propagate() after error_setg()
Replace
error_setg(&err, ...);
error_propagate(errp, err);
by
error_setg(errp, ...);
Related pattern:
if (...) {
error_setg(&err, ...);
goto out;
}
...
out:
error_propagate(errp, err);
return;
When all paths to label out are that way, replace by
if (...) {
error_setg(errp, ...);
return;
}
and delete the label along with the error_propagate().
When we have at most one other path that actually needs to propagate,
and maybe one at the end that where propagation is unnecessary, e.g.
foo(..., &err);
if (err) {
goto out;
}
...
bar(..., &err);
out:
error_propagate(errp, err);
return;
move the error_propagate() to where it's needed, like
if (...) {
foo(..., &err);
error_propagate(errp, err);
return;
}
...
bar(..., errp);
return;
and transform the error_setg() as above.
In some places, the transformation results in obviously unnecessary
error_propagate(). The next few commits will eliminate them.
Bonus: the elimination of gotos will make later patches in this series
easier to review.
Candidates for conversion tracked down with this Coccinelle script:
@@
identifier err, errp;
expression list args;
@@
- error_setg(&err, args);
+ error_setg(errp, args);
... when != err
error_propagate(errp, err);
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20200707160613.848843-34-armbru@redhat.com>
2020-07-07 18:06:01 +02:00
|
|
|
error_setg(errp, "'-acpitable' requires at least one pathname");
|
2013-03-21 00:23:15 +01:00
|
|
|
goto out;
|
2011-05-12 16:44:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* now read in the data files, reallocating buffer as needed */
|
2013-03-21 00:23:17 +01:00
|
|
|
for (cur = pathnames; *cur; ++cur) {
|
|
|
|
int fd = open(*cur, O_RDONLY | O_BINARY);
|
2011-05-12 16:44:17 +02:00
|
|
|
|
|
|
|
if (fd < 0) {
|
error: Avoid unnecessary error_propagate() after error_setg()
Replace
error_setg(&err, ...);
error_propagate(errp, err);
by
error_setg(errp, ...);
Related pattern:
if (...) {
error_setg(&err, ...);
goto out;
}
...
out:
error_propagate(errp, err);
return;
When all paths to label out are that way, replace by
if (...) {
error_setg(errp, ...);
return;
}
and delete the label along with the error_propagate().
When we have at most one other path that actually needs to propagate,
and maybe one at the end that where propagation is unnecessary, e.g.
foo(..., &err);
if (err) {
goto out;
}
...
bar(..., &err);
out:
error_propagate(errp, err);
return;
move the error_propagate() to where it's needed, like
if (...) {
foo(..., &err);
error_propagate(errp, err);
return;
}
...
bar(..., errp);
return;
and transform the error_setg() as above.
In some places, the transformation results in obviously unnecessary
error_propagate(). The next few commits will eliminate them.
Bonus: the elimination of gotos will make later patches in this series
easier to review.
Candidates for conversion tracked down with this Coccinelle script:
@@
identifier err, errp;
expression list args;
@@
- error_setg(&err, args);
+ error_setg(errp, args);
... when != err
error_propagate(errp, err);
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20200707160613.848843-34-armbru@redhat.com>
2020-07-07 18:06:01 +02:00
|
|
|
error_setg(errp, "can't open file %s: %s", *cur, strerror(errno));
|
2013-03-21 00:23:15 +01:00
|
|
|
goto out;
|
2011-05-12 16:44:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for (;;) {
|
2013-03-21 00:23:14 +01:00
|
|
|
char unsigned data[8192];
|
2013-03-21 00:23:18 +01:00
|
|
|
ssize_t r;
|
|
|
|
|
|
|
|
r = read(fd, data, sizeof data);
|
2011-05-12 16:44:17 +02:00
|
|
|
if (r == 0) {
|
|
|
|
break;
|
|
|
|
} else if (r > 0) {
|
2013-03-21 00:23:18 +01:00
|
|
|
blob = g_realloc(blob, bloblen + r);
|
|
|
|
memcpy(blob + bloblen, data, r);
|
|
|
|
bloblen += r;
|
2011-05-12 16:44:17 +02:00
|
|
|
} else if (errno != EINTR) {
|
error: Avoid unnecessary error_propagate() after error_setg()
Replace
error_setg(&err, ...);
error_propagate(errp, err);
by
error_setg(errp, ...);
Related pattern:
if (...) {
error_setg(&err, ...);
goto out;
}
...
out:
error_propagate(errp, err);
return;
When all paths to label out are that way, replace by
if (...) {
error_setg(errp, ...);
return;
}
and delete the label along with the error_propagate().
When we have at most one other path that actually needs to propagate,
and maybe one at the end that where propagation is unnecessary, e.g.
foo(..., &err);
if (err) {
goto out;
}
...
bar(..., &err);
out:
error_propagate(errp, err);
return;
move the error_propagate() to where it's needed, like
if (...) {
foo(..., &err);
error_propagate(errp, err);
return;
}
...
bar(..., errp);
return;
and transform the error_setg() as above.
In some places, the transformation results in obviously unnecessary
error_propagate(). The next few commits will eliminate them.
Bonus: the elimination of gotos will make later patches in this series
easier to review.
Candidates for conversion tracked down with this Coccinelle script:
@@
identifier err, errp;
expression list args;
@@
- error_setg(&err, args);
+ error_setg(errp, args);
... when != err
error_propagate(errp, err);
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20200707160613.848843-34-armbru@redhat.com>
2020-07-07 18:06:01 +02:00
|
|
|
error_setg(errp, "can't read file %s: %s", *cur,
|
|
|
|
strerror(errno));
|
2011-05-12 16:44:17 +02:00
|
|
|
close(fd);
|
2013-03-21 00:23:15 +01:00
|
|
|
goto out;
|
2011-05-12 16:44:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
}
|
|
|
|
|
2022-11-04 17:06:48 +01:00
|
|
|
acpi_table_install(blob, bloblen, !!hdrs->file, hdrs, errp);
|
2011-05-12 16:44:17 +02:00
|
|
|
|
2013-03-21 00:23:15 +01:00
|
|
|
out:
|
2013-03-21 00:23:18 +01:00
|
|
|
g_free(blob);
|
2013-03-21 00:23:17 +01:00
|
|
|
g_strfreev(pathnames);
|
2016-02-23 22:14:33 +01:00
|
|
|
qapi_free_AcpiTableOptions(hdrs);
|
2009-02-27 21:12:36 +01:00
|
|
|
}
|
2011-03-25 11:54:38 +01:00
|
|
|
|
2013-10-07 13:41:40 +02:00
|
|
|
unsigned acpi_table_len(void *current)
|
|
|
|
{
|
|
|
|
struct acpi_table_header *hdr = current - sizeof(hdr->_length);
|
|
|
|
return hdr->_length;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
void *acpi_table_hdr(void *h)
|
|
|
|
{
|
|
|
|
struct acpi_table_header *hdr = h;
|
|
|
|
return &hdr->sig;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t *acpi_table_first(void)
|
|
|
|
{
|
2019-02-14 09:49:39 +01:00
|
|
|
if (!acpi_tables) {
|
2013-10-07 13:41:40 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return acpi_table_hdr(acpi_tables + ACPI_TABLE_PFX_SIZE);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t *acpi_table_next(uint8_t *current)
|
|
|
|
{
|
|
|
|
uint8_t *next = current + acpi_table_len(current);
|
|
|
|
|
|
|
|
if (next - acpi_tables >= acpi_tables_len) {
|
|
|
|
return NULL;
|
|
|
|
} else {
|
|
|
|
return acpi_table_hdr(next);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-18 15:12:12 +01:00
|
|
|
int acpi_get_slic_oem(AcpiSlicOem *oem)
|
|
|
|
{
|
|
|
|
uint8_t *u;
|
|
|
|
|
|
|
|
for (u = acpi_table_first(); u; u = acpi_table_next(u)) {
|
|
|
|
struct acpi_table_header *hdr = (void *)(u - sizeof(hdr->_length));
|
|
|
|
|
|
|
|
if (memcmp(hdr->sig, "SLIC", 4) == 0) {
|
acpi: fix QEMU crash when started with SLIC table
if QEMU is started with used provided SLIC table blob,
-acpitable sig=SLIC,oem_id='CRASH ',oem_table_id="ME",oem_rev=00002210,asl_compiler_id="",asl_compiler_rev=00000000,data=/dev/null
it will assert with:
hw/acpi/aml-build.c:61:build_append_padded_str: assertion failed: (len <= maxlen)
and following backtrace:
...
build_append_padded_str (array=0x555556afe320, str=0x555556afdb2e "CRASH ME", maxlen=0x6, pad=0x20) at hw/acpi/aml-build.c:61
acpi_table_begin (desc=0x7fffffffd1b0, array=0x555556afe320) at hw/acpi/aml-build.c:1727
build_fadt (tbl=0x555556afe320, linker=0x555557ca3830, f=0x7fffffffd318, oem_id=0x555556afdb2e "CRASH ME", oem_table_id=0x555556afdb34 "ME") at hw/acpi/aml-build.c:2064
...
which happens due to acpi_table_begin() expecting NULL terminated
oem_id and oem_table_id strings, which is normally the case, but
in case of user provided SLIC table, oem_id points to table's blob
directly and as result oem_id became longer than expected.
Fix issue by handling oem_id consistently and make acpi_get_slic_oem()
return NULL terminated strings.
PS:
After [1] refactoring, oem_id semantics became inconsistent, where
NULL terminated string was coming from machine and old way pointer
into byte array coming from -acpitable option. That used to work
since build_header() wasn't expecting NULL terminated string and
blindly copied the 1st 6 bytes only.
However commit [2] broke that by replacing build_header() with
acpi_table_begin(), which was expecting NULL terminated string
and was checking oem_id size.
1) 602b45820 ("acpi: Permit OEM ID and OEM table ID fields to be changed")
2)
Fixes: 4b56e1e4eb08 ("acpi: build_fadt: use acpi_table_begin()/acpi_table_end() instead of build_header()")
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/786
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Message-Id: <20211227193120.1084176-2-imammedo@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Tested-by: Denis Lisov <dennis.lissov@gmail.com>
Tested-by: Alexander Tsoy <alexander@tsoy.me>
Cc: qemu-stable@nongnu.org
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
2021-12-27 20:31:17 +01:00
|
|
|
oem->id = g_strndup(hdr->oem_id, 6);
|
|
|
|
oem->table_id = g_strndup(hdr->oem_table_id, 8);
|
2016-01-18 15:12:12 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-02-23 13:45:20 +01:00
|
|
|
static void acpi_notify_wakeup(Notifier *notifier, void *data)
|
|
|
|
{
|
|
|
|
ACPIREGS *ar = container_of(notifier, ACPIREGS, wakeup);
|
|
|
|
WakeupReason *reason = data;
|
|
|
|
|
|
|
|
switch (*reason) {
|
2012-02-23 13:45:24 +01:00
|
|
|
case QEMU_WAKEUP_REASON_RTC:
|
|
|
|
ar->pm1.evt.sts |=
|
|
|
|
(ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_RT_CLOCK_STATUS);
|
|
|
|
break;
|
2012-02-23 13:45:25 +01:00
|
|
|
case QEMU_WAKEUP_REASON_PMTIMER:
|
|
|
|
ar->pm1.evt.sts |=
|
|
|
|
(ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_TIMER_STATUS);
|
|
|
|
break;
|
2012-02-23 13:45:20 +01:00
|
|
|
case QEMU_WAKEUP_REASON_OTHER:
|
|
|
|
/* ACPI_BITMASK_WAKE_STATUS should be set on resume.
|
|
|
|
Pretend that resume was caused by power button */
|
|
|
|
ar->pm1.evt.sts |=
|
|
|
|
(ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_POWER_BUTTON_STATUS);
|
|
|
|
break;
|
2013-09-25 18:38:29 +02:00
|
|
|
default:
|
|
|
|
break;
|
2012-02-23 13:45:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-25 11:54:39 +01:00
|
|
|
/* ACPI PM1a EVT */
|
2012-02-23 13:45:17 +01:00
|
|
|
uint16_t acpi_pm1_evt_get_sts(ACPIREGS *ar)
|
2011-03-25 11:54:39 +01:00
|
|
|
{
|
2014-11-07 11:31:33 +01:00
|
|
|
/* Compare ns-clock, not PM timer ticks, because
|
|
|
|
acpi_pm_tmr_update function uses ns for setting the timer. */
|
|
|
|
int64_t d = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
|
|
|
if (d >= muldiv64(ar->tmr.overflow_time,
|
2016-03-21 17:02:30 +01:00
|
|
|
NANOSECONDS_PER_SECOND, PM_TIMER_FREQUENCY)) {
|
2012-02-23 13:45:16 +01:00
|
|
|
ar->pm1.evt.sts |= ACPI_BITMASK_TIMER_STATUS;
|
2011-03-25 11:54:39 +01:00
|
|
|
}
|
2012-02-23 13:45:16 +01:00
|
|
|
return ar->pm1.evt.sts;
|
2011-03-25 11:54:39 +01:00
|
|
|
}
|
|
|
|
|
2012-11-22 13:25:10 +01:00
|
|
|
static void acpi_pm1_evt_write_sts(ACPIREGS *ar, uint16_t val)
|
2011-03-25 11:54:39 +01:00
|
|
|
{
|
2012-02-23 13:45:17 +01:00
|
|
|
uint16_t pm1_sts = acpi_pm1_evt_get_sts(ar);
|
2011-03-25 11:54:39 +01:00
|
|
|
if (pm1_sts & val & ACPI_BITMASK_TIMER_STATUS) {
|
|
|
|
/* if TMRSTS is reset, then compute the new overflow time */
|
2012-02-23 13:45:16 +01:00
|
|
|
acpi_pm_tmr_calc_overflow_time(ar);
|
2011-03-25 11:54:39 +01:00
|
|
|
}
|
2012-02-23 13:45:16 +01:00
|
|
|
ar->pm1.evt.sts &= ~val;
|
2011-03-25 11:54:39 +01:00
|
|
|
}
|
|
|
|
|
2012-11-22 13:25:10 +01:00
|
|
|
static void acpi_pm1_evt_write_en(ACPIREGS *ar, uint16_t val)
|
2012-02-23 13:45:18 +01:00
|
|
|
{
|
|
|
|
ar->pm1.evt.en = val;
|
2012-02-23 13:45:24 +01:00
|
|
|
qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_RTC,
|
|
|
|
val & ACPI_BITMASK_RT_CLOCK_ENABLE);
|
2012-02-23 13:45:25 +01:00
|
|
|
qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_PMTIMER,
|
|
|
|
val & ACPI_BITMASK_TIMER_ENABLE);
|
2012-02-23 13:45:18 +01:00
|
|
|
}
|
|
|
|
|
2012-02-23 13:45:16 +01:00
|
|
|
void acpi_pm1_evt_power_down(ACPIREGS *ar)
|
2011-03-25 11:54:39 +01:00
|
|
|
{
|
2012-02-23 13:45:16 +01:00
|
|
|
if (ar->pm1.evt.en & ACPI_BITMASK_POWER_BUTTON_ENABLE) {
|
|
|
|
ar->pm1.evt.sts |= ACPI_BITMASK_POWER_BUTTON_STATUS;
|
|
|
|
ar->tmr.update_sci(ar);
|
2011-03-25 11:54:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-23 13:45:16 +01:00
|
|
|
void acpi_pm1_evt_reset(ACPIREGS *ar)
|
2011-03-25 11:54:39 +01:00
|
|
|
{
|
2012-02-23 13:45:16 +01:00
|
|
|
ar->pm1.evt.sts = 0;
|
|
|
|
ar->pm1.evt.en = 0;
|
2012-02-23 13:45:24 +01:00
|
|
|
qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_RTC, 0);
|
2012-02-23 13:45:25 +01:00
|
|
|
qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_PMTIMER, 0);
|
2011-03-25 11:54:39 +01:00
|
|
|
}
|
|
|
|
|
2012-11-22 13:25:10 +01:00
|
|
|
static uint64_t acpi_pm_evt_read(void *opaque, hwaddr addr, unsigned width)
|
|
|
|
{
|
|
|
|
ACPIREGS *ar = opaque;
|
|
|
|
switch (addr) {
|
|
|
|
case 0:
|
|
|
|
return acpi_pm1_evt_get_sts(ar);
|
|
|
|
case 2:
|
|
|
|
return ar->pm1.evt.en;
|
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void acpi_pm_evt_write(void *opaque, hwaddr addr, uint64_t val,
|
|
|
|
unsigned width)
|
|
|
|
{
|
|
|
|
ACPIREGS *ar = opaque;
|
|
|
|
switch (addr) {
|
|
|
|
case 0:
|
|
|
|
acpi_pm1_evt_write_sts(ar, val);
|
|
|
|
ar->pm1.evt.update_sci(ar);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
acpi_pm1_evt_write_en(ar, val);
|
|
|
|
ar->pm1.evt.update_sci(ar);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const MemoryRegionOps acpi_pm_evt_ops = {
|
|
|
|
.read = acpi_pm_evt_read,
|
|
|
|
.write = acpi_pm_evt_write,
|
2020-07-20 18:06:27 +02:00
|
|
|
.impl.min_access_size = 2,
|
|
|
|
.valid.min_access_size = 1,
|
2012-11-22 13:25:10 +01:00
|
|
|
.valid.max_access_size = 2,
|
|
|
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
|
|
|
};
|
|
|
|
|
|
|
|
void acpi_pm1_evt_init(ACPIREGS *ar, acpi_update_sci_fn update_sci,
|
|
|
|
MemoryRegion *parent)
|
|
|
|
{
|
|
|
|
ar->pm1.evt.update_sci = update_sci;
|
2013-06-07 03:25:08 +02:00
|
|
|
memory_region_init_io(&ar->pm1.evt.io, memory_region_owner(parent),
|
|
|
|
&acpi_pm_evt_ops, ar, "acpi-evt", 4);
|
2012-11-22 13:25:10 +01:00
|
|
|
memory_region_add_subregion(parent, 0, &ar->pm1.evt.io);
|
|
|
|
}
|
|
|
|
|
2011-03-25 11:54:38 +01:00
|
|
|
/* ACPI PM_TMR */
|
2012-02-23 13:45:16 +01:00
|
|
|
void acpi_pm_tmr_update(ACPIREGS *ar, bool enable)
|
2011-03-25 11:54:38 +01:00
|
|
|
{
|
|
|
|
int64_t expire_time;
|
|
|
|
|
|
|
|
/* schedule a timer interruption if needed */
|
|
|
|
if (enable) {
|
2016-03-21 17:02:30 +01:00
|
|
|
expire_time = muldiv64(ar->tmr.overflow_time, NANOSECONDS_PER_SECOND,
|
2011-03-25 11:54:38 +01:00
|
|
|
PM_TIMER_FREQUENCY);
|
2013-08-21 17:03:08 +02:00
|
|
|
timer_mod(ar->tmr.timer, expire_time);
|
2011-03-25 11:54:38 +01:00
|
|
|
} else {
|
2013-08-21 17:03:08 +02:00
|
|
|
timer_del(ar->tmr.timer);
|
2011-03-25 11:54:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-15 15:36:13 +01:00
|
|
|
static inline int64_t acpi_pm_tmr_get_clock(void)
|
|
|
|
{
|
|
|
|
return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), PM_TIMER_FREQUENCY,
|
|
|
|
NANOSECONDS_PER_SECOND);
|
|
|
|
}
|
|
|
|
|
2012-02-23 13:45:16 +01:00
|
|
|
void acpi_pm_tmr_calc_overflow_time(ACPIREGS *ar)
|
2011-03-25 11:54:38 +01:00
|
|
|
{
|
|
|
|
int64_t d = acpi_pm_tmr_get_clock();
|
2012-02-23 13:45:16 +01:00
|
|
|
ar->tmr.overflow_time = (d + 0x800000LL) & ~0x7fffffLL;
|
2011-03-25 11:54:38 +01:00
|
|
|
}
|
|
|
|
|
2012-11-22 12:12:30 +01:00
|
|
|
static uint32_t acpi_pm_tmr_get(ACPIREGS *ar)
|
2011-03-25 11:54:38 +01:00
|
|
|
{
|
2011-11-29 09:52:38 +01:00
|
|
|
uint32_t d = acpi_pm_tmr_get_clock();
|
2011-03-25 11:54:38 +01:00
|
|
|
return d & 0xffffff;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void acpi_pm_tmr_timer(void *opaque)
|
|
|
|
{
|
2012-02-23 13:45:16 +01:00
|
|
|
ACPIREGS *ar = opaque;
|
qmp hmp: Make system_wakeup check wake-up support and run state
The qmp/hmp command 'system_wakeup' is simply a direct call to
'qemu_system_wakeup_request' from vl.c. This function verifies if
runstate is SUSPENDED and if the wake up reason is valid before
proceeding. However, no error or warning is thrown if any of those
pre-requirements isn't met. There is no way for the caller to
differentiate between a successful wakeup or an error state caused
when trying to wake up a guest that wasn't suspended.
This means that system_wakeup is silently failing, which can be
considered a bug. Adding error handling isn't an API break in this
case - applications that didn't check the result will remain broken,
the ones that check it will have a chance to deal with it.
Adding to that, the commit before previous created a new QMP API called
query-current-machine, with a new flag called wakeup-suspend-support,
that indicates if the guest has the capability of waking up from suspended
state. Although such guest will never reach SUSPENDED state and erroring
it out in this scenario would suffice, it is more informative for the user
to differentiate between a failure because the guest isn't suspended versus
a failure because the guest does not have support for wake up at all.
All this considered, this patch changes qmp_system_wakeup to check if
the guest is capable of waking up from suspend, and if it is suspended.
After this patch, this is the output of system_wakeup in a guest that
does not have wake-up from suspend support (ppc64):
(qemu) system_wakeup
wake-up from suspend is not supported by this guest
(qemu)
And this is the output of system_wakeup in a x86 guest that has the
support but isn't suspended:
(qemu) system_wakeup
Unable to wake up: guest is not in suspended state
(qemu)
Reported-by: Balamuruhan S <bala24@linux.vnet.ibm.com>
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Message-Id: <20181205194701.17836-4-danielhb413@gmail.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Acked-by: Eduardo Habkost <ehabkost@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2018-12-05 20:47:01 +01:00
|
|
|
|
|
|
|
qemu_system_wakeup_request(QEMU_WAKEUP_REASON_PMTIMER, NULL);
|
2012-02-23 13:45:16 +01:00
|
|
|
ar->tmr.update_sci(ar);
|
2011-03-25 11:54:38 +01:00
|
|
|
}
|
|
|
|
|
2012-11-22 12:12:30 +01:00
|
|
|
static uint64_t acpi_pm_tmr_read(void *opaque, hwaddr addr, unsigned width)
|
|
|
|
{
|
|
|
|
return acpi_pm_tmr_get(opaque);
|
|
|
|
}
|
|
|
|
|
2013-05-14 14:29:04 +02:00
|
|
|
static void acpi_pm_tmr_write(void *opaque, hwaddr addr, uint64_t val,
|
|
|
|
unsigned width)
|
|
|
|
{
|
|
|
|
/* nothing */
|
|
|
|
}
|
|
|
|
|
2012-11-22 12:12:30 +01:00
|
|
|
static const MemoryRegionOps acpi_pm_tmr_ops = {
|
|
|
|
.read = acpi_pm_tmr_read,
|
2013-05-14 14:29:04 +02:00
|
|
|
.write = acpi_pm_tmr_write,
|
2020-07-20 18:06:27 +02:00
|
|
|
.impl.min_access_size = 4,
|
|
|
|
.valid.min_access_size = 1,
|
2012-11-22 12:12:30 +01:00
|
|
|
.valid.max_access_size = 4,
|
|
|
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
|
|
|
};
|
|
|
|
|
|
|
|
void acpi_pm_tmr_init(ACPIREGS *ar, acpi_update_sci_fn update_sci,
|
|
|
|
MemoryRegion *parent)
|
2011-03-25 11:54:38 +01:00
|
|
|
{
|
2012-02-23 13:45:16 +01:00
|
|
|
ar->tmr.update_sci = update_sci;
|
2013-08-21 17:03:08 +02:00
|
|
|
ar->tmr.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, acpi_pm_tmr_timer, ar);
|
2013-06-07 03:25:08 +02:00
|
|
|
memory_region_init_io(&ar->tmr.io, memory_region_owner(parent),
|
|
|
|
&acpi_pm_tmr_ops, ar, "acpi-tmr", 4);
|
2012-11-22 12:12:30 +01:00
|
|
|
memory_region_add_subregion(parent, 8, &ar->tmr.io);
|
2011-03-25 11:54:38 +01:00
|
|
|
}
|
|
|
|
|
2012-02-23 13:45:16 +01:00
|
|
|
void acpi_pm_tmr_reset(ACPIREGS *ar)
|
2011-03-25 11:54:38 +01:00
|
|
|
{
|
2012-02-23 13:45:16 +01:00
|
|
|
ar->tmr.overflow_time = 0;
|
2013-08-21 17:03:08 +02:00
|
|
|
timer_del(ar->tmr.timer);
|
2011-03-25 11:54:38 +01:00
|
|
|
}
|
2011-03-25 11:54:40 +01:00
|
|
|
|
|
|
|
/* ACPI PM1aCNT */
|
2012-02-23 13:45:16 +01:00
|
|
|
void acpi_pm1_cnt_update(ACPIREGS *ar,
|
2011-03-25 11:54:40 +01:00
|
|
|
bool sci_enable, bool sci_disable)
|
|
|
|
{
|
|
|
|
/* ACPI specs 3.0, 4.7.2.5 */
|
2021-02-18 06:51:12 +01:00
|
|
|
if (ar->pm1.cnt.acpi_only) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-03-25 11:54:40 +01:00
|
|
|
if (sci_enable) {
|
2012-02-23 13:45:16 +01:00
|
|
|
ar->pm1.cnt.cnt |= ACPI_BITMASK_SCI_ENABLE;
|
2011-03-25 11:54:40 +01:00
|
|
|
} else if (sci_disable) {
|
2012-02-23 13:45:16 +01:00
|
|
|
ar->pm1.cnt.cnt &= ~ACPI_BITMASK_SCI_ENABLE;
|
2011-03-25 11:54:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-22 13:17:57 +01:00
|
|
|
static uint64_t acpi_pm_cnt_read(void *opaque, hwaddr addr, unsigned width)
|
|
|
|
{
|
|
|
|
ACPIREGS *ar = opaque;
|
2023-06-07 22:01:25 +02:00
|
|
|
return ar->pm1.cnt.cnt >> addr * 8;
|
2012-11-22 13:17:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void acpi_pm_cnt_write(void *opaque, hwaddr addr, uint64_t val,
|
|
|
|
unsigned width)
|
|
|
|
{
|
2023-06-07 22:01:25 +02:00
|
|
|
ACPIREGS *ar = opaque;
|
|
|
|
|
|
|
|
if (addr == 1) {
|
|
|
|
val = val << 8 | (ar->pm1.cnt.cnt & 0xff);
|
|
|
|
}
|
|
|
|
ar->pm1.cnt.cnt = val & ~(ACPI_BITMASK_SLEEP_ENABLE);
|
|
|
|
|
|
|
|
if (val & ACPI_BITMASK_SLEEP_ENABLE) {
|
|
|
|
/* change suspend type */
|
|
|
|
uint16_t sus_typ = (val >> 10) & 7;
|
|
|
|
switch (sus_typ) {
|
|
|
|
case 0: /* soft power off */
|
|
|
|
qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
qemu_system_suspend_request();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (sus_typ == ar->pm1.cnt.s4_val) { /* S4 request */
|
|
|
|
qapi_event_send_suspend_disk();
|
|
|
|
qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2012-11-22 13:17:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static const MemoryRegionOps acpi_pm_cnt_ops = {
|
|
|
|
.read = acpi_pm_cnt_read,
|
|
|
|
.write = acpi_pm_cnt_write,
|
2020-07-20 18:06:27 +02:00
|
|
|
.impl.min_access_size = 2,
|
|
|
|
.valid.min_access_size = 1,
|
2012-11-22 13:17:57 +01:00
|
|
|
.valid.max_access_size = 2,
|
|
|
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
|
|
|
};
|
|
|
|
|
2015-04-29 15:20:14 +02:00
|
|
|
void acpi_pm1_cnt_init(ACPIREGS *ar, MemoryRegion *parent,
|
2021-02-18 06:51:12 +01:00
|
|
|
bool disable_s3, bool disable_s4, uint8_t s4_val,
|
|
|
|
bool acpi_only)
|
2012-11-22 13:17:57 +01:00
|
|
|
{
|
2015-04-29 15:20:15 +02:00
|
|
|
FWCfgState *fw_cfg;
|
|
|
|
|
2013-04-02 20:41:40 +02:00
|
|
|
ar->pm1.cnt.s4_val = s4_val;
|
2021-02-18 06:51:12 +01:00
|
|
|
ar->pm1.cnt.acpi_only = acpi_only;
|
2012-11-22 13:17:57 +01:00
|
|
|
ar->wakeup.notify = acpi_notify_wakeup;
|
|
|
|
qemu_register_wakeup_notifier(&ar->wakeup);
|
qmp: query-current-machine with wakeup-suspend-support
When issuing the qmp/hmp 'system_wakeup' command, what happens in a
nutshell is:
- qmp_system_wakeup_request set runstate to RUNNING, sets a wakeup_reason
and notify the event
- in the main_loop, all vcpus are paused, a system reset is issued, all
subscribers of wakeup_notifiers receives a notification, vcpus are then
resumed and the wake up QAPI event is fired
Note that this procedure alone doesn't ensure that the guest will awake
from SUSPENDED state - the subscribers of the wake up event must take
action to resume the guest, otherwise the guest will simply reboot. At
this moment, only the ACPI machines via acpi_pm1_cnt_init and xen_hvm_init
have wake-up from suspend support.
However, only the presence of 'system_wakeup' is required for QGA to
support 'guest-suspend-ram' and 'guest-suspend-hybrid' at this moment.
This means that the user/management will expect to suspend the guest using
one of those suspend commands and then resume execution using system_wakeup,
regardless of the support offered in system_wakeup in the first place.
This patch creates a new API called query-current-machine [1], that holds
a new flag called 'wakeup-suspend-support' that indicates if the guest
supports wake up from suspend via system_wakeup. The machine is considered
to implement wake-up support if a call to a new 'qemu_register_wakeup_support'
is made during its init, as it is now being done inside acpi_pm1_cnt_init
and xen_hvm_init. This allows for any other machine type to declare wake-up
support regardless of ACPI state or wakeup_notifiers subscription, making easier
for newer implementations that might have their own mechanisms in the future.
This is the expected output of query-current-machine when running a x86
guest:
{"execute" : "query-current-machine"}
{"return": {"wakeup-suspend-support": true}}
Running the same x86 guest, but with the --no-acpi option:
{"execute" : "query-current-machine"}
{"return": {"wakeup-suspend-support": false}}
This is the output when running a pseries guest:
{"execute" : "query-current-machine"}
{"return": {"wakeup-suspend-support": false}}
With this extra tool, management can avoid situations where a guest
that does not have proper suspend/wake capabilities ends up in
inconsistent state (e.g.
https://github.com/open-power-host-os/qemu/issues/31).
[1] the decision of creating the query-current-machine API is based
on discussions in the QEMU mailing list where it was decided that
query-target wasn't a proper place to store the wake-up flag, neither
was query-machines because this isn't a static property of the
machine object. This new API can then be used to store other
dynamic machine properties that are scattered around the code
ATM. More info at:
https://lists.gnu.org/archive/html/qemu-devel/2018-05/msg04235.html
Reported-by: Balamuruhan S <bala24@linux.vnet.ibm.com>
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Message-Id: <20181205194701.17836-2-danielhb413@gmail.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Acked-by: Eduardo Habkost <ehabkost@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2018-12-05 20:46:59 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Register wake-up support in QMP query-current-machine API
|
|
|
|
*/
|
|
|
|
qemu_register_wakeup_support();
|
|
|
|
|
2013-06-07 03:25:08 +02:00
|
|
|
memory_region_init_io(&ar->pm1.cnt.io, memory_region_owner(parent),
|
|
|
|
&acpi_pm_cnt_ops, ar, "acpi-cnt", 2);
|
2012-11-22 13:17:57 +01:00
|
|
|
memory_region_add_subregion(parent, 4, &ar->pm1.cnt.io);
|
2015-04-29 15:20:15 +02:00
|
|
|
|
|
|
|
fw_cfg = fw_cfg_find();
|
|
|
|
if (fw_cfg) {
|
|
|
|
uint8_t suspend[6] = {128, 0, 0, 129, 128, 128};
|
|
|
|
suspend[3] = 1 | ((!disable_s3) << 7);
|
|
|
|
suspend[4] = s4_val | ((!disable_s4) << 7);
|
|
|
|
|
|
|
|
fw_cfg_add_file(fw_cfg, "etc/system-states", g_memdup(suspend, 6), 6);
|
|
|
|
}
|
2012-11-22 13:17:57 +01:00
|
|
|
}
|
|
|
|
|
2012-02-23 13:45:16 +01:00
|
|
|
void acpi_pm1_cnt_reset(ACPIREGS *ar)
|
2011-03-25 11:54:40 +01:00
|
|
|
{
|
2012-02-23 13:45:16 +01:00
|
|
|
ar->pm1.cnt.cnt = 0;
|
2021-02-18 06:51:12 +01:00
|
|
|
if (ar->pm1.cnt.acpi_only) {
|
|
|
|
ar->pm1.cnt.cnt |= ACPI_BITMASK_SCI_ENABLE;
|
|
|
|
}
|
2011-03-25 11:54:40 +01:00
|
|
|
}
|
2011-03-25 11:54:41 +01:00
|
|
|
|
|
|
|
/* ACPI GPE */
|
2012-02-23 13:45:16 +01:00
|
|
|
void acpi_gpe_init(ACPIREGS *ar, uint8_t len)
|
2011-03-25 11:54:41 +01:00
|
|
|
{
|
2012-02-23 13:45:16 +01:00
|
|
|
ar->gpe.len = len;
|
2015-11-19 14:14:07 +01:00
|
|
|
/* Only first len / 2 bytes are ever used,
|
|
|
|
* but the caller in ich9.c migrates full len bytes.
|
|
|
|
* TODO: fix ich9.c and drop the extra allocation.
|
|
|
|
*/
|
|
|
|
ar->gpe.sts = g_malloc0(len);
|
|
|
|
ar->gpe.en = g_malloc0(len);
|
2011-03-25 11:54:41 +01:00
|
|
|
}
|
|
|
|
|
2012-02-23 13:45:16 +01:00
|
|
|
void acpi_gpe_reset(ACPIREGS *ar)
|
2011-03-25 11:54:41 +01:00
|
|
|
{
|
2012-02-23 13:45:16 +01:00
|
|
|
memset(ar->gpe.sts, 0, ar->gpe.len / 2);
|
|
|
|
memset(ar->gpe.en, 0, ar->gpe.len / 2);
|
2011-03-25 11:54:41 +01:00
|
|
|
}
|
|
|
|
|
2012-02-23 13:45:16 +01:00
|
|
|
static uint8_t *acpi_gpe_ioport_get_ptr(ACPIREGS *ar, uint32_t addr)
|
2011-03-25 11:54:41 +01:00
|
|
|
{
|
|
|
|
uint8_t *cur = NULL;
|
|
|
|
|
2012-02-23 13:45:16 +01:00
|
|
|
if (addr < ar->gpe.len / 2) {
|
|
|
|
cur = ar->gpe.sts + addr;
|
|
|
|
} else if (addr < ar->gpe.len) {
|
|
|
|
cur = ar->gpe.en + addr - ar->gpe.len / 2;
|
2011-03-25 11:54:41 +01:00
|
|
|
} else {
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
return cur;
|
|
|
|
}
|
|
|
|
|
2012-02-23 13:45:16 +01:00
|
|
|
void acpi_gpe_ioport_writeb(ACPIREGS *ar, uint32_t addr, uint32_t val)
|
2011-03-25 11:54:41 +01:00
|
|
|
{
|
|
|
|
uint8_t *cur;
|
|
|
|
|
2012-02-23 13:45:16 +01:00
|
|
|
cur = acpi_gpe_ioport_get_ptr(ar, addr);
|
|
|
|
if (addr < ar->gpe.len / 2) {
|
2023-09-08 10:42:34 +02:00
|
|
|
trace_acpi_gpe_sts_ioport_writeb(addr, val);
|
2011-03-25 11:54:41 +01:00
|
|
|
/* GPE_STS */
|
|
|
|
*cur = (*cur) & ~val;
|
2012-02-23 13:45:16 +01:00
|
|
|
} else if (addr < ar->gpe.len) {
|
2023-09-08 10:42:34 +02:00
|
|
|
trace_acpi_gpe_en_ioport_writeb(addr - (ar->gpe.len / 2), val);
|
2011-03-25 11:54:41 +01:00
|
|
|
/* GPE_EN */
|
|
|
|
*cur = val;
|
|
|
|
} else {
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-23 13:45:16 +01:00
|
|
|
uint32_t acpi_gpe_ioport_readb(ACPIREGS *ar, uint32_t addr)
|
2011-03-25 11:54:41 +01:00
|
|
|
{
|
|
|
|
uint8_t *cur;
|
|
|
|
uint32_t val;
|
|
|
|
|
2012-02-23 13:45:16 +01:00
|
|
|
cur = acpi_gpe_ioport_get_ptr(ar, addr);
|
2011-03-25 11:54:41 +01:00
|
|
|
val = 0;
|
|
|
|
if (cur != NULL) {
|
|
|
|
val = *cur;
|
|
|
|
}
|
|
|
|
|
2023-09-08 10:42:34 +02:00
|
|
|
if (addr < ar->gpe.len / 2) {
|
|
|
|
trace_acpi_gpe_sts_ioport_readb(addr, val);
|
|
|
|
} else {
|
|
|
|
trace_acpi_gpe_en_ioport_readb(addr - (ar->gpe.len / 2), val);
|
|
|
|
}
|
2023-09-08 10:42:33 +02:00
|
|
|
|
2011-03-25 11:54:41 +01:00
|
|
|
return val;
|
|
|
|
}
|
2013-12-13 17:22:07 +01:00
|
|
|
|
2015-05-13 11:21:36 +02:00
|
|
|
void acpi_send_gpe_event(ACPIREGS *ar, qemu_irq irq,
|
2016-05-31 11:57:57 +02:00
|
|
|
AcpiEventStatusBits status)
|
2015-05-13 11:21:36 +02:00
|
|
|
{
|
|
|
|
ar->gpe.sts[0] |= status;
|
|
|
|
acpi_update_sci(ar, irq);
|
|
|
|
}
|
|
|
|
|
2013-12-13 17:22:07 +01:00
|
|
|
void acpi_update_sci(ACPIREGS *regs, qemu_irq irq)
|
|
|
|
{
|
|
|
|
int sci_level, pm1a_sts;
|
|
|
|
|
|
|
|
pm1a_sts = acpi_pm1_evt_get_sts(regs);
|
|
|
|
|
|
|
|
sci_level = ((pm1a_sts &
|
|
|
|
regs->pm1.evt.en & ACPI_BITMASK_PM1_COMMON_ENABLED) != 0) ||
|
|
|
|
((regs->gpe.sts[0] & regs->gpe.en[0]) != 0);
|
|
|
|
|
|
|
|
qemu_set_irq(irq, sci_level);
|
|
|
|
|
|
|
|
/* schedule a timer interruption if needed */
|
|
|
|
acpi_pm_tmr_update(regs,
|
|
|
|
(regs->pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
|
|
|
|
!(pm1a_sts & ACPI_BITMASK_TIMER_STATUS));
|
|
|
|
}
|