]>
Commit | Line | Data |
---|---|---|
8045df14 GH |
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" | |
3b98c65f | 24 | #include "qemu/cutils.h" |
8045df14 GH |
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" | |
8486f12f | 33 | #include "hw/acpi/erst.h" |
8045df14 GH |
34 | #include "hw/i386/fw_cfg.h" |
35 | #include "hw/i386/microvm.h" | |
24db877a GH |
36 | #include "hw/pci/pci.h" |
37 | #include "hw/pci/pcie_host.h" | |
d4a42e85 | 38 | #include "hw/usb/xhci.h" |
3b98c65f | 39 | #include "hw/virtio/virtio-mmio.h" |
128e050d | 40 | #include "hw/input/i8042.h" |
8045df14 GH |
41 | |
42 | #include "acpi-common.h" | |
43 | #include "acpi-microvm.h" | |
44 | ||
8486f12f ED |
45 | #include CONFIG_DEVICES |
46 | ||
3b98c65f GH |
47 | static void acpi_dsdt_add_virtio(Aml *scope, |
48 | MicrovmMachineState *mms) | |
49 | { | |
50 | gchar *separator; | |
51 | long int index; | |
52 | BusState *bus; | |
53 | BusChild *kid; | |
54 | ||
55 | bus = sysbus_get_default(); | |
56 | QTAILQ_FOREACH(kid, &bus->children, sibling) { | |
57 | DeviceState *dev = kid->child; | |
58 | Object *obj = object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MMIO); | |
59 | ||
60 | if (obj) { | |
61 | VirtIOMMIOProxy *mmio = VIRTIO_MMIO(obj); | |
62 | VirtioBusState *mmio_virtio_bus = &mmio->bus; | |
63 | BusState *mmio_bus = &mmio_virtio_bus->parent_obj; | |
64 | ||
65 | if (QTAILQ_EMPTY(&mmio_bus->children)) { | |
66 | continue; | |
67 | } | |
68 | separator = g_strrstr(mmio_bus->name, "."); | |
69 | if (!separator) { | |
70 | continue; | |
71 | } | |
72 | if (qemu_strtol(separator + 1, NULL, 10, &index) != 0) { | |
73 | continue; | |
74 | } | |
75 | ||
76 | uint32_t irq = mms->virtio_irq_base + index; | |
77 | hwaddr base = VIRTIO_MMIO_BASE + index * 512; | |
78 | hwaddr size = 512; | |
79 | ||
80 | Aml *dev = aml_device("VR%02u", (unsigned)index); | |
81 | aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0005"))); | |
82 | aml_append(dev, aml_name_decl("_UID", aml_int(index))); | |
83 | aml_append(dev, aml_name_decl("_CCA", aml_int(1))); | |
84 | ||
85 | Aml *crs = aml_resource_template(); | |
86 | aml_append(crs, aml_memory32_fixed(base, size, AML_READ_WRITE)); | |
87 | aml_append(crs, | |
88 | aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, | |
89 | AML_EXCLUSIVE, &irq, 1)); | |
90 | aml_append(dev, aml_name_decl("_CRS", crs)); | |
91 | aml_append(scope, dev); | |
92 | } | |
93 | } | |
94 | } | |
95 | ||
d4a42e85 GH |
96 | static void acpi_dsdt_add_xhci(Aml *scope, MicrovmMachineState *mms) |
97 | { | |
98 | if (machine_usb(MACHINE(mms))) { | |
99 | xhci_sysbus_build_aml(scope, MICROVM_XHCI_BASE, MICROVM_XHCI_IRQ); | |
100 | } | |
101 | } | |
102 | ||
24db877a GH |
103 | static void acpi_dsdt_add_pci(Aml *scope, MicrovmMachineState *mms) |
104 | { | |
105 | if (mms->pcie != ON_OFF_AUTO_ON) { | |
106 | return; | |
107 | } | |
108 | ||
109 | acpi_dsdt_add_gpex(scope, &mms->gpex); | |
110 | } | |
111 | ||
8045df14 GH |
112 | static void |
113 | build_dsdt_microvm(GArray *table_data, BIOSLinker *linker, | |
114 | MicrovmMachineState *mms) | |
115 | { | |
116 | X86MachineState *x86ms = X86_MACHINE(mms); | |
117 | Aml *dsdt, *sb_scope, *scope, *pkg; | |
118 | bool ambiguous; | |
119 | Object *isabus; | |
8f20f9a7 IM |
120 | AcpiTable table = { .sig = "DSDT", .rev = 2, .oem_id = x86ms->oem_id, |
121 | .oem_table_id = x86ms->oem_table_id }; | |
8045df14 GH |
122 | |
123 | isabus = object_resolve_path_type("", TYPE_ISA_BUS, &ambiguous); | |
124 | assert(isabus); | |
125 | assert(!ambiguous); | |
126 | ||
8f20f9a7 | 127 | acpi_table_begin(&table, table_data); |
8045df14 GH |
128 | dsdt = init_aml_allocator(); |
129 | ||
8045df14 GH |
130 | sb_scope = aml_scope("_SB"); |
131 | fw_cfg_add_acpi_dsdt(sb_scope, x86ms->fw_cfg); | |
132 | isa_build_aml(ISA_BUS(isabus), sb_scope); | |
50aef131 | 133 | build_ged_aml(sb_scope, GED_DEVICE, x86ms->acpi_dev, |
8045df14 GH |
134 | GED_MMIO_IRQ, AML_SYSTEM_MEMORY, GED_MMIO_BASE); |
135 | acpi_dsdt_add_power_button(sb_scope); | |
3b98c65f | 136 | acpi_dsdt_add_virtio(sb_scope, mms); |
d4a42e85 | 137 | acpi_dsdt_add_xhci(sb_scope, mms); |
24db877a | 138 | acpi_dsdt_add_pci(sb_scope, mms); |
8045df14 GH |
139 | aml_append(dsdt, sb_scope); |
140 | ||
141 | /* ACPI 5.0: Table 7-209 System State Package */ | |
142 | scope = aml_scope("\\"); | |
143 | pkg = aml_package(4); | |
144 | aml_append(pkg, aml_int(ACPI_GED_SLP_TYP_S5)); | |
145 | aml_append(pkg, aml_int(0)); /* ignored */ | |
146 | aml_append(pkg, aml_int(0)); /* reserved */ | |
147 | aml_append(pkg, aml_int(0)); /* reserved */ | |
148 | aml_append(scope, aml_name_decl("_S5", pkg)); | |
149 | aml_append(dsdt, scope); | |
150 | ||
8f20f9a7 | 151 | /* copy AML bytecode into ACPI tables blob */ |
8045df14 | 152 | g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len); |
8f20f9a7 IM |
153 | |
154 | acpi_table_end(linker, &table); | |
8045df14 GH |
155 | free_aml_allocator(); |
156 | } | |
157 | ||
158 | static void acpi_build_microvm(AcpiBuildTables *tables, | |
159 | MicrovmMachineState *mms) | |
160 | { | |
161 | MachineState *machine = MACHINE(mms); | |
50aef131 | 162 | X86MachineState *x86ms = X86_MACHINE(mms); |
8045df14 GH |
163 | GArray *table_offsets; |
164 | GArray *tables_blob = tables->table_data; | |
165 | unsigned dsdt, xsdt; | |
166 | AcpiFadtData pmfadt = { | |
167 | /* ACPI 5.0: 4.1 Hardware-Reduced ACPI */ | |
168 | .rev = 5, | |
169 | .flags = ((1 << ACPI_FADT_F_HW_REDUCED_ACPI) | | |
170 | (1 << ACPI_FADT_F_RESET_REG_SUP)), | |
171 | ||
172 | /* ACPI 5.0: 4.8.3.7 Sleep Control and Status Registers */ | |
173 | .sleep_ctl = { | |
174 | .space_id = AML_AS_SYSTEM_MEMORY, | |
175 | .bit_width = 8, | |
176 | .address = GED_MMIO_BASE_REGS + ACPI_GED_REG_SLEEP_CTL, | |
177 | }, | |
178 | .sleep_sts = { | |
179 | .space_id = AML_AS_SYSTEM_MEMORY, | |
180 | .bit_width = 8, | |
181 | .address = GED_MMIO_BASE_REGS + ACPI_GED_REG_SLEEP_STS, | |
182 | }, | |
183 | ||
184 | /* ACPI 5.0: 4.8.3.6 Reset Register */ | |
185 | .reset_reg = { | |
186 | .space_id = AML_AS_SYSTEM_MEMORY, | |
187 | .bit_width = 8, | |
188 | .address = GED_MMIO_BASE_REGS + ACPI_GED_REG_RESET, | |
189 | }, | |
190 | .reset_val = ACPI_GED_RESET_VALUE, | |
128e050d AS |
191 | /* |
192 | * ACPI v2, Table 5-10 - Fixed ACPI Description Table Boot Architecture | |
193 | * Flags, bit offset 1 - 8042. | |
194 | */ | |
195 | .iapc_boot_arch = iapc_boot_arch_8042(), | |
8045df14 GH |
196 | }; |
197 | ||
198 | table_offsets = g_array_new(false, true /* clear */, | |
199 | sizeof(uint32_t)); | |
200 | bios_linker_loader_alloc(tables->linker, | |
201 | ACPI_BUILD_TABLE_FILE, tables_blob, | |
202 | 64 /* Ensure FACS is aligned */, | |
203 | false /* high memory */); | |
204 | ||
205 | dsdt = tables_blob->len; | |
206 | build_dsdt_microvm(tables_blob, tables->linker, mms); | |
207 | ||
208 | pmfadt.dsdt_tbl_offset = &dsdt; | |
209 | pmfadt.xdsdt_tbl_offset = &dsdt; | |
210 | acpi_add_table(table_offsets, tables_blob); | |
d07b2286 MP |
211 | build_fadt(tables_blob, tables->linker, &pmfadt, x86ms->oem_id, |
212 | x86ms->oem_table_id); | |
8045df14 GH |
213 | |
214 | acpi_add_table(table_offsets, tables_blob); | |
215 | acpi_build_madt(tables_blob, tables->linker, X86_MACHINE(machine), | |
d07b2286 MP |
216 | ACPI_DEVICE_IF(x86ms->acpi_dev), x86ms->oem_id, |
217 | x86ms->oem_table_id); | |
8045df14 | 218 | |
8486f12f ED |
219 | #ifdef CONFIG_ACPI_ERST |
220 | { | |
221 | Object *erst_dev; | |
222 | erst_dev = find_erst_dev(); | |
223 | if (erst_dev) { | |
224 | acpi_add_table(table_offsets, tables_blob); | |
225 | build_erst(tables_blob, tables->linker, erst_dev, | |
226 | x86ms->oem_id, x86ms->oem_table_id); | |
227 | } | |
228 | } | |
229 | #endif | |
230 | ||
8045df14 | 231 | xsdt = tables_blob->len; |
d07b2286 MP |
232 | build_xsdt(tables_blob, tables->linker, table_offsets, x86ms->oem_id, |
233 | x86ms->oem_table_id); | |
8045df14 GH |
234 | |
235 | /* RSDP is in FSEG memory, so allocate it separately */ | |
236 | { | |
237 | AcpiRsdpData rsdp_data = { | |
238 | /* ACPI 2.0: 5.2.4.3 RSDP Structure */ | |
239 | .revision = 2, /* xsdt needs v2 */ | |
d07b2286 | 240 | .oem_id = x86ms->oem_id, |
8045df14 GH |
241 | .xsdt_tbl_offset = &xsdt, |
242 | .rsdt_tbl_offset = NULL, | |
243 | }; | |
244 | build_rsdp(tables->rsdp, tables->linker, &rsdp_data); | |
245 | } | |
246 | ||
247 | /* Cleanup memory that's no longer used. */ | |
248 | g_array_free(table_offsets, true); | |
249 | } | |
250 | ||
251 | static void acpi_build_no_update(void *build_opaque) | |
252 | { | |
253 | /* nothing, microvm tables don't change at runtime */ | |
254 | } | |
255 | ||
256 | void acpi_setup_microvm(MicrovmMachineState *mms) | |
257 | { | |
258 | X86MachineState *x86ms = X86_MACHINE(mms); | |
259 | AcpiBuildTables tables; | |
260 | ||
261 | assert(x86ms->fw_cfg); | |
262 | ||
263 | if (!x86_machine_is_acpi_enabled(x86ms)) { | |
264 | return; | |
265 | } | |
266 | ||
267 | acpi_build_tables_init(&tables); | |
268 | acpi_build_microvm(&tables, mms); | |
269 | ||
270 | /* Now expose it all to Guest */ | |
6930ba0d DH |
271 | acpi_add_rom_blob(acpi_build_no_update, NULL, tables.table_data, |
272 | ACPI_BUILD_TABLE_FILE); | |
273 | acpi_add_rom_blob(acpi_build_no_update, NULL, tables.linker->cmd_blob, | |
274 | ACPI_BUILD_LOADER_FILE); | |
275 | acpi_add_rom_blob(acpi_build_no_update, NULL, tables.rsdp, | |
276 | ACPI_BUILD_RSDP_FILE); | |
8045df14 GH |
277 | |
278 | acpi_build_tables_cleanup(&tables, false); | |
279 | } |