x86: move acpi_dev from pc/microvm
[qemu.git] / hw / i386 / acpi-microvm.c
1 /* Support for generating ACPI tables and passing them to Guests
2 *
3 * Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net>
4 * Copyright (C) 2006 Fabrice Bellard
5 * Copyright (C) 2013 Red Hat Inc
6 *
7 * Author: Michael S. Tsirkin <mst@redhat.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "qemu/osdep.h"
24 #include "qemu/cutils.h"
25 #include "qapi/error.h"
26
27 #include "exec/memory.h"
28 #include "hw/acpi/acpi.h"
29 #include "hw/acpi/aml-build.h"
30 #include "hw/acpi/bios-linker-loader.h"
31 #include "hw/acpi/generic_event_device.h"
32 #include "hw/acpi/utils.h"
33 #include "hw/boards.h"
34 #include "hw/i386/fw_cfg.h"
35 #include "hw/i386/microvm.h"
36 #include "hw/virtio/virtio-mmio.h"
37
38 #include "acpi-common.h"
39 #include "acpi-microvm.h"
40
41 static void acpi_dsdt_add_virtio(Aml *scope,
42 MicrovmMachineState *mms)
43 {
44 gchar *separator;
45 long int index;
46 BusState *bus;
47 BusChild *kid;
48
49 bus = sysbus_get_default();
50 QTAILQ_FOREACH(kid, &bus->children, sibling) {
51 DeviceState *dev = kid->child;
52 Object *obj = object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MMIO);
53
54 if (obj) {
55 VirtIOMMIOProxy *mmio = VIRTIO_MMIO(obj);
56 VirtioBusState *mmio_virtio_bus = &mmio->bus;
57 BusState *mmio_bus = &mmio_virtio_bus->parent_obj;
58
59 if (QTAILQ_EMPTY(&mmio_bus->children)) {
60 continue;
61 }
62 separator = g_strrstr(mmio_bus->name, ".");
63 if (!separator) {
64 continue;
65 }
66 if (qemu_strtol(separator + 1, NULL, 10, &index) != 0) {
67 continue;
68 }
69
70 uint32_t irq = mms->virtio_irq_base + index;
71 hwaddr base = VIRTIO_MMIO_BASE + index * 512;
72 hwaddr size = 512;
73
74 Aml *dev = aml_device("VR%02u", (unsigned)index);
75 aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0005")));
76 aml_append(dev, aml_name_decl("_UID", aml_int(index)));
77 aml_append(dev, aml_name_decl("_CCA", aml_int(1)));
78
79 Aml *crs = aml_resource_template();
80 aml_append(crs, aml_memory32_fixed(base, size, AML_READ_WRITE));
81 aml_append(crs,
82 aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
83 AML_EXCLUSIVE, &irq, 1));
84 aml_append(dev, aml_name_decl("_CRS", crs));
85 aml_append(scope, dev);
86 }
87 }
88 }
89
90 static void
91 build_dsdt_microvm(GArray *table_data, BIOSLinker *linker,
92 MicrovmMachineState *mms)
93 {
94 X86MachineState *x86ms = X86_MACHINE(mms);
95 Aml *dsdt, *sb_scope, *scope, *pkg;
96 bool ambiguous;
97 Object *isabus;
98
99 isabus = object_resolve_path_type("", TYPE_ISA_BUS, &ambiguous);
100 assert(isabus);
101 assert(!ambiguous);
102
103 dsdt = init_aml_allocator();
104
105 /* Reserve space for header */
106 acpi_data_push(dsdt->buf, sizeof(AcpiTableHeader));
107
108 sb_scope = aml_scope("_SB");
109 fw_cfg_add_acpi_dsdt(sb_scope, x86ms->fw_cfg);
110 isa_build_aml(ISA_BUS(isabus), sb_scope);
111 build_ged_aml(sb_scope, GED_DEVICE, x86ms->acpi_dev,
112 GED_MMIO_IRQ, AML_SYSTEM_MEMORY, GED_MMIO_BASE);
113 acpi_dsdt_add_power_button(sb_scope);
114 acpi_dsdt_add_virtio(sb_scope, mms);
115 aml_append(dsdt, sb_scope);
116
117 /* ACPI 5.0: Table 7-209 System State Package */
118 scope = aml_scope("\\");
119 pkg = aml_package(4);
120 aml_append(pkg, aml_int(ACPI_GED_SLP_TYP_S5));
121 aml_append(pkg, aml_int(0)); /* ignored */
122 aml_append(pkg, aml_int(0)); /* reserved */
123 aml_append(pkg, aml_int(0)); /* reserved */
124 aml_append(scope, aml_name_decl("_S5", pkg));
125 aml_append(dsdt, scope);
126
127 /* copy AML table into ACPI tables blob and patch header there */
128 g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len);
129 build_header(linker, table_data,
130 (void *)(table_data->data + table_data->len - dsdt->buf->len),
131 "DSDT", dsdt->buf->len, 2, NULL, NULL);
132 free_aml_allocator();
133 }
134
135 static void acpi_build_microvm(AcpiBuildTables *tables,
136 MicrovmMachineState *mms)
137 {
138 MachineState *machine = MACHINE(mms);
139 X86MachineState *x86ms = X86_MACHINE(mms);
140 GArray *table_offsets;
141 GArray *tables_blob = tables->table_data;
142 unsigned dsdt, xsdt;
143 AcpiFadtData pmfadt = {
144 /* ACPI 5.0: 4.1 Hardware-Reduced ACPI */
145 .rev = 5,
146 .flags = ((1 << ACPI_FADT_F_HW_REDUCED_ACPI) |
147 (1 << ACPI_FADT_F_RESET_REG_SUP)),
148
149 /* ACPI 5.0: 4.8.3.7 Sleep Control and Status Registers */
150 .sleep_ctl = {
151 .space_id = AML_AS_SYSTEM_MEMORY,
152 .bit_width = 8,
153 .address = GED_MMIO_BASE_REGS + ACPI_GED_REG_SLEEP_CTL,
154 },
155 .sleep_sts = {
156 .space_id = AML_AS_SYSTEM_MEMORY,
157 .bit_width = 8,
158 .address = GED_MMIO_BASE_REGS + ACPI_GED_REG_SLEEP_STS,
159 },
160
161 /* ACPI 5.0: 4.8.3.6 Reset Register */
162 .reset_reg = {
163 .space_id = AML_AS_SYSTEM_MEMORY,
164 .bit_width = 8,
165 .address = GED_MMIO_BASE_REGS + ACPI_GED_REG_RESET,
166 },
167 .reset_val = ACPI_GED_RESET_VALUE,
168 };
169
170 table_offsets = g_array_new(false, true /* clear */,
171 sizeof(uint32_t));
172 bios_linker_loader_alloc(tables->linker,
173 ACPI_BUILD_TABLE_FILE, tables_blob,
174 64 /* Ensure FACS is aligned */,
175 false /* high memory */);
176
177 dsdt = tables_blob->len;
178 build_dsdt_microvm(tables_blob, tables->linker, mms);
179
180 pmfadt.dsdt_tbl_offset = &dsdt;
181 pmfadt.xdsdt_tbl_offset = &dsdt;
182 acpi_add_table(table_offsets, tables_blob);
183 build_fadt(tables_blob, tables->linker, &pmfadt, NULL, NULL);
184
185 acpi_add_table(table_offsets, tables_blob);
186 acpi_build_madt(tables_blob, tables->linker, X86_MACHINE(machine),
187 ACPI_DEVICE_IF(x86ms->acpi_dev), false);
188
189 xsdt = tables_blob->len;
190 build_xsdt(tables_blob, tables->linker, table_offsets, NULL, NULL);
191
192 /* RSDP is in FSEG memory, so allocate it separately */
193 {
194 AcpiRsdpData rsdp_data = {
195 /* ACPI 2.0: 5.2.4.3 RSDP Structure */
196 .revision = 2, /* xsdt needs v2 */
197 .oem_id = ACPI_BUILD_APPNAME6,
198 .xsdt_tbl_offset = &xsdt,
199 .rsdt_tbl_offset = NULL,
200 };
201 build_rsdp(tables->rsdp, tables->linker, &rsdp_data);
202 }
203
204 /* Cleanup memory that's no longer used. */
205 g_array_free(table_offsets, true);
206 }
207
208 static void acpi_build_no_update(void *build_opaque)
209 {
210 /* nothing, microvm tables don't change at runtime */
211 }
212
213 void acpi_setup_microvm(MicrovmMachineState *mms)
214 {
215 X86MachineState *x86ms = X86_MACHINE(mms);
216 AcpiBuildTables tables;
217
218 assert(x86ms->fw_cfg);
219
220 if (!x86_machine_is_acpi_enabled(x86ms)) {
221 return;
222 }
223
224 acpi_build_tables_init(&tables);
225 acpi_build_microvm(&tables, mms);
226
227 /* Now expose it all to Guest */
228 acpi_add_rom_blob(acpi_build_no_update, NULL,
229 tables.table_data,
230 ACPI_BUILD_TABLE_FILE,
231 ACPI_BUILD_TABLE_MAX_SIZE);
232 acpi_add_rom_blob(acpi_build_no_update, NULL,
233 tables.linker->cmd_blob,
234 "etc/table-loader", 0);
235 acpi_add_rom_blob(acpi_build_no_update, NULL,
236 tables.rsdp,
237 ACPI_BUILD_RSDP_FILE, 0);
238
239 acpi_build_tables_cleanup(&tables, false);
240 }