xref: /openbmc/qemu/hw/acpi/core.c (revision cf83f140059f21d4629ae4b61d468c3baef2bb4c)
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
849ab747fSPaolo Bonzini  * License version 2 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  */
21b6a0aa05SPeter Maydell #include "qemu/osdep.h"
2249ab747fSPaolo Bonzini #include "sysemu/sysemu.h"
2349ab747fSPaolo Bonzini #include "hw/hw.h"
2449ab747fSPaolo Bonzini #include "hw/i386/pc.h"
2549ab747fSPaolo Bonzini #include "hw/acpi/acpi.h"
26e3845e7cSLaszlo Ersek #include "hw/nvram/fw_cfg.h"
2749ab747fSPaolo Bonzini #include "qemu/config-file.h"
2849ab747fSPaolo Bonzini #include "qapi/opts-visitor.h"
2949ab747fSPaolo Bonzini #include "qapi-visit.h"
302ea4100fSWenchao Xia #include "qapi-event.h"
3149ab747fSPaolo Bonzini 
3249ab747fSPaolo Bonzini struct acpi_table_header {
3349ab747fSPaolo Bonzini     uint16_t _length;         /* our length, not actual part of the hdr */
3449ab747fSPaolo Bonzini                               /* allows easier parsing for fw_cfg clients */
3549ab747fSPaolo Bonzini     char sig[4];              /* ACPI signature (4 ASCII characters) */
3649ab747fSPaolo Bonzini     uint32_t length;          /* Length of table, in bytes, including header */
3749ab747fSPaolo Bonzini     uint8_t revision;         /* ACPI Specification minor version # */
3849ab747fSPaolo Bonzini     uint8_t checksum;         /* To make sum of entire table == 0 */
3949ab747fSPaolo Bonzini     char oem_id[6];           /* OEM identification */
4049ab747fSPaolo Bonzini     char oem_table_id[8];     /* OEM table identification */
4149ab747fSPaolo Bonzini     uint32_t oem_revision;    /* OEM revision number */
4249ab747fSPaolo Bonzini     char asl_compiler_id[4];  /* ASL compiler vendor ID */
4349ab747fSPaolo Bonzini     uint32_t asl_compiler_revision; /* ASL compiler revision number */
4449ab747fSPaolo Bonzini } QEMU_PACKED;
4549ab747fSPaolo Bonzini 
4649ab747fSPaolo Bonzini #define ACPI_TABLE_HDR_SIZE sizeof(struct acpi_table_header)
4749ab747fSPaolo Bonzini #define ACPI_TABLE_PFX_SIZE sizeof(uint16_t)  /* size of the extra prefix */
4849ab747fSPaolo Bonzini 
4949ab747fSPaolo Bonzini static const char unsigned dfl_hdr[ACPI_TABLE_HDR_SIZE - ACPI_TABLE_PFX_SIZE] =
5049ab747fSPaolo Bonzini     "QEMU\0\0\0\0\1\0"       /* sig (4), len(4), revno (1), csum (1) */
5149ab747fSPaolo Bonzini     "QEMUQEQEMUQEMU\1\0\0\0" /* OEM id (6), table (8), revno (4) */
5249ab747fSPaolo Bonzini     "QEMU\1\0\0\0"           /* ASL compiler ID (4), version (4) */
5349ab747fSPaolo Bonzini     ;
5449ab747fSPaolo Bonzini 
5549ab747fSPaolo Bonzini char unsigned *acpi_tables;
5649ab747fSPaolo Bonzini size_t acpi_tables_len;
5749ab747fSPaolo Bonzini 
5849ab747fSPaolo Bonzini static QemuOptsList qemu_acpi_opts = {
5949ab747fSPaolo Bonzini     .name = "acpi",
6049ab747fSPaolo Bonzini     .implied_opt_name = "data",
6149ab747fSPaolo Bonzini     .head = QTAILQ_HEAD_INITIALIZER(qemu_acpi_opts.head),
6249ab747fSPaolo Bonzini     .desc = { { 0 } } /* validated with OptsVisitor */
6349ab747fSPaolo Bonzini };
6449ab747fSPaolo Bonzini 
6549ab747fSPaolo Bonzini static void acpi_register_config(void)
6649ab747fSPaolo Bonzini {
6749ab747fSPaolo Bonzini     qemu_add_opts(&qemu_acpi_opts);
6849ab747fSPaolo Bonzini }
6949ab747fSPaolo Bonzini 
7034294e2fSEduardo Habkost opts_init(acpi_register_config);
7149ab747fSPaolo Bonzini 
7249ab747fSPaolo Bonzini static int acpi_checksum(const uint8_t *data, int len)
7349ab747fSPaolo Bonzini {
7449ab747fSPaolo Bonzini     int sum, i;
7549ab747fSPaolo Bonzini     sum = 0;
7649ab747fSPaolo Bonzini     for (i = 0; i < len; i++) {
7749ab747fSPaolo Bonzini         sum += data[i];
7849ab747fSPaolo Bonzini     }
7949ab747fSPaolo Bonzini     return (-sum) & 0xff;
8049ab747fSPaolo Bonzini }
8149ab747fSPaolo Bonzini 
8249ab747fSPaolo Bonzini 
8349ab747fSPaolo Bonzini /* Install a copy of the ACPI table specified in @blob.
8449ab747fSPaolo Bonzini  *
8549ab747fSPaolo Bonzini  * If @has_header is set, @blob starts with the System Description Table Header
8649ab747fSPaolo Bonzini  * structure. Otherwise, "dfl_hdr" is prepended. In any case, each header field
8749ab747fSPaolo Bonzini  * is optionally overwritten from @hdrs.
8849ab747fSPaolo Bonzini  *
8949ab747fSPaolo Bonzini  * It is valid to call this function with
9049ab747fSPaolo Bonzini  * (@blob == NULL && bloblen == 0 && !has_header).
9149ab747fSPaolo Bonzini  *
9249ab747fSPaolo Bonzini  * @hdrs->file and @hdrs->data are ignored.
9349ab747fSPaolo Bonzini  *
9449ab747fSPaolo Bonzini  * SIZE_MAX is considered "infinity" in this function.
9549ab747fSPaolo Bonzini  *
9649ab747fSPaolo Bonzini  * The number of tables that can be installed is not limited, but the 16-bit
9749ab747fSPaolo Bonzini  * counter at the beginning of "acpi_tables" wraps around after UINT16_MAX.
9849ab747fSPaolo Bonzini  */
9949ab747fSPaolo Bonzini static void acpi_table_install(const char unsigned *blob, size_t bloblen,
10049ab747fSPaolo Bonzini                                bool has_header,
10149ab747fSPaolo Bonzini                                const struct AcpiTableOptions *hdrs,
10249ab747fSPaolo Bonzini                                Error **errp)
10349ab747fSPaolo Bonzini {
10449ab747fSPaolo Bonzini     size_t body_start;
10549ab747fSPaolo Bonzini     const char unsigned *hdr_src;
10649ab747fSPaolo Bonzini     size_t body_size, acpi_payload_size;
10749ab747fSPaolo Bonzini     struct acpi_table_header *ext_hdr;
10849ab747fSPaolo Bonzini     unsigned changed_fields;
10949ab747fSPaolo Bonzini 
11049ab747fSPaolo Bonzini     /* Calculate where the ACPI table body starts within the blob, plus where
11149ab747fSPaolo Bonzini      * to copy the ACPI table header from.
11249ab747fSPaolo Bonzini      */
11349ab747fSPaolo Bonzini     if (has_header) {
11449ab747fSPaolo Bonzini         /*   _length             | ACPI header in blob | blob body
11549ab747fSPaolo Bonzini          *   ^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^
11649ab747fSPaolo Bonzini          *   ACPI_TABLE_PFX_SIZE     sizeof dfl_hdr      body_size
11749ab747fSPaolo Bonzini          *                           == body_start
11849ab747fSPaolo Bonzini          *
11949ab747fSPaolo Bonzini          *                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12049ab747fSPaolo Bonzini          *                           acpi_payload_size == bloblen
12149ab747fSPaolo Bonzini          */
12249ab747fSPaolo Bonzini         body_start = sizeof dfl_hdr;
12349ab747fSPaolo Bonzini 
12449ab747fSPaolo Bonzini         if (bloblen < body_start) {
12549ab747fSPaolo Bonzini             error_setg(errp, "ACPI table claiming to have header is too "
12649ab747fSPaolo Bonzini                        "short, available: %zu, expected: %zu", bloblen,
12749ab747fSPaolo Bonzini                        body_start);
12849ab747fSPaolo Bonzini             return;
12949ab747fSPaolo Bonzini         }
13049ab747fSPaolo Bonzini         hdr_src = blob;
13149ab747fSPaolo Bonzini     } else {
13249ab747fSPaolo Bonzini         /*   _length             | ACPI header in template | blob body
13349ab747fSPaolo Bonzini          *   ^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^
13449ab747fSPaolo Bonzini          *   ACPI_TABLE_PFX_SIZE       sizeof dfl_hdr        body_size
13549ab747fSPaolo Bonzini          *                                                   == bloblen
13649ab747fSPaolo Bonzini          *
13749ab747fSPaolo Bonzini          *                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13849ab747fSPaolo Bonzini          *                                  acpi_payload_size
13949ab747fSPaolo Bonzini          */
14049ab747fSPaolo Bonzini         body_start = 0;
14149ab747fSPaolo Bonzini         hdr_src = dfl_hdr;
14249ab747fSPaolo Bonzini     }
14349ab747fSPaolo Bonzini     body_size = bloblen - body_start;
14449ab747fSPaolo Bonzini     acpi_payload_size = sizeof dfl_hdr + body_size;
14549ab747fSPaolo Bonzini 
14649ab747fSPaolo Bonzini     if (acpi_payload_size > UINT16_MAX) {
14749ab747fSPaolo Bonzini         error_setg(errp, "ACPI table too big, requested: %zu, max: %u",
14849ab747fSPaolo Bonzini                    acpi_payload_size, (unsigned)UINT16_MAX);
14949ab747fSPaolo Bonzini         return;
15049ab747fSPaolo Bonzini     }
15149ab747fSPaolo Bonzini 
15249ab747fSPaolo Bonzini     /* We won't fail from here on. Initialize / extend the globals. */
15349ab747fSPaolo Bonzini     if (acpi_tables == NULL) {
15449ab747fSPaolo Bonzini         acpi_tables_len = sizeof(uint16_t);
15549ab747fSPaolo Bonzini         acpi_tables = g_malloc0(acpi_tables_len);
15649ab747fSPaolo Bonzini     }
15749ab747fSPaolo Bonzini 
15849ab747fSPaolo Bonzini     acpi_tables = g_realloc(acpi_tables, acpi_tables_len +
15949ab747fSPaolo Bonzini                                          ACPI_TABLE_PFX_SIZE +
16049ab747fSPaolo Bonzini                                          sizeof dfl_hdr + body_size);
16149ab747fSPaolo Bonzini 
16249ab747fSPaolo Bonzini     ext_hdr = (struct acpi_table_header *)(acpi_tables + acpi_tables_len);
16349ab747fSPaolo Bonzini     acpi_tables_len += ACPI_TABLE_PFX_SIZE;
16449ab747fSPaolo Bonzini 
16549ab747fSPaolo Bonzini     memcpy(acpi_tables + acpi_tables_len, hdr_src, sizeof dfl_hdr);
16649ab747fSPaolo Bonzini     acpi_tables_len += sizeof dfl_hdr;
16749ab747fSPaolo Bonzini 
16849ab747fSPaolo Bonzini     if (blob != NULL) {
16949ab747fSPaolo Bonzini         memcpy(acpi_tables + acpi_tables_len, blob + body_start, body_size);
17049ab747fSPaolo Bonzini         acpi_tables_len += body_size;
17149ab747fSPaolo Bonzini     }
17249ab747fSPaolo Bonzini 
17349ab747fSPaolo Bonzini     /* increase number of tables */
174c65e5de9SPeter Maydell     stw_le_p(acpi_tables, lduw_le_p(acpi_tables) + 1u);
17549ab747fSPaolo Bonzini 
17649ab747fSPaolo Bonzini     /* Update the header fields. The strings need not be NUL-terminated. */
17749ab747fSPaolo Bonzini     changed_fields = 0;
17849ab747fSPaolo Bonzini     ext_hdr->_length = cpu_to_le16(acpi_payload_size);
17949ab747fSPaolo Bonzini 
18049ab747fSPaolo Bonzini     if (hdrs->has_sig) {
18149ab747fSPaolo Bonzini         strncpy(ext_hdr->sig, hdrs->sig, sizeof ext_hdr->sig);
18249ab747fSPaolo Bonzini         ++changed_fields;
18349ab747fSPaolo Bonzini     }
18449ab747fSPaolo Bonzini 
18549ab747fSPaolo Bonzini     if (has_header && le32_to_cpu(ext_hdr->length) != acpi_payload_size) {
18649ab747fSPaolo Bonzini         fprintf(stderr,
18749ab747fSPaolo Bonzini                 "warning: ACPI table has wrong length, header says "
18849ab747fSPaolo Bonzini                 "%" PRIu32 ", actual size %zu bytes\n",
18949ab747fSPaolo Bonzini                 le32_to_cpu(ext_hdr->length), acpi_payload_size);
19049ab747fSPaolo Bonzini     }
19149ab747fSPaolo Bonzini     ext_hdr->length = cpu_to_le32(acpi_payload_size);
19249ab747fSPaolo Bonzini 
19349ab747fSPaolo Bonzini     if (hdrs->has_rev) {
19449ab747fSPaolo Bonzini         ext_hdr->revision = hdrs->rev;
19549ab747fSPaolo Bonzini         ++changed_fields;
19649ab747fSPaolo Bonzini     }
19749ab747fSPaolo Bonzini 
19849ab747fSPaolo Bonzini     ext_hdr->checksum = 0;
19949ab747fSPaolo Bonzini 
20049ab747fSPaolo Bonzini     if (hdrs->has_oem_id) {
20149ab747fSPaolo Bonzini         strncpy(ext_hdr->oem_id, hdrs->oem_id, sizeof ext_hdr->oem_id);
20249ab747fSPaolo Bonzini         ++changed_fields;
20349ab747fSPaolo Bonzini     }
20449ab747fSPaolo Bonzini     if (hdrs->has_oem_table_id) {
20549ab747fSPaolo Bonzini         strncpy(ext_hdr->oem_table_id, hdrs->oem_table_id,
20649ab747fSPaolo Bonzini                 sizeof ext_hdr->oem_table_id);
20749ab747fSPaolo Bonzini         ++changed_fields;
20849ab747fSPaolo Bonzini     }
20949ab747fSPaolo Bonzini     if (hdrs->has_oem_rev) {
21049ab747fSPaolo Bonzini         ext_hdr->oem_revision = cpu_to_le32(hdrs->oem_rev);
21149ab747fSPaolo Bonzini         ++changed_fields;
21249ab747fSPaolo Bonzini     }
21349ab747fSPaolo Bonzini     if (hdrs->has_asl_compiler_id) {
21449ab747fSPaolo Bonzini         strncpy(ext_hdr->asl_compiler_id, hdrs->asl_compiler_id,
21549ab747fSPaolo Bonzini                 sizeof ext_hdr->asl_compiler_id);
21649ab747fSPaolo Bonzini         ++changed_fields;
21749ab747fSPaolo Bonzini     }
21849ab747fSPaolo Bonzini     if (hdrs->has_asl_compiler_rev) {
21949ab747fSPaolo Bonzini         ext_hdr->asl_compiler_revision = cpu_to_le32(hdrs->asl_compiler_rev);
22049ab747fSPaolo Bonzini         ++changed_fields;
22149ab747fSPaolo Bonzini     }
22249ab747fSPaolo Bonzini 
22349ab747fSPaolo Bonzini     if (!has_header && changed_fields == 0) {
22449ab747fSPaolo Bonzini         fprintf(stderr, "warning: ACPI table: no headers are specified\n");
22549ab747fSPaolo Bonzini     }
22649ab747fSPaolo Bonzini 
22749ab747fSPaolo Bonzini     /* recalculate checksum */
22849ab747fSPaolo Bonzini     ext_hdr->checksum = acpi_checksum((const char unsigned *)ext_hdr +
22949ab747fSPaolo Bonzini                                       ACPI_TABLE_PFX_SIZE, acpi_payload_size);
23049ab747fSPaolo Bonzini }
23149ab747fSPaolo Bonzini 
23249ab747fSPaolo Bonzini void acpi_table_add(const QemuOpts *opts, Error **errp)
23349ab747fSPaolo Bonzini {
23449ab747fSPaolo Bonzini     AcpiTableOptions *hdrs = NULL;
23549ab747fSPaolo Bonzini     Error *err = NULL;
23649ab747fSPaolo Bonzini     char **pathnames = NULL;
23749ab747fSPaolo Bonzini     char **cur;
23849ab747fSPaolo Bonzini     size_t bloblen = 0;
23949ab747fSPaolo Bonzini     char unsigned *blob = NULL;
24049ab747fSPaolo Bonzini 
24149ab747fSPaolo Bonzini     {
24209204eacSEric Blake         Visitor *v;
24349ab747fSPaolo Bonzini 
24409204eacSEric Blake         v = opts_visitor_new(opts);
24509204eacSEric Blake         visit_type_AcpiTableOptions(v, NULL, &hdrs, &err);
24609204eacSEric Blake         visit_free(v);
24749ab747fSPaolo Bonzini     }
24849ab747fSPaolo Bonzini 
24949ab747fSPaolo Bonzini     if (err) {
25049ab747fSPaolo Bonzini         goto out;
25149ab747fSPaolo Bonzini     }
25249ab747fSPaolo Bonzini     if (hdrs->has_file == hdrs->has_data) {
25349ab747fSPaolo Bonzini         error_setg(&err, "'-acpitable' requires one of 'data' or 'file'");
25449ab747fSPaolo Bonzini         goto out;
25549ab747fSPaolo Bonzini     }
25649ab747fSPaolo Bonzini 
25749ab747fSPaolo Bonzini     pathnames = g_strsplit(hdrs->has_file ? hdrs->file : hdrs->data, ":", 0);
25849ab747fSPaolo Bonzini     if (pathnames == NULL || pathnames[0] == NULL) {
25949ab747fSPaolo Bonzini         error_setg(&err, "'-acpitable' requires at least one pathname");
26049ab747fSPaolo Bonzini         goto out;
26149ab747fSPaolo Bonzini     }
26249ab747fSPaolo Bonzini 
26349ab747fSPaolo Bonzini     /* now read in the data files, reallocating buffer as needed */
26449ab747fSPaolo Bonzini     for (cur = pathnames; *cur; ++cur) {
26549ab747fSPaolo Bonzini         int fd = open(*cur, O_RDONLY | O_BINARY);
26649ab747fSPaolo Bonzini 
26749ab747fSPaolo Bonzini         if (fd < 0) {
26849ab747fSPaolo Bonzini             error_setg(&err, "can't open file %s: %s", *cur, strerror(errno));
26949ab747fSPaolo Bonzini             goto out;
27049ab747fSPaolo Bonzini         }
27149ab747fSPaolo Bonzini 
27249ab747fSPaolo Bonzini         for (;;) {
27349ab747fSPaolo Bonzini             char unsigned data[8192];
27449ab747fSPaolo Bonzini             ssize_t r;
27549ab747fSPaolo Bonzini 
27649ab747fSPaolo Bonzini             r = read(fd, data, sizeof data);
27749ab747fSPaolo Bonzini             if (r == 0) {
27849ab747fSPaolo Bonzini                 break;
27949ab747fSPaolo Bonzini             } else if (r > 0) {
28049ab747fSPaolo Bonzini                 blob = g_realloc(blob, bloblen + r);
28149ab747fSPaolo Bonzini                 memcpy(blob + bloblen, data, r);
28249ab747fSPaolo Bonzini                 bloblen += r;
28349ab747fSPaolo Bonzini             } else if (errno != EINTR) {
28449ab747fSPaolo Bonzini                 error_setg(&err, "can't read file %s: %s",
28549ab747fSPaolo Bonzini                            *cur, strerror(errno));
28649ab747fSPaolo Bonzini                 close(fd);
28749ab747fSPaolo Bonzini                 goto out;
28849ab747fSPaolo Bonzini             }
28949ab747fSPaolo Bonzini         }
29049ab747fSPaolo Bonzini 
29149ab747fSPaolo Bonzini         close(fd);
29249ab747fSPaolo Bonzini     }
29349ab747fSPaolo Bonzini 
29449ab747fSPaolo Bonzini     acpi_table_install(blob, bloblen, hdrs->has_file, hdrs, &err);
29549ab747fSPaolo Bonzini 
29649ab747fSPaolo Bonzini out:
29749ab747fSPaolo Bonzini     g_free(blob);
29849ab747fSPaolo Bonzini     g_strfreev(pathnames);
29996a1616cSEric Blake     qapi_free_AcpiTableOptions(hdrs);
30049ab747fSPaolo Bonzini 
30149ab747fSPaolo Bonzini     error_propagate(errp, err);
30249ab747fSPaolo Bonzini }
30349ab747fSPaolo Bonzini 
30460de1163SMichael S. Tsirkin static bool acpi_table_builtin = false;
30560de1163SMichael S. Tsirkin 
30660de1163SMichael S. Tsirkin void acpi_table_add_builtin(const QemuOpts *opts, Error **errp)
30760de1163SMichael S. Tsirkin {
30860de1163SMichael S. Tsirkin     acpi_table_builtin = true;
30960de1163SMichael S. Tsirkin     acpi_table_add(opts, errp);
31060de1163SMichael S. Tsirkin }
31160de1163SMichael S. Tsirkin 
31260de1163SMichael S. Tsirkin unsigned acpi_table_len(void *current)
31360de1163SMichael S. Tsirkin {
31460de1163SMichael S. Tsirkin     struct acpi_table_header *hdr = current - sizeof(hdr->_length);
31560de1163SMichael S. Tsirkin     return hdr->_length;
31660de1163SMichael S. Tsirkin }
31760de1163SMichael S. Tsirkin 
31860de1163SMichael S. Tsirkin static
31960de1163SMichael S. Tsirkin void *acpi_table_hdr(void *h)
32060de1163SMichael S. Tsirkin {
32160de1163SMichael S. Tsirkin     struct acpi_table_header *hdr = h;
32260de1163SMichael S. Tsirkin     return &hdr->sig;
32360de1163SMichael S. Tsirkin }
32460de1163SMichael S. Tsirkin 
32560de1163SMichael S. Tsirkin uint8_t *acpi_table_first(void)
32660de1163SMichael S. Tsirkin {
32760de1163SMichael S. Tsirkin     if (acpi_table_builtin || !acpi_tables) {
32860de1163SMichael S. Tsirkin         return NULL;
32960de1163SMichael S. Tsirkin     }
33060de1163SMichael S. Tsirkin     return acpi_table_hdr(acpi_tables + ACPI_TABLE_PFX_SIZE);
33160de1163SMichael S. Tsirkin }
33260de1163SMichael S. Tsirkin 
33360de1163SMichael S. Tsirkin uint8_t *acpi_table_next(uint8_t *current)
33460de1163SMichael S. Tsirkin {
33560de1163SMichael S. Tsirkin     uint8_t *next = current + acpi_table_len(current);
33660de1163SMichael S. Tsirkin 
33760de1163SMichael S. Tsirkin     if (next - acpi_tables >= acpi_tables_len) {
33860de1163SMichael S. Tsirkin         return NULL;
33960de1163SMichael S. Tsirkin     } else {
34060de1163SMichael S. Tsirkin         return acpi_table_hdr(next);
34160de1163SMichael S. Tsirkin     }
34260de1163SMichael S. Tsirkin }
34360de1163SMichael S. Tsirkin 
34488594e4fSLaszlo Ersek int acpi_get_slic_oem(AcpiSlicOem *oem)
34588594e4fSLaszlo Ersek {
34688594e4fSLaszlo Ersek     uint8_t *u;
34788594e4fSLaszlo Ersek 
34888594e4fSLaszlo Ersek     for (u = acpi_table_first(); u; u = acpi_table_next(u)) {
34988594e4fSLaszlo Ersek         struct acpi_table_header *hdr = (void *)(u - sizeof(hdr->_length));
35088594e4fSLaszlo Ersek 
35188594e4fSLaszlo Ersek         if (memcmp(hdr->sig, "SLIC", 4) == 0) {
35288594e4fSLaszlo Ersek             oem->id = hdr->oem_id;
35388594e4fSLaszlo Ersek             oem->table_id = hdr->oem_table_id;
35488594e4fSLaszlo Ersek             return 0;
35588594e4fSLaszlo Ersek         }
35688594e4fSLaszlo Ersek     }
35788594e4fSLaszlo Ersek     return -1;
35888594e4fSLaszlo Ersek }
35988594e4fSLaszlo Ersek 
36049ab747fSPaolo Bonzini static void acpi_notify_wakeup(Notifier *notifier, void *data)
36149ab747fSPaolo Bonzini {
36249ab747fSPaolo Bonzini     ACPIREGS *ar = container_of(notifier, ACPIREGS, wakeup);
36349ab747fSPaolo Bonzini     WakeupReason *reason = data;
36449ab747fSPaolo Bonzini 
36549ab747fSPaolo Bonzini     switch (*reason) {
36649ab747fSPaolo Bonzini     case QEMU_WAKEUP_REASON_RTC:
36749ab747fSPaolo Bonzini         ar->pm1.evt.sts |=
36849ab747fSPaolo Bonzini             (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_RT_CLOCK_STATUS);
36949ab747fSPaolo Bonzini         break;
37049ab747fSPaolo Bonzini     case QEMU_WAKEUP_REASON_PMTIMER:
37149ab747fSPaolo Bonzini         ar->pm1.evt.sts |=
37249ab747fSPaolo Bonzini             (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_TIMER_STATUS);
37349ab747fSPaolo Bonzini         break;
37449ab747fSPaolo Bonzini     case QEMU_WAKEUP_REASON_OTHER:
37549ab747fSPaolo Bonzini         /* ACPI_BITMASK_WAKE_STATUS should be set on resume.
37649ab747fSPaolo Bonzini            Pretend that resume was caused by power button */
37749ab747fSPaolo Bonzini         ar->pm1.evt.sts |=
37849ab747fSPaolo Bonzini             (ACPI_BITMASK_WAKE_STATUS | ACPI_BITMASK_POWER_BUTTON_STATUS);
37949ab747fSPaolo Bonzini         break;
3804bc78a87SLiu, Jinsong     default:
3814bc78a87SLiu, Jinsong         break;
38249ab747fSPaolo Bonzini     }
38349ab747fSPaolo Bonzini }
38449ab747fSPaolo Bonzini 
38549ab747fSPaolo Bonzini /* ACPI PM1a EVT */
38649ab747fSPaolo Bonzini uint16_t acpi_pm1_evt_get_sts(ACPIREGS *ar)
38749ab747fSPaolo Bonzini {
3883ef0eab1SPavel Dovgalyuk     /* Compare ns-clock, not PM timer ticks, because
3893ef0eab1SPavel Dovgalyuk        acpi_pm_tmr_update function uses ns for setting the timer. */
3903ef0eab1SPavel Dovgalyuk     int64_t d = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
3913ef0eab1SPavel Dovgalyuk     if (d >= muldiv64(ar->tmr.overflow_time,
39273bcb24dSRutuja Shah                       NANOSECONDS_PER_SECOND, PM_TIMER_FREQUENCY)) {
39349ab747fSPaolo Bonzini         ar->pm1.evt.sts |= ACPI_BITMASK_TIMER_STATUS;
39449ab747fSPaolo Bonzini     }
39549ab747fSPaolo Bonzini     return ar->pm1.evt.sts;
39649ab747fSPaolo Bonzini }
39749ab747fSPaolo Bonzini 
39849ab747fSPaolo Bonzini static void acpi_pm1_evt_write_sts(ACPIREGS *ar, uint16_t val)
39949ab747fSPaolo Bonzini {
40049ab747fSPaolo Bonzini     uint16_t pm1_sts = acpi_pm1_evt_get_sts(ar);
40149ab747fSPaolo Bonzini     if (pm1_sts & val & ACPI_BITMASK_TIMER_STATUS) {
40249ab747fSPaolo Bonzini         /* if TMRSTS is reset, then compute the new overflow time */
40349ab747fSPaolo Bonzini         acpi_pm_tmr_calc_overflow_time(ar);
40449ab747fSPaolo Bonzini     }
40549ab747fSPaolo Bonzini     ar->pm1.evt.sts &= ~val;
40649ab747fSPaolo Bonzini }
40749ab747fSPaolo Bonzini 
40849ab747fSPaolo Bonzini static void acpi_pm1_evt_write_en(ACPIREGS *ar, uint16_t val)
40949ab747fSPaolo Bonzini {
41049ab747fSPaolo Bonzini     ar->pm1.evt.en = val;
41149ab747fSPaolo Bonzini     qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_RTC,
41249ab747fSPaolo Bonzini                               val & ACPI_BITMASK_RT_CLOCK_ENABLE);
41349ab747fSPaolo Bonzini     qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_PMTIMER,
41449ab747fSPaolo Bonzini                               val & ACPI_BITMASK_TIMER_ENABLE);
41549ab747fSPaolo Bonzini }
41649ab747fSPaolo Bonzini 
41749ab747fSPaolo Bonzini void acpi_pm1_evt_power_down(ACPIREGS *ar)
41849ab747fSPaolo Bonzini {
41949ab747fSPaolo Bonzini     if (ar->pm1.evt.en & ACPI_BITMASK_POWER_BUTTON_ENABLE) {
42049ab747fSPaolo Bonzini         ar->pm1.evt.sts |= ACPI_BITMASK_POWER_BUTTON_STATUS;
42149ab747fSPaolo Bonzini         ar->tmr.update_sci(ar);
42249ab747fSPaolo Bonzini     }
42349ab747fSPaolo Bonzini }
42449ab747fSPaolo Bonzini 
42549ab747fSPaolo Bonzini void acpi_pm1_evt_reset(ACPIREGS *ar)
42649ab747fSPaolo Bonzini {
42749ab747fSPaolo Bonzini     ar->pm1.evt.sts = 0;
42849ab747fSPaolo Bonzini     ar->pm1.evt.en = 0;
42949ab747fSPaolo Bonzini     qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_RTC, 0);
43049ab747fSPaolo Bonzini     qemu_system_wakeup_enable(QEMU_WAKEUP_REASON_PMTIMER, 0);
43149ab747fSPaolo Bonzini }
43249ab747fSPaolo Bonzini 
43349ab747fSPaolo Bonzini static uint64_t acpi_pm_evt_read(void *opaque, hwaddr addr, unsigned width)
43449ab747fSPaolo Bonzini {
43549ab747fSPaolo Bonzini     ACPIREGS *ar = opaque;
43649ab747fSPaolo Bonzini     switch (addr) {
43749ab747fSPaolo Bonzini     case 0:
43849ab747fSPaolo Bonzini         return acpi_pm1_evt_get_sts(ar);
43949ab747fSPaolo Bonzini     case 2:
44049ab747fSPaolo Bonzini         return ar->pm1.evt.en;
44149ab747fSPaolo Bonzini     default:
44249ab747fSPaolo Bonzini         return 0;
44349ab747fSPaolo Bonzini     }
44449ab747fSPaolo Bonzini }
44549ab747fSPaolo Bonzini 
44649ab747fSPaolo Bonzini static void acpi_pm_evt_write(void *opaque, hwaddr addr, uint64_t val,
44749ab747fSPaolo Bonzini                               unsigned width)
44849ab747fSPaolo Bonzini {
44949ab747fSPaolo Bonzini     ACPIREGS *ar = opaque;
45049ab747fSPaolo Bonzini     switch (addr) {
45149ab747fSPaolo Bonzini     case 0:
45249ab747fSPaolo Bonzini         acpi_pm1_evt_write_sts(ar, val);
45349ab747fSPaolo Bonzini         ar->pm1.evt.update_sci(ar);
45449ab747fSPaolo Bonzini         break;
45549ab747fSPaolo Bonzini     case 2:
45649ab747fSPaolo Bonzini         acpi_pm1_evt_write_en(ar, val);
45749ab747fSPaolo Bonzini         ar->pm1.evt.update_sci(ar);
45849ab747fSPaolo Bonzini         break;
45949ab747fSPaolo Bonzini     }
46049ab747fSPaolo Bonzini }
46149ab747fSPaolo Bonzini 
46249ab747fSPaolo Bonzini static const MemoryRegionOps acpi_pm_evt_ops = {
46349ab747fSPaolo Bonzini     .read = acpi_pm_evt_read,
46449ab747fSPaolo Bonzini     .write = acpi_pm_evt_write,
46549ab747fSPaolo Bonzini     .valid.min_access_size = 2,
46649ab747fSPaolo Bonzini     .valid.max_access_size = 2,
46749ab747fSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
46849ab747fSPaolo Bonzini };
46949ab747fSPaolo Bonzini 
47049ab747fSPaolo Bonzini void acpi_pm1_evt_init(ACPIREGS *ar, acpi_update_sci_fn update_sci,
47149ab747fSPaolo Bonzini                        MemoryRegion *parent)
47249ab747fSPaolo Bonzini {
47349ab747fSPaolo Bonzini     ar->pm1.evt.update_sci = update_sci;
47464bde0f3SPaolo Bonzini     memory_region_init_io(&ar->pm1.evt.io, memory_region_owner(parent),
47564bde0f3SPaolo Bonzini                           &acpi_pm_evt_ops, ar, "acpi-evt", 4);
47649ab747fSPaolo Bonzini     memory_region_add_subregion(parent, 0, &ar->pm1.evt.io);
47749ab747fSPaolo Bonzini }
47849ab747fSPaolo Bonzini 
47949ab747fSPaolo Bonzini /* ACPI PM_TMR */
48049ab747fSPaolo Bonzini void acpi_pm_tmr_update(ACPIREGS *ar, bool enable)
48149ab747fSPaolo Bonzini {
48249ab747fSPaolo Bonzini     int64_t expire_time;
48349ab747fSPaolo Bonzini 
48449ab747fSPaolo Bonzini     /* schedule a timer interruption if needed */
48549ab747fSPaolo Bonzini     if (enable) {
48673bcb24dSRutuja Shah         expire_time = muldiv64(ar->tmr.overflow_time, NANOSECONDS_PER_SECOND,
48749ab747fSPaolo Bonzini                                PM_TIMER_FREQUENCY);
488bc72ad67SAlex Bligh         timer_mod(ar->tmr.timer, expire_time);
48949ab747fSPaolo Bonzini     } else {
490bc72ad67SAlex Bligh         timer_del(ar->tmr.timer);
49149ab747fSPaolo Bonzini     }
49249ab747fSPaolo Bonzini }
49349ab747fSPaolo Bonzini 
49487776ab7SPaolo Bonzini static inline int64_t acpi_pm_tmr_get_clock(void)
49587776ab7SPaolo Bonzini {
49687776ab7SPaolo Bonzini     return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), PM_TIMER_FREQUENCY,
49787776ab7SPaolo Bonzini                     NANOSECONDS_PER_SECOND);
49887776ab7SPaolo Bonzini }
49987776ab7SPaolo Bonzini 
50049ab747fSPaolo Bonzini void acpi_pm_tmr_calc_overflow_time(ACPIREGS *ar)
50149ab747fSPaolo Bonzini {
50249ab747fSPaolo Bonzini     int64_t d = acpi_pm_tmr_get_clock();
50349ab747fSPaolo Bonzini     ar->tmr.overflow_time = (d + 0x800000LL) & ~0x7fffffLL;
50449ab747fSPaolo Bonzini }
50549ab747fSPaolo Bonzini 
50649ab747fSPaolo Bonzini static uint32_t acpi_pm_tmr_get(ACPIREGS *ar)
50749ab747fSPaolo Bonzini {
50849ab747fSPaolo Bonzini     uint32_t d = acpi_pm_tmr_get_clock();
50949ab747fSPaolo Bonzini     return d & 0xffffff;
51049ab747fSPaolo Bonzini }
51149ab747fSPaolo Bonzini 
51249ab747fSPaolo Bonzini static void acpi_pm_tmr_timer(void *opaque)
51349ab747fSPaolo Bonzini {
51449ab747fSPaolo Bonzini     ACPIREGS *ar = opaque;
51549ab747fSPaolo Bonzini     qemu_system_wakeup_request(QEMU_WAKEUP_REASON_PMTIMER);
51649ab747fSPaolo Bonzini     ar->tmr.update_sci(ar);
51749ab747fSPaolo Bonzini }
51849ab747fSPaolo Bonzini 
51949ab747fSPaolo Bonzini static uint64_t acpi_pm_tmr_read(void *opaque, hwaddr addr, unsigned width)
52049ab747fSPaolo Bonzini {
52149ab747fSPaolo Bonzini     return acpi_pm_tmr_get(opaque);
52249ab747fSPaolo Bonzini }
52349ab747fSPaolo Bonzini 
5242d3b9895SGerd Hoffmann static void acpi_pm_tmr_write(void *opaque, hwaddr addr, uint64_t val,
5252d3b9895SGerd Hoffmann                               unsigned width)
5262d3b9895SGerd Hoffmann {
5272d3b9895SGerd Hoffmann     /* nothing */
5282d3b9895SGerd Hoffmann }
5292d3b9895SGerd Hoffmann 
53049ab747fSPaolo Bonzini static const MemoryRegionOps acpi_pm_tmr_ops = {
53149ab747fSPaolo Bonzini     .read = acpi_pm_tmr_read,
5322d3b9895SGerd Hoffmann     .write = acpi_pm_tmr_write,
53349ab747fSPaolo Bonzini     .valid.min_access_size = 4,
53449ab747fSPaolo Bonzini     .valid.max_access_size = 4,
53549ab747fSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
53649ab747fSPaolo Bonzini };
53749ab747fSPaolo Bonzini 
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 
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 */
55549ab747fSPaolo Bonzini static void acpi_pm1_cnt_write(ACPIREGS *ar, uint16_t val)
55649ab747fSPaolo Bonzini {
55749ab747fSPaolo Bonzini     ar->pm1.cnt.cnt = val & ~(ACPI_BITMASK_SLEEP_ENABLE);
55849ab747fSPaolo Bonzini 
55949ab747fSPaolo Bonzini     if (val & ACPI_BITMASK_SLEEP_ENABLE) {
56049ab747fSPaolo Bonzini         /* change suspend type */
56149ab747fSPaolo Bonzini         uint16_t sus_typ = (val >> 10) & 7;
56249ab747fSPaolo Bonzini         switch(sus_typ) {
56349ab747fSPaolo Bonzini         case 0: /* soft power off */
564*cf83f140SEric Blake             qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
56549ab747fSPaolo Bonzini             break;
56649ab747fSPaolo Bonzini         case 1:
56749ab747fSPaolo Bonzini             qemu_system_suspend_request();
56849ab747fSPaolo Bonzini             break;
56949ab747fSPaolo Bonzini         default:
57049ab747fSPaolo Bonzini             if (sus_typ == ar->pm1.cnt.s4_val) { /* S4 request */
5712ea4100fSWenchao Xia                 qapi_event_send_suspend_disk(&error_abort);
572*cf83f140SEric Blake                 qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
57349ab747fSPaolo Bonzini             }
57449ab747fSPaolo Bonzini             break;
57549ab747fSPaolo Bonzini         }
57649ab747fSPaolo Bonzini     }
57749ab747fSPaolo Bonzini }
57849ab747fSPaolo Bonzini 
57949ab747fSPaolo Bonzini void acpi_pm1_cnt_update(ACPIREGS *ar,
58049ab747fSPaolo Bonzini                          bool sci_enable, bool sci_disable)
58149ab747fSPaolo Bonzini {
58249ab747fSPaolo Bonzini     /* ACPI specs 3.0, 4.7.2.5 */
58349ab747fSPaolo Bonzini     if (sci_enable) {
58449ab747fSPaolo Bonzini         ar->pm1.cnt.cnt |= ACPI_BITMASK_SCI_ENABLE;
58549ab747fSPaolo Bonzini     } else if (sci_disable) {
58649ab747fSPaolo Bonzini         ar->pm1.cnt.cnt &= ~ACPI_BITMASK_SCI_ENABLE;
58749ab747fSPaolo Bonzini     }
58849ab747fSPaolo Bonzini }
58949ab747fSPaolo Bonzini 
59049ab747fSPaolo Bonzini static uint64_t acpi_pm_cnt_read(void *opaque, hwaddr addr, unsigned width)
59149ab747fSPaolo Bonzini {
59249ab747fSPaolo Bonzini     ACPIREGS *ar = opaque;
59349ab747fSPaolo Bonzini     return ar->pm1.cnt.cnt;
59449ab747fSPaolo Bonzini }
59549ab747fSPaolo Bonzini 
59649ab747fSPaolo Bonzini static void acpi_pm_cnt_write(void *opaque, hwaddr addr, uint64_t val,
59749ab747fSPaolo Bonzini                               unsigned width)
59849ab747fSPaolo Bonzini {
59949ab747fSPaolo Bonzini     acpi_pm1_cnt_write(opaque, val);
60049ab747fSPaolo Bonzini }
60149ab747fSPaolo Bonzini 
60249ab747fSPaolo Bonzini static const MemoryRegionOps acpi_pm_cnt_ops = {
60349ab747fSPaolo Bonzini     .read = acpi_pm_cnt_read,
60449ab747fSPaolo Bonzini     .write = acpi_pm_cnt_write,
60549ab747fSPaolo Bonzini     .valid.min_access_size = 2,
60649ab747fSPaolo Bonzini     .valid.max_access_size = 2,
60749ab747fSPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
60849ab747fSPaolo Bonzini };
60949ab747fSPaolo Bonzini 
6109a10bbb4SLaszlo Ersek void acpi_pm1_cnt_init(ACPIREGS *ar, MemoryRegion *parent,
6119a10bbb4SLaszlo Ersek                        bool disable_s3, bool disable_s4, uint8_t s4_val)
61249ab747fSPaolo Bonzini {
613e3845e7cSLaszlo Ersek     FWCfgState *fw_cfg;
614e3845e7cSLaszlo Ersek 
61549ab747fSPaolo Bonzini     ar->pm1.cnt.s4_val = s4_val;
61649ab747fSPaolo Bonzini     ar->wakeup.notify = acpi_notify_wakeup;
61749ab747fSPaolo Bonzini     qemu_register_wakeup_notifier(&ar->wakeup);
61864bde0f3SPaolo Bonzini     memory_region_init_io(&ar->pm1.cnt.io, memory_region_owner(parent),
61964bde0f3SPaolo Bonzini                           &acpi_pm_cnt_ops, ar, "acpi-cnt", 2);
62049ab747fSPaolo Bonzini     memory_region_add_subregion(parent, 4, &ar->pm1.cnt.io);
621e3845e7cSLaszlo Ersek 
622e3845e7cSLaszlo Ersek     fw_cfg = fw_cfg_find();
623e3845e7cSLaszlo Ersek     if (fw_cfg) {
624e3845e7cSLaszlo Ersek         uint8_t suspend[6] = {128, 0, 0, 129, 128, 128};
625e3845e7cSLaszlo Ersek         suspend[3] = 1 | ((!disable_s3) << 7);
626e3845e7cSLaszlo Ersek         suspend[4] = s4_val | ((!disable_s4) << 7);
627e3845e7cSLaszlo Ersek 
628e3845e7cSLaszlo Ersek         fw_cfg_add_file(fw_cfg, "etc/system-states", g_memdup(suspend, 6), 6);
629e3845e7cSLaszlo Ersek     }
63049ab747fSPaolo Bonzini }
63149ab747fSPaolo Bonzini 
63249ab747fSPaolo Bonzini void acpi_pm1_cnt_reset(ACPIREGS *ar)
63349ab747fSPaolo Bonzini {
63449ab747fSPaolo Bonzini     ar->pm1.cnt.cnt = 0;
63549ab747fSPaolo Bonzini }
63649ab747fSPaolo Bonzini 
63749ab747fSPaolo Bonzini /* ACPI GPE */
63849ab747fSPaolo Bonzini void acpi_gpe_init(ACPIREGS *ar, uint8_t len)
63949ab747fSPaolo Bonzini {
64049ab747fSPaolo Bonzini     ar->gpe.len = len;
641d9a3b33dSMichael S. Tsirkin     /* Only first len / 2 bytes are ever used,
642d9a3b33dSMichael S. Tsirkin      * but the caller in ich9.c migrates full len bytes.
643d9a3b33dSMichael S. Tsirkin      * TODO: fix ich9.c and drop the extra allocation.
644d9a3b33dSMichael S. Tsirkin      */
645d9a3b33dSMichael S. Tsirkin     ar->gpe.sts = g_malloc0(len);
646d9a3b33dSMichael S. Tsirkin     ar->gpe.en = g_malloc0(len);
64749ab747fSPaolo Bonzini }
64849ab747fSPaolo Bonzini 
64949ab747fSPaolo Bonzini void acpi_gpe_reset(ACPIREGS *ar)
65049ab747fSPaolo Bonzini {
65149ab747fSPaolo Bonzini     memset(ar->gpe.sts, 0, ar->gpe.len / 2);
65249ab747fSPaolo Bonzini     memset(ar->gpe.en, 0, ar->gpe.len / 2);
65349ab747fSPaolo Bonzini }
65449ab747fSPaolo Bonzini 
65549ab747fSPaolo Bonzini static uint8_t *acpi_gpe_ioport_get_ptr(ACPIREGS *ar, uint32_t addr)
65649ab747fSPaolo Bonzini {
65749ab747fSPaolo Bonzini     uint8_t *cur = NULL;
65849ab747fSPaolo Bonzini 
65949ab747fSPaolo Bonzini     if (addr < ar->gpe.len / 2) {
66049ab747fSPaolo Bonzini         cur = ar->gpe.sts + addr;
66149ab747fSPaolo Bonzini     } else if (addr < ar->gpe.len) {
66249ab747fSPaolo Bonzini         cur = ar->gpe.en + addr - ar->gpe.len / 2;
66349ab747fSPaolo Bonzini     } else {
66449ab747fSPaolo Bonzini         abort();
66549ab747fSPaolo Bonzini     }
66649ab747fSPaolo Bonzini 
66749ab747fSPaolo Bonzini     return cur;
66849ab747fSPaolo Bonzini }
66949ab747fSPaolo Bonzini 
67049ab747fSPaolo Bonzini void acpi_gpe_ioport_writeb(ACPIREGS *ar, uint32_t addr, uint32_t val)
67149ab747fSPaolo Bonzini {
67249ab747fSPaolo Bonzini     uint8_t *cur;
67349ab747fSPaolo Bonzini 
67449ab747fSPaolo Bonzini     cur = acpi_gpe_ioport_get_ptr(ar, addr);
67549ab747fSPaolo Bonzini     if (addr < ar->gpe.len / 2) {
67649ab747fSPaolo Bonzini         /* GPE_STS */
67749ab747fSPaolo Bonzini         *cur = (*cur) & ~val;
67849ab747fSPaolo Bonzini     } else if (addr < ar->gpe.len) {
67949ab747fSPaolo Bonzini         /* GPE_EN */
68049ab747fSPaolo Bonzini         *cur = val;
68149ab747fSPaolo Bonzini     } else {
68249ab747fSPaolo Bonzini         abort();
68349ab747fSPaolo Bonzini     }
68449ab747fSPaolo Bonzini }
68549ab747fSPaolo Bonzini 
68649ab747fSPaolo Bonzini uint32_t acpi_gpe_ioport_readb(ACPIREGS *ar, uint32_t addr)
68749ab747fSPaolo Bonzini {
68849ab747fSPaolo Bonzini     uint8_t *cur;
68949ab747fSPaolo Bonzini     uint32_t val;
69049ab747fSPaolo Bonzini 
69149ab747fSPaolo Bonzini     cur = acpi_gpe_ioport_get_ptr(ar, addr);
69249ab747fSPaolo Bonzini     val = 0;
69349ab747fSPaolo Bonzini     if (cur != NULL) {
69449ab747fSPaolo Bonzini         val = *cur;
69549ab747fSPaolo Bonzini     }
69649ab747fSPaolo Bonzini 
69749ab747fSPaolo Bonzini     return val;
69849ab747fSPaolo Bonzini }
69906313503SIgor Mammedov 
700ca9b46bcSZhu Guihua void acpi_send_gpe_event(ACPIREGS *ar, qemu_irq irq,
701eaf23bf7SIgor Mammedov                          AcpiEventStatusBits status)
702ca9b46bcSZhu Guihua {
703ca9b46bcSZhu Guihua     ar->gpe.sts[0] |= status;
704ca9b46bcSZhu Guihua     acpi_update_sci(ar, irq);
705ca9b46bcSZhu Guihua }
706ca9b46bcSZhu Guihua 
70706313503SIgor Mammedov void acpi_update_sci(ACPIREGS *regs, qemu_irq irq)
70806313503SIgor Mammedov {
70906313503SIgor Mammedov     int sci_level, pm1a_sts;
71006313503SIgor Mammedov 
71106313503SIgor Mammedov     pm1a_sts = acpi_pm1_evt_get_sts(regs);
71206313503SIgor Mammedov 
71306313503SIgor Mammedov     sci_level = ((pm1a_sts &
71406313503SIgor Mammedov                   regs->pm1.evt.en & ACPI_BITMASK_PM1_COMMON_ENABLED) != 0) ||
71506313503SIgor Mammedov                 ((regs->gpe.sts[0] & regs->gpe.en[0]) != 0);
71606313503SIgor Mammedov 
71706313503SIgor Mammedov     qemu_set_irq(irq, sci_level);
71806313503SIgor Mammedov 
71906313503SIgor Mammedov     /* schedule a timer interruption if needed */
72006313503SIgor Mammedov     acpi_pm_tmr_update(regs,
72106313503SIgor Mammedov                        (regs->pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
72206313503SIgor Mammedov                        !(pm1a_sts & ACPI_BITMASK_TIMER_STATUS));
72306313503SIgor Mammedov }
724