149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini * ACPI implementation
349ab747fSPaolo Bonzini *
449ab747fSPaolo Bonzini * Copyright (c) 2006 Fabrice Bellard
549ab747fSPaolo Bonzini *
649ab747fSPaolo Bonzini * This library is free software; you can redistribute it and/or
749ab747fSPaolo Bonzini * modify it under the terms of the GNU Lesser General Public
861f3c91aSChetan Pant * License version 2.1 as published by the Free Software Foundation.
949ab747fSPaolo Bonzini *
1049ab747fSPaolo Bonzini * This library is distributed in the hope that it will be useful,
1149ab747fSPaolo Bonzini * but WITHOUT ANY WARRANTY; without even the implied warranty of
1249ab747fSPaolo Bonzini * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1349ab747fSPaolo Bonzini * Lesser General Public License for more details.
1449ab747fSPaolo Bonzini *
1549ab747fSPaolo Bonzini * You should have received a copy of the GNU Lesser General Public
1649ab747fSPaolo Bonzini * License along with this library; if not, see <http://www.gnu.org/licenses/>
1749ab747fSPaolo Bonzini *
1849ab747fSPaolo Bonzini * Contributions after 2012-01-13 are licensed under the terms of the
1949ab747fSPaolo Bonzini * GNU GPL, version 2 or (at your option) any later version.
2049ab747fSPaolo Bonzini */
21e688df6bSMarkus Armbruster
22b6a0aa05SPeter Maydell #include "qemu/osdep.h"
2364552b6bSMarkus Armbruster #include "hw/irq.h"
2449ab747fSPaolo Bonzini #include "hw/acpi/acpi.h"
25e3845e7cSLaszlo Ersek #include "hw/nvram/fw_cfg.h"
2649ab747fSPaolo Bonzini #include "qemu/config-file.h"
27e688df6bSMarkus Armbruster #include "qapi/error.h"
2849ab747fSPaolo Bonzini #include "qapi/opts-visitor.h"
299af23989SMarkus Armbruster #include "qapi/qapi-events-run-state.h"
3027c9188fSPhilippe Mathieu-Daudé #include "qapi/qapi-visit-acpi.h"
312ab4b135SAlistair Francis #include "qemu/error-report.h"
320b8fa32fSMarkus Armbruster #include "qemu/module.h"
33922a01a0SMarkus Armbruster #include "qemu/option.h"
3454d31236SMarkus Armbruster #include "sysemu/runstate.h"
357f558ea5SBernhard Beschow #include "trace.h"
3649ab747fSPaolo Bonzini
3749ab747fSPaolo Bonzini struct acpi_table_header {
3849ab747fSPaolo Bonzini uint16_t _length; /* our length, not actual part of the hdr */
3949ab747fSPaolo Bonzini /* allows easier parsing for fw_cfg clients */
409cbb8ecaSPhilippe Mathieu-Daudé char sig[4]
419cbb8ecaSPhilippe Mathieu-Daudé QEMU_NONSTRING; /* ACPI signature (4 ASCII characters) */
4249ab747fSPaolo Bonzini uint32_t length; /* Length of table, in bytes, including header */
4349ab747fSPaolo Bonzini uint8_t revision; /* ACPI Specification minor version # */
4449ab747fSPaolo Bonzini uint8_t checksum; /* To make sum of entire table == 0 */
459cbb8ecaSPhilippe Mathieu-Daudé char oem_id[6]
469cbb8ecaSPhilippe Mathieu-Daudé QEMU_NONSTRING; /* OEM identification */
479cbb8ecaSPhilippe Mathieu-Daudé char oem_table_id[8]
489cbb8ecaSPhilippe Mathieu-Daudé QEMU_NONSTRING; /* OEM table identification */
4949ab747fSPaolo Bonzini uint32_t oem_revision; /* OEM revision number */
509cbb8ecaSPhilippe Mathieu-Daudé char asl_compiler_id[4]
519cbb8ecaSPhilippe Mathieu-Daudé QEMU_NONSTRING; /* ASL compiler vendor ID */
5249ab747fSPaolo Bonzini uint32_t asl_compiler_revision; /* ASL compiler revision number */
5349ab747fSPaolo Bonzini } QEMU_PACKED;
5449ab747fSPaolo Bonzini
5549ab747fSPaolo Bonzini #define ACPI_TABLE_HDR_SIZE sizeof(struct acpi_table_header)
5649ab747fSPaolo Bonzini #define ACPI_TABLE_PFX_SIZE sizeof(uint16_t) /* size of the extra prefix */
5749ab747fSPaolo Bonzini
5849ab747fSPaolo Bonzini static const char unsigned dfl_hdr[ACPI_TABLE_HDR_SIZE - ACPI_TABLE_PFX_SIZE] =
5949ab747fSPaolo Bonzini "QEMU\0\0\0\0\1\0" /* sig (4), len(4), revno (1), csum (1) */
6049ab747fSPaolo Bonzini "QEMUQEQEMUQEMU\1\0\0\0" /* OEM id (6), table (8), revno (4) */
6149ab747fSPaolo Bonzini "QEMU\1\0\0\0" /* ASL compiler ID (4), version (4) */
6249ab747fSPaolo Bonzini ;
6349ab747fSPaolo Bonzini
6449ab747fSPaolo Bonzini char unsigned *acpi_tables;
6549ab747fSPaolo Bonzini size_t acpi_tables_len;
6649ab747fSPaolo Bonzini
6749ab747fSPaolo Bonzini static QemuOptsList qemu_acpi_opts = {
6849ab747fSPaolo Bonzini .name = "acpi",
6949ab747fSPaolo Bonzini .implied_opt_name = "data",
7049ab747fSPaolo Bonzini .head = QTAILQ_HEAD_INITIALIZER(qemu_acpi_opts.head),
7149ab747fSPaolo Bonzini .desc = { { 0 } } /* validated with OptsVisitor */
7249ab747fSPaolo Bonzini };
7349ab747fSPaolo Bonzini
acpi_register_config(void)7449ab747fSPaolo Bonzini static void acpi_register_config(void)
7549ab747fSPaolo Bonzini {
7649ab747fSPaolo Bonzini qemu_add_opts(&qemu_acpi_opts);
7749ab747fSPaolo Bonzini }
7849ab747fSPaolo Bonzini
7934294e2fSEduardo Habkost opts_init(acpi_register_config);
8049ab747fSPaolo Bonzini
acpi_checksum(const uint8_t * data,int len)8149ab747fSPaolo Bonzini static int acpi_checksum(const uint8_t *data, int len)
8249ab747fSPaolo Bonzini {
8349ab747fSPaolo Bonzini int sum, i;
8449ab747fSPaolo Bonzini sum = 0;
8549ab747fSPaolo Bonzini for (i = 0; i < len; i++) {
8649ab747fSPaolo Bonzini sum += data[i];
8749ab747fSPaolo Bonzini }
8849ab747fSPaolo Bonzini return (-sum) & 0xff;
8949ab747fSPaolo Bonzini }
9049ab747fSPaolo Bonzini
9149ab747fSPaolo Bonzini
9249ab747fSPaolo Bonzini /* Install a copy of the ACPI table specified in @blob.
9349ab747fSPaolo Bonzini *
9449ab747fSPaolo Bonzini * If @has_header is set, @blob starts with the System Description Table Header
9549ab747fSPaolo Bonzini * structure. Otherwise, "dfl_hdr" is prepended. In any case, each header field
9649ab747fSPaolo Bonzini * is optionally overwritten from @hdrs.
9749ab747fSPaolo Bonzini *
9849ab747fSPaolo Bonzini * It is valid to call this function with
9949ab747fSPaolo Bonzini * (@blob == NULL && bloblen == 0 && !has_header).
10049ab747fSPaolo Bonzini *
10149ab747fSPaolo Bonzini * @hdrs->file and @hdrs->data are ignored.
10249ab747fSPaolo Bonzini *
10349ab747fSPaolo Bonzini * SIZE_MAX is considered "infinity" in this function.
10449ab747fSPaolo Bonzini *
10549ab747fSPaolo Bonzini * The number of tables that can be installed is not limited, but the 16-bit
10649ab747fSPaolo Bonzini * counter at the beginning of "acpi_tables" wraps around after UINT16_MAX.
10749ab747fSPaolo Bonzini */
acpi_table_install(const char unsigned * blob,size_t bloblen,bool has_header,const struct AcpiTableOptions * hdrs,Error ** errp)10849ab747fSPaolo Bonzini static void acpi_table_install(const char unsigned *blob, size_t bloblen,
10949ab747fSPaolo Bonzini bool has_header,
11049ab747fSPaolo Bonzini const struct AcpiTableOptions *hdrs,
11149ab747fSPaolo Bonzini Error **errp)
11249ab747fSPaolo Bonzini {
11349ab747fSPaolo Bonzini size_t body_start;
11449ab747fSPaolo Bonzini const char unsigned *hdr_src;
11549ab747fSPaolo Bonzini size_t body_size, acpi_payload_size;
11649ab747fSPaolo Bonzini struct acpi_table_header *ext_hdr;
11749ab747fSPaolo Bonzini unsigned changed_fields;
11849ab747fSPaolo Bonzini
11949ab747fSPaolo Bonzini /* Calculate where the ACPI table body starts within the blob, plus where
12049ab747fSPaolo Bonzini * to copy the ACPI table header from.
12149ab747fSPaolo Bonzini */
12249ab747fSPaolo Bonzini if (has_header) {
12349ab747fSPaolo Bonzini /* _length | ACPI header in blob | blob body
12449ab747fSPaolo Bonzini * ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^
12549ab747fSPaolo Bonzini * ACPI_TABLE_PFX_SIZE sizeof dfl_hdr body_size
12649ab747fSPaolo Bonzini * == body_start
12749ab747fSPaolo Bonzini *
12849ab747fSPaolo Bonzini * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12949ab747fSPaolo Bonzini * acpi_payload_size == bloblen
13049ab747fSPaolo Bonzini */
13149ab747fSPaolo Bonzini body_start = sizeof dfl_hdr;
13249ab747fSPaolo Bonzini
13349ab747fSPaolo Bonzini if (bloblen < body_start) {
13449ab747fSPaolo Bonzini error_setg(errp, "ACPI table claiming to have header is too "
13549ab747fSPaolo Bonzini "short, available: %zu, expected: %zu", bloblen,
13649ab747fSPaolo Bonzini body_start);
13749ab747fSPaolo Bonzini return;
13849ab747fSPaolo Bonzini }
13949ab747fSPaolo Bonzini hdr_src = blob;
14049ab747fSPaolo Bonzini } else {
14149ab747fSPaolo Bonzini /* _length | ACPI header in template | blob body
14249ab747fSPaolo Bonzini * ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^
14349ab747fSPaolo Bonzini * ACPI_TABLE_PFX_SIZE sizeof dfl_hdr body_size
14449ab747fSPaolo Bonzini * == bloblen
14549ab747fSPaolo Bonzini *
14649ab747fSPaolo Bonzini * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14749ab747fSPaolo Bonzini * acpi_payload_size
14849ab747fSPaolo Bonzini */
14949ab747fSPaolo Bonzini body_start = 0;
15049ab747fSPaolo Bonzini hdr_src = dfl_hdr;
15149ab747fSPaolo Bonzini }
15249ab747fSPaolo Bonzini body_size = bloblen - body_start;
15349ab747fSPaolo Bonzini acpi_payload_size = sizeof dfl_hdr + body_size;
15449ab747fSPaolo Bonzini
15549ab747fSPaolo Bonzini if (acpi_payload_size > UINT16_MAX) {
15649ab747fSPaolo Bonzini error_setg(errp, "ACPI table too big, requested: %zu, max: %u",
15749ab747fSPaolo Bonzini acpi_payload_size, (unsigned)UINT16_MAX);
15849ab747fSPaolo Bonzini return;
15949ab747fSPaolo Bonzini }
16049ab747fSPaolo Bonzini
16149ab747fSPaolo Bonzini /* We won't fail from here on. Initialize / extend the globals. */
16249ab747fSPaolo Bonzini if (acpi_tables == NULL) {
16349ab747fSPaolo Bonzini acpi_tables_len = sizeof(uint16_t);
16449ab747fSPaolo Bonzini acpi_tables = g_malloc0(acpi_tables_len);
16549ab747fSPaolo Bonzini }
16649ab747fSPaolo Bonzini
16749ab747fSPaolo Bonzini acpi_tables = g_realloc(acpi_tables, acpi_tables_len +
16849ab747fSPaolo Bonzini ACPI_TABLE_PFX_SIZE +
16949ab747fSPaolo Bonzini sizeof dfl_hdr + body_size);
17049ab747fSPaolo Bonzini
17149ab747fSPaolo Bonzini ext_hdr = (struct acpi_table_header *)(acpi_tables + acpi_tables_len);
17249ab747fSPaolo Bonzini acpi_tables_len += ACPI_TABLE_PFX_SIZE;
17349ab747fSPaolo Bonzini
17449ab747fSPaolo Bonzini memcpy(acpi_tables + acpi_tables_len, hdr_src, sizeof dfl_hdr);
17549ab747fSPaolo Bonzini acpi_tables_len += sizeof dfl_hdr;
17649ab747fSPaolo Bonzini
17749ab747fSPaolo Bonzini if (blob != NULL) {
17849ab747fSPaolo Bonzini memcpy(acpi_tables + acpi_tables_len, blob + body_start, body_size);
17949ab747fSPaolo Bonzini acpi_tables_len += body_size;
18049ab747fSPaolo Bonzini }
18149ab747fSPaolo Bonzini
18249ab747fSPaolo Bonzini /* increase number of tables */
183c65e5de9SPeter Maydell stw_le_p(acpi_tables, lduw_le_p(acpi_tables) + 1u);
18449ab747fSPaolo Bonzini
18549ab747fSPaolo Bonzini /* Update the header fields. The strings need not be NUL-terminated. */
18649ab747fSPaolo Bonzini changed_fields = 0;
18749ab747fSPaolo Bonzini ext_hdr->_length = cpu_to_le16(acpi_payload_size);
18849ab747fSPaolo Bonzini
189b94ba62fSMarkus Armbruster if (hdrs->sig) {
19049ab747fSPaolo Bonzini strncpy(ext_hdr->sig, hdrs->sig, sizeof ext_hdr->sig);
19149ab747fSPaolo Bonzini ++changed_fields;
19249ab747fSPaolo Bonzini }
19349ab747fSPaolo Bonzini
19449ab747fSPaolo Bonzini if (has_header && le32_to_cpu(ext_hdr->length) != acpi_payload_size) {
1958297be80SAlistair Francis warn_report("ACPI table has wrong length, header says "
1968297be80SAlistair Francis "%" PRIu32 ", actual size %zu bytes",
19749ab747fSPaolo Bonzini le32_to_cpu(ext_hdr->length), acpi_payload_size);
19849ab747fSPaolo Bonzini }
19949ab747fSPaolo Bonzini ext_hdr->length = cpu_to_le32(acpi_payload_size);
20049ab747fSPaolo Bonzini
20149ab747fSPaolo Bonzini if (hdrs->has_rev) {
20249ab747fSPaolo Bonzini ext_hdr->revision = hdrs->rev;
20349ab747fSPaolo Bonzini ++changed_fields;
20449ab747fSPaolo Bonzini }
20549ab747fSPaolo Bonzini
20649ab747fSPaolo Bonzini ext_hdr->checksum = 0;
20749ab747fSPaolo Bonzini
208b94ba62fSMarkus Armbruster if (hdrs->oem_id) {
20949ab747fSPaolo Bonzini strncpy(ext_hdr->oem_id, hdrs->oem_id, sizeof ext_hdr->oem_id);
21049ab747fSPaolo Bonzini ++changed_fields;
21149ab747fSPaolo Bonzini }
212b94ba62fSMarkus Armbruster if (hdrs->oem_table_id) {
21349ab747fSPaolo Bonzini strncpy(ext_hdr->oem_table_id, hdrs->oem_table_id,
21449ab747fSPaolo Bonzini sizeof ext_hdr->oem_table_id);
21549ab747fSPaolo Bonzini ++changed_fields;
21649ab747fSPaolo Bonzini }
21749ab747fSPaolo Bonzini if (hdrs->has_oem_rev) {
21849ab747fSPaolo Bonzini ext_hdr->oem_revision = cpu_to_le32(hdrs->oem_rev);
21949ab747fSPaolo Bonzini ++changed_fields;
22049ab747fSPaolo Bonzini }
221b94ba62fSMarkus Armbruster if (hdrs->asl_compiler_id) {
22249ab747fSPaolo Bonzini strncpy(ext_hdr->asl_compiler_id, hdrs->asl_compiler_id,
22349ab747fSPaolo Bonzini sizeof ext_hdr->asl_compiler_id);
22449ab747fSPaolo Bonzini ++changed_fields;
22549ab747fSPaolo Bonzini }
22649ab747fSPaolo Bonzini if (hdrs->has_asl_compiler_rev) {
22749ab747fSPaolo Bonzini ext_hdr->asl_compiler_revision = cpu_to_le32(hdrs->asl_compiler_rev);
22849ab747fSPaolo Bonzini ++changed_fields;
22949ab747fSPaolo Bonzini }
23049ab747fSPaolo Bonzini
23149ab747fSPaolo Bonzini if (!has_header && changed_fields == 0) {
2322ab4b135SAlistair Francis warn_report("ACPI table: no headers are specified");
23349ab747fSPaolo Bonzini }
23449ab747fSPaolo Bonzini
23549ab747fSPaolo Bonzini /* recalculate checksum */
23649ab747fSPaolo Bonzini ext_hdr->checksum = acpi_checksum((const char unsigned *)ext_hdr +
23749ab747fSPaolo Bonzini ACPI_TABLE_PFX_SIZE, acpi_payload_size);
23849ab747fSPaolo Bonzini }
23949ab747fSPaolo Bonzini
acpi_table_add(const QemuOpts * opts,Error ** errp)24049ab747fSPaolo Bonzini void acpi_table_add(const QemuOpts *opts, Error **errp)
24149ab747fSPaolo Bonzini {
24249ab747fSPaolo Bonzini AcpiTableOptions *hdrs = NULL;
24349ab747fSPaolo Bonzini char **pathnames = NULL;
24449ab747fSPaolo Bonzini char **cur;
24549ab747fSPaolo Bonzini size_t bloblen = 0;
24649ab747fSPaolo Bonzini char unsigned *blob = NULL;
24749ab747fSPaolo Bonzini
24849ab747fSPaolo Bonzini {
24909204eacSEric Blake Visitor *v;
25049ab747fSPaolo Bonzini
25109204eacSEric Blake v = opts_visitor_new(opts);
252b11a093cSMarkus Armbruster visit_type_AcpiTableOptions(v, NULL, &hdrs, errp);
25309204eacSEric Blake visit_free(v);
25449ab747fSPaolo Bonzini }
25549ab747fSPaolo Bonzini
256b11a093cSMarkus Armbruster if (!hdrs) {
25749ab747fSPaolo Bonzini goto out;
25849ab747fSPaolo Bonzini }
259b94ba62fSMarkus Armbruster if (!hdrs->file == !hdrs->data) {
260dcfe4805SMarkus Armbruster error_setg(errp, "'-acpitable' requires one of 'data' or 'file'");
26149ab747fSPaolo Bonzini goto out;
26249ab747fSPaolo Bonzini }
26349ab747fSPaolo Bonzini
264b94ba62fSMarkus Armbruster pathnames = g_strsplit(hdrs->file ?: hdrs->data, ":", 0);
26549ab747fSPaolo Bonzini if (pathnames == NULL || pathnames[0] == NULL) {
266dcfe4805SMarkus Armbruster error_setg(errp, "'-acpitable' requires at least one pathname");
26749ab747fSPaolo Bonzini goto out;
26849ab747fSPaolo Bonzini }
26949ab747fSPaolo Bonzini
27049ab747fSPaolo Bonzini /* now read in the data files, reallocating buffer as needed */
27149ab747fSPaolo Bonzini for (cur = pathnames; *cur; ++cur) {
27249ab747fSPaolo Bonzini int fd = open(*cur, O_RDONLY | O_BINARY);
27349ab747fSPaolo Bonzini
27449ab747fSPaolo Bonzini if (fd < 0) {
275dcfe4805SMarkus Armbruster error_setg(errp, "can't open file %s: %s", *cur, strerror(errno));
27649ab747fSPaolo Bonzini goto out;
27749ab747fSPaolo Bonzini }
27849ab747fSPaolo Bonzini
27949ab747fSPaolo Bonzini for (;;) {
28049ab747fSPaolo Bonzini char unsigned data[8192];
28149ab747fSPaolo Bonzini ssize_t r;
28249ab747fSPaolo Bonzini
28349ab747fSPaolo Bonzini r = read(fd, data, sizeof data);
28449ab747fSPaolo Bonzini if (r == 0) {
28549ab747fSPaolo Bonzini break;
28649ab747fSPaolo Bonzini } else if (r > 0) {
28749ab747fSPaolo Bonzini blob = g_realloc(blob, bloblen + r);
28849ab747fSPaolo Bonzini memcpy(blob + bloblen, data, r);
28949ab747fSPaolo Bonzini bloblen += r;
29049ab747fSPaolo Bonzini } else if (errno != EINTR) {
291dcfe4805SMarkus Armbruster error_setg(errp, "can't read file %s: %s", *cur,
292dcfe4805SMarkus Armbruster strerror(errno));
29349ab747fSPaolo Bonzini close(fd);
29449ab747fSPaolo Bonzini goto out;
29549ab747fSPaolo Bonzini }
29649ab747fSPaolo Bonzini }
29749ab747fSPaolo Bonzini
29849ab747fSPaolo Bonzini close(fd);
29949ab747fSPaolo Bonzini }
30049ab747fSPaolo Bonzini
301b94ba62fSMarkus Armbruster acpi_table_install(blob, bloblen, !!hdrs->file, hdrs, errp);
30249ab747fSPaolo Bonzini
30349ab747fSPaolo Bonzini out:
30449ab747fSPaolo Bonzini g_free(blob);
30549ab747fSPaolo Bonzini g_strfreev(pathnames);
30696a1616cSEric Blake qapi_free_AcpiTableOptions(hdrs);
30749ab747fSPaolo Bonzini }
30849ab747fSPaolo Bonzini
acpi_table_len(void * current)30960de1163SMichael S. Tsirkin unsigned acpi_table_len(void *current)
31060de1163SMichael S. Tsirkin {
31160de1163SMichael S. Tsirkin struct acpi_table_header *hdr = current - sizeof(hdr->_length);
31260de1163SMichael S. Tsirkin return hdr->_length;
31360de1163SMichael S. Tsirkin }
31460de1163SMichael S. Tsirkin
31560de1163SMichael S. Tsirkin static
acpi_table_hdr(void * h)31660de1163SMichael S. Tsirkin void *acpi_table_hdr(void *h)
31760de1163SMichael S. Tsirkin {
31860de1163SMichael S. Tsirkin struct acpi_table_header *hdr = h;
31960de1163SMichael S. Tsirkin return &hdr->sig;
32060de1163SMichael S. Tsirkin }
32160de1163SMichael S. Tsirkin
acpi_table_first(void)32260de1163SMichael S. Tsirkin uint8_t *acpi_table_first(void)
32360de1163SMichael S. Tsirkin {
3247d9b68acSWei Yang if (!acpi_tables) {
32560de1163SMichael S. Tsirkin return NULL;
32660de1163SMichael S. Tsirkin }
32760de1163SMichael S. Tsirkin return acpi_table_hdr(acpi_tables + ACPI_TABLE_PFX_SIZE);
32860de1163SMichael S. Tsirkin }
32960de1163SMichael S. Tsirkin
acpi_table_next(uint8_t * current)33060de1163SMichael S. Tsirkin uint8_t *acpi_table_next(uint8_t *current)
33160de1163SMichael S. Tsirkin {
33260de1163SMichael S. Tsirkin uint8_t *next = current + acpi_table_len(current);
33360de1163SMichael S. Tsirkin
33460de1163SMichael S. Tsirkin if (next - acpi_tables >= acpi_tables_len) {
33560de1163SMichael S. Tsirkin return NULL;
33660de1163SMichael S. Tsirkin } else {
33760de1163SMichael S. Tsirkin return acpi_table_hdr(next);
33860de1163SMichael S. Tsirkin }
33960de1163SMichael S. Tsirkin }
34060de1163SMichael S. Tsirkin
acpi_get_slic_oem(AcpiSlicOem * oem)34188594e4fSLaszlo Ersek int acpi_get_slic_oem(AcpiSlicOem *oem)
34288594e4fSLaszlo Ersek {
34388594e4fSLaszlo Ersek uint8_t *u;
34488594e4fSLaszlo Ersek
34588594e4fSLaszlo Ersek for (u = acpi_table_first(); u; u = acpi_table_next(u)) {
34688594e4fSLaszlo Ersek struct acpi_table_header *hdr = (void *)(u - sizeof(hdr->_length));
34788594e4fSLaszlo Ersek
34888594e4fSLaszlo Ersek if (memcmp(hdr->sig, "SLIC", 4) == 0) {
3498cdb99afSIgor Mammedov oem->id = g_strndup(hdr->oem_id, 6);
3508cdb99afSIgor Mammedov oem->table_id = g_strndup(hdr->oem_table_id, 8);
35188594e4fSLaszlo Ersek return 0;
35288594e4fSLaszlo Ersek }
35388594e4fSLaszlo Ersek }
35488594e4fSLaszlo Ersek return -1;
35588594e4fSLaszlo Ersek }
35688594e4fSLaszlo Ersek
acpi_notify_wakeup(Notifier * notifier,void * data)35749ab747fSPaolo Bonzini static void acpi_notify_wakeup(Notifier *notifier, void *data)
35849ab747fSPaolo Bonzini {
35949ab747fSPaolo Bonzini ACPIREGS *ar = container_of(notifier, ACPIREGS, wakeup);
36049ab747fSPaolo Bonzini WakeupReason *reason = data;
36149ab747fSPaolo Bonzini
36249ab747fSPaolo Bonzini switch (*reason) {
36349ab747fSPaolo Bonzini case QEMU_WAKEUP_REASON_RTC:
36449ab747fSPaolo Bonzini ar->pm1.evt.sts |=
36549ab747fSPaolo Bonzini (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_RT_CLOCK_STATUS);
36649ab747fSPaolo Bonzini break;
36749ab747fSPaolo Bonzini case QEMU_WAKEUP_REASON_PMTIMER:
36849ab747fSPaolo Bonzini ar->pm1.evt.sts |=
36949ab747fSPaolo Bonzini (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_TIMER_STATUS);
37049ab747fSPaolo Bonzini break;
37149ab747fSPaolo Bonzini case QEMU_WAKEUP_REASON_OTHER:
37249ab747fSPaolo Bonzini /* ACPI_BITMASK_WAKE_STATUS should be set on resume.
37349ab747fSPaolo Bonzini Pretend that resume was caused by power button */
37449ab747fSPaolo Bonzini ar->pm1.evt.sts |=
37549ab747fSPaolo Bonzini (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_POWER_BUTTON_STATUS);
37649ab747fSPaolo Bonzini break;
3774bc78a87SLiu, Jinsong default:
3784bc78a87SLiu, Jinsong break;
37949ab747fSPaolo Bonzini }
38049ab747fSPaolo Bonzini }
38149ab747fSPaolo Bonzini
38249ab747fSPaolo Bonzini /* ACPI PM1a EVT */
acpi_pm1_evt_get_sts(ACPIREGS * ar)38349ab747fSPaolo Bonzini uint16_t acpi_pm1_evt_get_sts(ACPIREGS *ar)
38449ab747fSPaolo Bonzini {
3853ef0eab1SPavel Dovgalyuk /* Compare ns-clock, not PM timer ticks, because
3863ef0eab1SPavel Dovgalyuk acpi_pm_tmr_update function uses ns for setting the timer. */
3873ef0eab1SPavel Dovgalyuk int64_t d = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
3883ef0eab1SPavel Dovgalyuk if (d >= muldiv64(ar->tmr.overflow_time,
38973bcb24dSRutuja Shah NANOSECONDS_PER_SECOND, PM_TIMER_FREQUENCY)) {
39049ab747fSPaolo Bonzini ar->pm1.evt.sts |= ACPI_BITMASK_TIMER_STATUS;
39149ab747fSPaolo Bonzini }
39249ab747fSPaolo Bonzini return ar->pm1.evt.sts;
39349ab747fSPaolo Bonzini }
39449ab747fSPaolo Bonzini
acpi_pm1_evt_write_sts(ACPIREGS * ar,uint16_t val)39549ab747fSPaolo Bonzini static void acpi_pm1_evt_write_sts(ACPIREGS *ar, uint16_t val)
39649ab747fSPaolo Bonzini {
39749ab747fSPaolo Bonzini uint16_t pm1_sts = acpi_pm1_evt_get_sts(ar);
39849ab747fSPaolo Bonzini if (pm1_sts & val & ACPI_BITMASK_TIMER_STATUS) {
39949ab747fSPaolo Bonzini /* if TMRSTS is reset, then compute the new overflow time */
40049ab747fSPaolo Bonzini acpi_pm_tmr_calc_overflow_time(ar);
40149ab747fSPaolo Bonzini }
40249ab747fSPaolo Bonzini ar->pm1.evt.sts &= ~val;
40349ab747fSPaolo Bonzini }
40449ab747fSPaolo Bonzini
acpi_pm1_evt_write_en(ACPIREGS * ar,uint16_t val)40549ab747fSPaolo Bonzini static void acpi_pm1_evt_write_en(ACPIREGS *ar, uint16_t val)
40649ab747fSPaolo Bonzini {
40749ab747fSPaolo Bonzini ar->pm1.evt.en = val;
40849ab747fSPaolo Bonzini qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_RTC,
40949ab747fSPaolo Bonzini val & ACPI_BITMASK_RT_CLOCK_ENABLE);
41049ab747fSPaolo Bonzini qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_PMTIMER,
41149ab747fSPaolo Bonzini val & ACPI_BITMASK_TIMER_ENABLE);
41249ab747fSPaolo Bonzini }
41349ab747fSPaolo Bonzini
acpi_pm1_evt_power_down(ACPIREGS * ar)41449ab747fSPaolo Bonzini void acpi_pm1_evt_power_down(ACPIREGS *ar)
41549ab747fSPaolo Bonzini {
41649ab747fSPaolo Bonzini if (ar->pm1.evt.en & ACPI_BITMASK_POWER_BUTTON_ENABLE) {
41749ab747fSPaolo Bonzini ar->pm1.evt.sts |= ACPI_BITMASK_POWER_BUTTON_STATUS;
41849ab747fSPaolo Bonzini ar->tmr.update_sci(ar);
41949ab747fSPaolo Bonzini }
42049ab747fSPaolo Bonzini }
42149ab747fSPaolo Bonzini
acpi_pm1_evt_reset(ACPIREGS * ar)42249ab747fSPaolo Bonzini void acpi_pm1_evt_reset(ACPIREGS *ar)
42349ab747fSPaolo Bonzini {
42449ab747fSPaolo Bonzini ar->pm1.evt.sts = 0;
42549ab747fSPaolo Bonzini ar->pm1.evt.en = 0;
42649ab747fSPaolo Bonzini qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_RTC, 0);
42749ab747fSPaolo Bonzini qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_PMTIMER, 0);
42849ab747fSPaolo Bonzini }
42949ab747fSPaolo Bonzini
acpi_pm_evt_read(void * opaque,hwaddr addr,unsigned width)43049ab747fSPaolo Bonzini static uint64_t acpi_pm_evt_read(void *opaque, hwaddr addr, unsigned width)
43149ab747fSPaolo Bonzini {
43249ab747fSPaolo Bonzini ACPIREGS *ar = opaque;
43349ab747fSPaolo Bonzini switch (addr) {
43449ab747fSPaolo Bonzini case 0:
43549ab747fSPaolo Bonzini return acpi_pm1_evt_get_sts(ar);
43649ab747fSPaolo Bonzini case 2:
43749ab747fSPaolo Bonzini return ar->pm1.evt.en;
43849ab747fSPaolo Bonzini default:
43949ab747fSPaolo Bonzini return 0;
44049ab747fSPaolo Bonzini }
44149ab747fSPaolo Bonzini }
44249ab747fSPaolo Bonzini
acpi_pm_evt_write(void * opaque,hwaddr addr,uint64_t val,unsigned width)44349ab747fSPaolo Bonzini static void acpi_pm_evt_write(void *opaque, hwaddr addr, uint64_t val,
44449ab747fSPaolo Bonzini unsigned width)
44549ab747fSPaolo Bonzini {
44649ab747fSPaolo Bonzini ACPIREGS *ar = opaque;
44749ab747fSPaolo Bonzini switch (addr) {
44849ab747fSPaolo Bonzini case 0:
44949ab747fSPaolo Bonzini acpi_pm1_evt_write_sts(ar, val);
45049ab747fSPaolo Bonzini ar->pm1.evt.update_sci(ar);
45149ab747fSPaolo Bonzini break;
45249ab747fSPaolo Bonzini case 2:
45349ab747fSPaolo Bonzini acpi_pm1_evt_write_en(ar, val);
45449ab747fSPaolo Bonzini ar->pm1.evt.update_sci(ar);
45549ab747fSPaolo Bonzini break;
45649ab747fSPaolo Bonzini }
45749ab747fSPaolo Bonzini }
45849ab747fSPaolo Bonzini
45949ab747fSPaolo Bonzini static const MemoryRegionOps acpi_pm_evt_ops = {
46049ab747fSPaolo Bonzini .read = acpi_pm_evt_read,
46149ab747fSPaolo Bonzini .write = acpi_pm_evt_write,
462dba04c34SMichael Tokarev .impl.min_access_size = 2,
463dba04c34SMichael Tokarev .valid.min_access_size = 1,
46449ab747fSPaolo Bonzini .valid.max_access_size = 2,
46549ab747fSPaolo Bonzini .endianness = DEVICE_LITTLE_ENDIAN,
46649ab747fSPaolo Bonzini };
46749ab747fSPaolo Bonzini
acpi_pm1_evt_init(ACPIREGS * ar,acpi_update_sci_fn update_sci,MemoryRegion * parent)46849ab747fSPaolo Bonzini void acpi_pm1_evt_init(ACPIREGS *ar, acpi_update_sci_fn update_sci,
46949ab747fSPaolo Bonzini MemoryRegion *parent)
47049ab747fSPaolo Bonzini {
47149ab747fSPaolo Bonzini ar->pm1.evt.update_sci = update_sci;
47264bde0f3SPaolo Bonzini memory_region_init_io(&ar->pm1.evt.io, memory_region_owner(parent),
47364bde0f3SPaolo Bonzini &acpi_pm_evt_ops, ar, "acpi-evt", 4);
47449ab747fSPaolo Bonzini memory_region_add_subregion(parent, 0, &ar->pm1.evt.io);
47549ab747fSPaolo Bonzini }
47649ab747fSPaolo Bonzini
47749ab747fSPaolo Bonzini /* ACPI PM_TMR */
acpi_pm_tmr_update(ACPIREGS * ar,bool enable)47849ab747fSPaolo Bonzini void acpi_pm_tmr_update(ACPIREGS *ar, bool enable)
47949ab747fSPaolo Bonzini {
48049ab747fSPaolo Bonzini int64_t expire_time;
48149ab747fSPaolo Bonzini
48249ab747fSPaolo Bonzini /* schedule a timer interruption if needed */
48349ab747fSPaolo Bonzini if (enable) {
48473bcb24dSRutuja Shah expire_time = muldiv64(ar->tmr.overflow_time, NANOSECONDS_PER_SECOND,
48549ab747fSPaolo Bonzini PM_TIMER_FREQUENCY);
486bc72ad67SAlex Bligh timer_mod(ar->tmr.timer, expire_time);
48749ab747fSPaolo Bonzini } else {
488bc72ad67SAlex Bligh timer_del(ar->tmr.timer);
48949ab747fSPaolo Bonzini }
49049ab747fSPaolo Bonzini }
49149ab747fSPaolo Bonzini
acpi_pm_tmr_get_clock(void)49287776ab7SPaolo Bonzini static inline int64_t acpi_pm_tmr_get_clock(void)
49387776ab7SPaolo Bonzini {
49487776ab7SPaolo Bonzini return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), PM_TIMER_FREQUENCY,
49587776ab7SPaolo Bonzini NANOSECONDS_PER_SECOND);
49687776ab7SPaolo Bonzini }
49787776ab7SPaolo Bonzini
acpi_pm_tmr_calc_overflow_time(ACPIREGS * ar)49849ab747fSPaolo Bonzini void acpi_pm_tmr_calc_overflow_time(ACPIREGS *ar)
49949ab747fSPaolo Bonzini {
50049ab747fSPaolo Bonzini int64_t d = acpi_pm_tmr_get_clock();
50149ab747fSPaolo Bonzini ar->tmr.overflow_time = (d + 0x800000LL) & ~0x7fffffLL;
50249ab747fSPaolo Bonzini }
50349ab747fSPaolo Bonzini
acpi_pm_tmr_get(ACPIREGS * ar)50449ab747fSPaolo Bonzini static uint32_t acpi_pm_tmr_get(ACPIREGS *ar)
50549ab747fSPaolo Bonzini {
50649ab747fSPaolo Bonzini uint32_t d = acpi_pm_tmr_get_clock();
50749ab747fSPaolo Bonzini return d & 0xffffff;
50849ab747fSPaolo Bonzini }
50949ab747fSPaolo Bonzini
acpi_pm_tmr_timer(void * opaque)51049ab747fSPaolo Bonzini static void acpi_pm_tmr_timer(void *opaque)
51149ab747fSPaolo Bonzini {
51249ab747fSPaolo Bonzini ACPIREGS *ar = opaque;
513fb064112SDaniel Henrique Barboza
514fb064112SDaniel Henrique Barboza qemu_system_wakeup_request(QEMU_WAKEUP_REASON_PMTIMER, NULL);
51549ab747fSPaolo Bonzini ar->tmr.update_sci(ar);
51649ab747fSPaolo Bonzini }
51749ab747fSPaolo Bonzini
acpi_pm_tmr_read(void * opaque,hwaddr addr,unsigned width)51849ab747fSPaolo Bonzini static uint64_t acpi_pm_tmr_read(void *opaque, hwaddr addr, unsigned width)
51949ab747fSPaolo Bonzini {
52049ab747fSPaolo Bonzini return acpi_pm_tmr_get(opaque);
52149ab747fSPaolo Bonzini }
52249ab747fSPaolo Bonzini
acpi_pm_tmr_write(void * opaque,hwaddr addr,uint64_t val,unsigned width)5232d3b9895SGerd Hoffmann static void acpi_pm_tmr_write(void *opaque, hwaddr addr, uint64_t val,
5242d3b9895SGerd Hoffmann unsigned width)
5252d3b9895SGerd Hoffmann {
5262d3b9895SGerd Hoffmann /* nothing */
5272d3b9895SGerd Hoffmann }
5282d3b9895SGerd Hoffmann
52949ab747fSPaolo Bonzini static const MemoryRegionOps acpi_pm_tmr_ops = {
53049ab747fSPaolo Bonzini .read = acpi_pm_tmr_read,
5312d3b9895SGerd Hoffmann .write = acpi_pm_tmr_write,
532dba04c34SMichael Tokarev .impl.min_access_size = 4,
533dba04c34SMichael Tokarev .valid.min_access_size = 1,
53449ab747fSPaolo Bonzini .valid.max_access_size = 4,
53549ab747fSPaolo Bonzini .endianness = DEVICE_LITTLE_ENDIAN,
53649ab747fSPaolo Bonzini };
53749ab747fSPaolo Bonzini
acpi_pm_tmr_init(ACPIREGS * ar,acpi_update_sci_fn update_sci,MemoryRegion * parent)53849ab747fSPaolo Bonzini void acpi_pm_tmr_init(ACPIREGS *ar, acpi_update_sci_fn update_sci,
53949ab747fSPaolo Bonzini MemoryRegion *parent)
54049ab747fSPaolo Bonzini {
54149ab747fSPaolo Bonzini ar->tmr.update_sci = update_sci;
542bc72ad67SAlex Bligh ar->tmr.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, acpi_pm_tmr_timer, ar);
54364bde0f3SPaolo Bonzini memory_region_init_io(&ar->tmr.io, memory_region_owner(parent),
54464bde0f3SPaolo Bonzini &acpi_pm_tmr_ops, ar, "acpi-tmr", 4);
54549ab747fSPaolo Bonzini memory_region_add_subregion(parent, 8, &ar->tmr.io);
54649ab747fSPaolo Bonzini }
54749ab747fSPaolo Bonzini
acpi_pm_tmr_reset(ACPIREGS * ar)54849ab747fSPaolo Bonzini void acpi_pm_tmr_reset(ACPIREGS *ar)
54949ab747fSPaolo Bonzini {
55049ab747fSPaolo Bonzini ar->tmr.overflow_time = 0;
551bc72ad67SAlex Bligh timer_del(ar->tmr.timer);
55249ab747fSPaolo Bonzini }
55349ab747fSPaolo Bonzini
55449ab747fSPaolo Bonzini /* ACPI PM1aCNT */
acpi_pm1_cnt_update(ACPIREGS * ar,bool sci_enable,bool sci_disable)55542b1b9d7SBALATON Zoltan void acpi_pm1_cnt_update(ACPIREGS *ar,
55642b1b9d7SBALATON Zoltan bool sci_enable, bool sci_disable)
55749ab747fSPaolo Bonzini {
55842b1b9d7SBALATON Zoltan /* ACPI specs 3.0, 4.7.2.5 */
55942b1b9d7SBALATON Zoltan if (ar->pm1.cnt.acpi_only) {
56042b1b9d7SBALATON Zoltan return;
56142b1b9d7SBALATON Zoltan }
56242b1b9d7SBALATON Zoltan
56342b1b9d7SBALATON Zoltan if (sci_enable) {
56442b1b9d7SBALATON Zoltan ar->pm1.cnt.cnt |= ACPI_BITMASK_SCI_ENABLE;
56542b1b9d7SBALATON Zoltan } else if (sci_disable) {
56642b1b9d7SBALATON Zoltan ar->pm1.cnt.cnt &= ~ACPI_BITMASK_SCI_ENABLE;
56742b1b9d7SBALATON Zoltan }
56842b1b9d7SBALATON Zoltan }
56942b1b9d7SBALATON Zoltan
acpi_pm_cnt_read(void * opaque,hwaddr addr,unsigned width)57042b1b9d7SBALATON Zoltan static uint64_t acpi_pm_cnt_read(void *opaque, hwaddr addr, unsigned width)
57142b1b9d7SBALATON Zoltan {
57242b1b9d7SBALATON Zoltan ACPIREGS *ar = opaque;
57342b1b9d7SBALATON Zoltan return ar->pm1.cnt.cnt >> addr * 8;
57442b1b9d7SBALATON Zoltan }
57542b1b9d7SBALATON Zoltan
acpi_pm_cnt_write(void * opaque,hwaddr addr,uint64_t val,unsigned width)57642b1b9d7SBALATON Zoltan static void acpi_pm_cnt_write(void *opaque, hwaddr addr, uint64_t val,
57742b1b9d7SBALATON Zoltan unsigned width)
57842b1b9d7SBALATON Zoltan {
57942b1b9d7SBALATON Zoltan ACPIREGS *ar = opaque;
58042b1b9d7SBALATON Zoltan
58142b1b9d7SBALATON Zoltan if (addr == 1) {
58242b1b9d7SBALATON Zoltan val = val << 8 | (ar->pm1.cnt.cnt & 0xff);
58342b1b9d7SBALATON Zoltan }
58449ab747fSPaolo Bonzini ar->pm1.cnt.cnt = val & ~(ACPI_BITMASK_SLEEP_ENABLE);
58549ab747fSPaolo Bonzini
58649ab747fSPaolo Bonzini if (val & ACPI_BITMASK_SLEEP_ENABLE) {
58749ab747fSPaolo Bonzini /* change suspend type */
58849ab747fSPaolo Bonzini uint16_t sus_typ = (val >> 10) & 7;
58949ab747fSPaolo Bonzini switch (sus_typ) {
59049ab747fSPaolo Bonzini case 0: /* soft power off */
591cf83f140SEric Blake qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
59249ab747fSPaolo Bonzini break;
59349ab747fSPaolo Bonzini case 1:
59449ab747fSPaolo Bonzini qemu_system_suspend_request();
59549ab747fSPaolo Bonzini break;
59649ab747fSPaolo Bonzini default:
59749ab747fSPaolo Bonzini if (sus_typ == ar->pm1.cnt.s4_val) { /* S4 request */
5983ab72385SPeter Xu qapi_event_send_suspend_disk();
599cf83f140SEric Blake qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
60049ab747fSPaolo Bonzini }
60149ab747fSPaolo Bonzini break;
60249ab747fSPaolo Bonzini }
60349ab747fSPaolo Bonzini }
60449ab747fSPaolo Bonzini }
60549ab747fSPaolo Bonzini
60649ab747fSPaolo Bonzini static const MemoryRegionOps acpi_pm_cnt_ops = {
60749ab747fSPaolo Bonzini .read = acpi_pm_cnt_read,
60849ab747fSPaolo Bonzini .write = acpi_pm_cnt_write,
609dba04c34SMichael Tokarev .impl.min_access_size = 2,
610dba04c34SMichael Tokarev .valid.min_access_size = 1,
61149ab747fSPaolo Bonzini .valid.max_access_size = 2,
61249ab747fSPaolo Bonzini .endianness = DEVICE_LITTLE_ENDIAN,
61349ab747fSPaolo Bonzini };
61449ab747fSPaolo Bonzini
acpi_pm1_cnt_init(ACPIREGS * ar,MemoryRegion * parent,bool disable_s3,bool disable_s4,uint8_t s4_val,bool acpi_only)6159a10bbb4SLaszlo Ersek void acpi_pm1_cnt_init(ACPIREGS *ar, MemoryRegion *parent,
6166be8cf56SIsaku Yamahata bool disable_s3, bool disable_s4, uint8_t s4_val,
6176be8cf56SIsaku Yamahata bool acpi_only)
61849ab747fSPaolo Bonzini {
619e3845e7cSLaszlo Ersek FWCfgState *fw_cfg;
620e3845e7cSLaszlo Ersek
62149ab747fSPaolo Bonzini ar->pm1.cnt.s4_val = s4_val;
6226be8cf56SIsaku Yamahata ar->pm1.cnt.acpi_only = acpi_only;
62349ab747fSPaolo Bonzini ar->wakeup.notify = acpi_notify_wakeup;
62449ab747fSPaolo Bonzini qemu_register_wakeup_notifier(&ar->wakeup);
62546ea94caSDaniel Henrique Barboza
62646ea94caSDaniel Henrique Barboza /*
62746ea94caSDaniel Henrique Barboza * Register wake-up support in QMP query-current-machine API
62846ea94caSDaniel Henrique Barboza */
62946ea94caSDaniel Henrique Barboza qemu_register_wakeup_support();
63046ea94caSDaniel Henrique Barboza
63164bde0f3SPaolo Bonzini memory_region_init_io(&ar->pm1.cnt.io, memory_region_owner(parent),
63264bde0f3SPaolo Bonzini &acpi_pm_cnt_ops, ar, "acpi-cnt", 2);
63349ab747fSPaolo Bonzini memory_region_add_subregion(parent, 4, &ar->pm1.cnt.io);
634e3845e7cSLaszlo Ersek
635e3845e7cSLaszlo Ersek fw_cfg = fw_cfg_find();
636e3845e7cSLaszlo Ersek if (fw_cfg) {
637e3845e7cSLaszlo Ersek uint8_t suspend[6] = {128, 0, 0, 129, 128, 128};
638e3845e7cSLaszlo Ersek suspend[3] = 1 | ((!disable_s3) << 7);
639e3845e7cSLaszlo Ersek suspend[4] = s4_val | ((!disable_s4) << 7);
640e3845e7cSLaszlo Ersek
641e3845e7cSLaszlo Ersek fw_cfg_add_file(fw_cfg, "etc/system-states", g_memdup(suspend, 6), 6);
642e3845e7cSLaszlo Ersek }
64349ab747fSPaolo Bonzini }
64449ab747fSPaolo Bonzini
acpi_pm1_cnt_reset(ACPIREGS * ar)64549ab747fSPaolo Bonzini void acpi_pm1_cnt_reset(ACPIREGS *ar)
64649ab747fSPaolo Bonzini {
64749ab747fSPaolo Bonzini ar->pm1.cnt.cnt = 0;
6486be8cf56SIsaku Yamahata if (ar->pm1.cnt.acpi_only) {
6496be8cf56SIsaku Yamahata ar->pm1.cnt.cnt |= ACPI_BITMASK_SCI_ENABLE;
6506be8cf56SIsaku Yamahata }
65149ab747fSPaolo Bonzini }
65249ab747fSPaolo Bonzini
65349ab747fSPaolo Bonzini /* ACPI GPE */
acpi_gpe_init(ACPIREGS * ar,uint8_t len)65449ab747fSPaolo Bonzini void acpi_gpe_init(ACPIREGS *ar, uint8_t len)
65549ab747fSPaolo Bonzini {
65649ab747fSPaolo Bonzini ar->gpe.len = len;
657d9a3b33dSMichael S. Tsirkin /* Only first len / 2 bytes are ever used,
658d9a3b33dSMichael S. Tsirkin * but the caller in ich9.c migrates full len bytes.
659d9a3b33dSMichael S. Tsirkin * TODO: fix ich9.c and drop the extra allocation.
660d9a3b33dSMichael S. Tsirkin */
661d9a3b33dSMichael S. Tsirkin ar->gpe.sts = g_malloc0(len);
662d9a3b33dSMichael S. Tsirkin ar->gpe.en = g_malloc0(len);
66349ab747fSPaolo Bonzini }
66449ab747fSPaolo Bonzini
acpi_gpe_reset(ACPIREGS * ar)66549ab747fSPaolo Bonzini void acpi_gpe_reset(ACPIREGS *ar)
66649ab747fSPaolo Bonzini {
66749ab747fSPaolo Bonzini memset(ar->gpe.sts, 0, ar->gpe.len / 2);
66849ab747fSPaolo Bonzini memset(ar->gpe.en, 0, ar->gpe.len / 2);
66949ab747fSPaolo Bonzini }
67049ab747fSPaolo Bonzini
acpi_gpe_ioport_get_ptr(ACPIREGS * ar,uint32_t addr)67149ab747fSPaolo Bonzini static uint8_t *acpi_gpe_ioport_get_ptr(ACPIREGS *ar, uint32_t addr)
67249ab747fSPaolo Bonzini {
67349ab747fSPaolo Bonzini uint8_t *cur = NULL;
67449ab747fSPaolo Bonzini
67549ab747fSPaolo Bonzini if (addr < ar->gpe.len / 2) {
67649ab747fSPaolo Bonzini cur = ar->gpe.sts + addr;
67749ab747fSPaolo Bonzini } else if (addr < ar->gpe.len) {
67849ab747fSPaolo Bonzini cur = ar->gpe.en + addr - ar->gpe.len / 2;
67949ab747fSPaolo Bonzini } else {
68049ab747fSPaolo Bonzini abort();
68149ab747fSPaolo Bonzini }
68249ab747fSPaolo Bonzini
68349ab747fSPaolo Bonzini return cur;
68449ab747fSPaolo Bonzini }
68549ab747fSPaolo Bonzini
acpi_gpe_ioport_writeb(ACPIREGS * ar,uint32_t addr,uint32_t val)68649ab747fSPaolo Bonzini void acpi_gpe_ioport_writeb(ACPIREGS *ar, uint32_t addr, uint32_t val)
68749ab747fSPaolo Bonzini {
68849ab747fSPaolo Bonzini uint8_t *cur;
68949ab747fSPaolo Bonzini
69049ab747fSPaolo Bonzini cur = acpi_gpe_ioport_get_ptr(ar, addr);
69149ab747fSPaolo Bonzini if (addr < ar->gpe.len / 2) {
692*40a6b893SBernhard Beschow trace_acpi_gpe_sts_ioport_writeb(addr, val);
69349ab747fSPaolo Bonzini /* GPE_STS */
69449ab747fSPaolo Bonzini *cur = (*cur) & ~val;
69549ab747fSPaolo Bonzini } else if (addr < ar->gpe.len) {
696*40a6b893SBernhard Beschow trace_acpi_gpe_en_ioport_writeb(addr - (ar->gpe.len / 2), val);
69749ab747fSPaolo Bonzini /* GPE_EN */
69849ab747fSPaolo Bonzini *cur = val;
69949ab747fSPaolo Bonzini } else {
70049ab747fSPaolo Bonzini abort();
70149ab747fSPaolo Bonzini }
70249ab747fSPaolo Bonzini }
70349ab747fSPaolo Bonzini
acpi_gpe_ioport_readb(ACPIREGS * ar,uint32_t addr)70449ab747fSPaolo Bonzini uint32_t acpi_gpe_ioport_readb(ACPIREGS *ar, uint32_t addr)
70549ab747fSPaolo Bonzini {
70649ab747fSPaolo Bonzini uint8_t *cur;
70749ab747fSPaolo Bonzini uint32_t val;
70849ab747fSPaolo Bonzini
70949ab747fSPaolo Bonzini cur = acpi_gpe_ioport_get_ptr(ar, addr);
71049ab747fSPaolo Bonzini val = 0;
71149ab747fSPaolo Bonzini if (cur != NULL) {
71249ab747fSPaolo Bonzini val = *cur;
71349ab747fSPaolo Bonzini }
71449ab747fSPaolo Bonzini
715*40a6b893SBernhard Beschow if (addr < ar->gpe.len / 2) {
716*40a6b893SBernhard Beschow trace_acpi_gpe_sts_ioport_readb(addr, val);
717*40a6b893SBernhard Beschow } else {
718*40a6b893SBernhard Beschow trace_acpi_gpe_en_ioport_readb(addr - (ar->gpe.len / 2), val);
719*40a6b893SBernhard Beschow }
7207f558ea5SBernhard Beschow
72149ab747fSPaolo Bonzini return val;
72249ab747fSPaolo Bonzini }
72306313503SIgor Mammedov
acpi_send_gpe_event(ACPIREGS * ar,qemu_irq irq,AcpiEventStatusBits status)724ca9b46bcSZhu Guihua void acpi_send_gpe_event(ACPIREGS *ar, qemu_irq irq,
725eaf23bf7SIgor Mammedov AcpiEventStatusBits status)
726ca9b46bcSZhu Guihua {
727ca9b46bcSZhu Guihua ar->gpe.sts[0] |= status;
728ca9b46bcSZhu Guihua acpi_update_sci(ar, irq);
729ca9b46bcSZhu Guihua }
730ca9b46bcSZhu Guihua
acpi_update_sci(ACPIREGS * regs,qemu_irq irq)73106313503SIgor Mammedov void acpi_update_sci(ACPIREGS *regs, qemu_irq irq)
73206313503SIgor Mammedov {
73306313503SIgor Mammedov int sci_level, pm1a_sts;
73406313503SIgor Mammedov
73506313503SIgor Mammedov pm1a_sts = acpi_pm1_evt_get_sts(regs);
73606313503SIgor Mammedov
73706313503SIgor Mammedov sci_level = ((pm1a_sts &
73806313503SIgor Mammedov regs->pm1.evt.en & ACPI_BITMASK_PM1_COMMON_ENABLED) != 0) ||
73906313503SIgor Mammedov ((regs->gpe.sts[0] & regs->gpe.en[0]) != 0);
74006313503SIgor Mammedov
74106313503SIgor Mammedov qemu_set_irq(irq, sci_level);
74206313503SIgor Mammedov
74306313503SIgor Mammedov /* schedule a timer interruption if needed */
74406313503SIgor Mammedov acpi_pm_tmr_update(regs,
74506313503SIgor Mammedov (regs->pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
74606313503SIgor Mammedov !(pm1a_sts & ACPI_BITMASK_TIMER_STATUS));
74706313503SIgor Mammedov }
748