xref: /qemu/hw/riscv/virt-acpi-build.c (revision 651ccdfa)
1 /*
2  * Support for generating ACPI tables and passing them to Guests
3  *
4  * RISC-V virt ACPI generation
5  *
6  * Copyright (C) 2008-2010  Kevin O'Connor <kevin@koconnor.net>
7  * Copyright (C) 2006 Fabrice Bellard
8  * Copyright (C) 2013 Red Hat Inc
9  * Copyright (c) 2015 HUAWEI TECHNOLOGIES CO.,LTD.
10  * Copyright (C) 2021-2023 Ventana Micro Systems Inc
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21 
22  * You should have received a copy of the GNU General Public License along
23  * with this program; if not, see <http://www.gnu.org/licenses/>.
24  */
25 
26 #include "qemu/osdep.h"
27 #include "hw/acpi/acpi-defs.h"
28 #include "hw/acpi/acpi.h"
29 #include "hw/acpi/aml-build.h"
30 #include "hw/acpi/utils.h"
31 #include "qapi/error.h"
32 #include "sysemu/reset.h"
33 #include "migration/vmstate.h"
34 #include "hw/riscv/virt.h"
35 #include "hw/riscv/numa.h"
36 #include "hw/intc/riscv_aclint.h"
37 
38 #define ACPI_BUILD_TABLE_SIZE             0x20000
39 
40 typedef struct AcpiBuildState {
41     /* Copy of table in RAM (for patching) */
42     MemoryRegion *table_mr;
43     MemoryRegion *rsdp_mr;
44     MemoryRegion *linker_mr;
45     /* Is table patched? */
46     bool patched;
47 } AcpiBuildState;
48 
49 static void acpi_align_size(GArray *blob, unsigned align)
50 {
51     /*
52      * Align size to multiple of given size. This reduces the chance
53      * we need to change size in the future (breaking cross version migration).
54      */
55     g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align));
56 }
57 
58 static void riscv_acpi_madt_add_rintc(uint32_t uid,
59                                       const CPUArchIdList *arch_ids,
60                                       GArray *entry)
61 {
62     uint64_t hart_id = arch_ids->cpus[uid].arch_id;
63 
64     build_append_int_noprefix(entry, 0x18, 1);       /* Type     */
65     build_append_int_noprefix(entry, 20, 1);         /* Length   */
66     build_append_int_noprefix(entry, 1, 1);          /* Version  */
67     build_append_int_noprefix(entry, 0, 1);          /* Reserved */
68     build_append_int_noprefix(entry, 0x1, 4);        /* Flags    */
69     build_append_int_noprefix(entry, hart_id, 8);    /* Hart ID  */
70     build_append_int_noprefix(entry, uid, 4);        /* ACPI Processor UID */
71 }
72 
73 static void acpi_dsdt_add_cpus(Aml *scope, RISCVVirtState *s)
74 {
75     MachineClass *mc = MACHINE_GET_CLASS(s);
76     MachineState *ms = MACHINE(s);
77     const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms);
78 
79     for (int i = 0; i < arch_ids->len; i++) {
80             Aml *dev;
81             GArray *madt_buf = g_array_new(0, 1, 1);
82 
83             dev = aml_device("C%.03X", i);
84             aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0007")));
85             aml_append(dev, aml_name_decl("_UID",
86                        aml_int(arch_ids->cpus[i].arch_id)));
87 
88             /* build _MAT object */
89             riscv_acpi_madt_add_rintc(i, arch_ids, madt_buf);
90             aml_append(dev, aml_name_decl("_MAT",
91                                           aml_buffer(madt_buf->len,
92                                           (uint8_t *)madt_buf->data)));
93             g_array_free(madt_buf, true);
94 
95             aml_append(scope, dev);
96     }
97 }
98 
99 static void acpi_dsdt_add_fw_cfg(Aml *scope, const MemMapEntry *fw_cfg_memmap)
100 {
101     Aml *dev = aml_device("FWCF");
102     aml_append(dev, aml_name_decl("_HID", aml_string("QEMU0002")));
103 
104     /* device present, functioning, decoding, not shown in UI */
105     aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
106     aml_append(dev, aml_name_decl("_CCA", aml_int(1)));
107 
108     Aml *crs = aml_resource_template();
109     aml_append(crs, aml_memory32_fixed(fw_cfg_memmap->base,
110                                        fw_cfg_memmap->size, AML_READ_WRITE));
111     aml_append(dev, aml_name_decl("_CRS", crs));
112     aml_append(scope, dev);
113 }
114 
115 /* RHCT Node[N] starts at offset 56 */
116 #define RHCT_NODE_ARRAY_OFFSET 56
117 
118 /*
119  * ACPI spec, Revision 6.5+
120  * 5.2.36 RISC-V Hart Capabilities Table (RHCT)
121  * REF: https://github.com/riscv-non-isa/riscv-acpi/issues/16
122  *      https://drive.google.com/file/d/1nP3nFiH4jkPMp6COOxP6123DCZKR-tia/view
123  */
124 static void build_rhct(GArray *table_data,
125                        BIOSLinker *linker,
126                        RISCVVirtState *s)
127 {
128     MachineClass *mc = MACHINE_GET_CLASS(s);
129     MachineState *ms = MACHINE(s);
130     const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms);
131     size_t len, aligned_len;
132     uint32_t isa_offset, num_rhct_nodes;
133     RISCVCPU *cpu;
134     char *isa;
135 
136     AcpiTable table = { .sig = "RHCT", .rev = 1, .oem_id = s->oem_id,
137                         .oem_table_id = s->oem_table_id };
138 
139     acpi_table_begin(&table, table_data);
140 
141     build_append_int_noprefix(table_data, 0x0, 4);   /* Reserved */
142 
143     /* Time Base Frequency */
144     build_append_int_noprefix(table_data,
145                               RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, 8);
146 
147     /* ISA + N hart info */
148     num_rhct_nodes = 1 + ms->smp.cpus;
149 
150     /* Number of RHCT nodes*/
151     build_append_int_noprefix(table_data, num_rhct_nodes, 4);
152 
153     /* Offset to the RHCT node array */
154     build_append_int_noprefix(table_data, RHCT_NODE_ARRAY_OFFSET, 4);
155 
156     /* ISA String Node */
157     isa_offset = table_data->len - table.table_offset;
158     build_append_int_noprefix(table_data, 0, 2);   /* Type 0 */
159 
160     cpu = &s->soc[0].harts[0];
161     isa = riscv_isa_string(cpu);
162     len = 8 + strlen(isa) + 1;
163     aligned_len = (len % 2) ? (len + 1) : len;
164 
165     build_append_int_noprefix(table_data, aligned_len, 2);   /* Length */
166     build_append_int_noprefix(table_data, 0x1, 2);           /* Revision */
167 
168     /* ISA string length including NUL */
169     build_append_int_noprefix(table_data, strlen(isa) + 1, 2);
170     g_array_append_vals(table_data, isa, strlen(isa) + 1);   /* ISA string */
171 
172     if (aligned_len != len) {
173         build_append_int_noprefix(table_data, 0x0, 1);   /* Optional Padding */
174     }
175 
176     /* Hart Info Node */
177     for (int i = 0; i < arch_ids->len; i++) {
178         build_append_int_noprefix(table_data, 0xFFFF, 2);  /* Type */
179         build_append_int_noprefix(table_data, 16, 2);      /* Length */
180         build_append_int_noprefix(table_data, 0x1, 2);     /* Revision */
181         build_append_int_noprefix(table_data, 1, 2);    /* Number of offsets */
182         build_append_int_noprefix(table_data, i, 4);    /* ACPI Processor UID */
183         build_append_int_noprefix(table_data, isa_offset, 4); /* Offsets[0] */
184     }
185 
186     acpi_table_end(linker, &table);
187 }
188 
189 /* FADT */
190 static void build_fadt_rev6(GArray *table_data,
191                             BIOSLinker *linker,
192                             RISCVVirtState *s,
193                             unsigned dsdt_tbl_offset)
194 {
195     AcpiFadtData fadt = {
196         .rev = 6,
197         .minor_ver = 5,
198         .flags = 1 << ACPI_FADT_F_HW_REDUCED_ACPI,
199         .xdsdt_tbl_offset = &dsdt_tbl_offset,
200     };
201 
202     build_fadt(table_data, linker, &fadt, s->oem_id, s->oem_table_id);
203 }
204 
205 /* DSDT */
206 static void build_dsdt(GArray *table_data,
207                        BIOSLinker *linker,
208                        RISCVVirtState *s)
209 {
210     Aml *scope, *dsdt;
211     const MemMapEntry *memmap = s->memmap;
212     AcpiTable table = { .sig = "DSDT", .rev = 2, .oem_id = s->oem_id,
213                         .oem_table_id = s->oem_table_id };
214 
215 
216     acpi_table_begin(&table, table_data);
217     dsdt = init_aml_allocator();
218 
219     /*
220      * When booting the VM with UEFI, UEFI takes ownership of the RTC hardware.
221      * While UEFI can use libfdt to disable the RTC device node in the DTB that
222      * it passes to the OS, it cannot modify AML. Therefore, we won't generate
223      * the RTC ACPI device at all when using UEFI.
224      */
225     scope = aml_scope("\\_SB");
226     acpi_dsdt_add_cpus(scope, s);
227 
228     acpi_dsdt_add_fw_cfg(scope, &memmap[VIRT_FW_CFG]);
229 
230     aml_append(dsdt, scope);
231 
232     /* copy AML table into ACPI tables blob and patch header there */
233     g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len);
234 
235     acpi_table_end(linker, &table);
236     free_aml_allocator();
237 }
238 
239 /*
240  * ACPI spec, Revision 6.5+
241  * 5.2.12 Multiple APIC Description Table (MADT)
242  * REF: https://github.com/riscv-non-isa/riscv-acpi/issues/15
243  *      https://drive.google.com/file/d/1R6k4MshhN3WTT-hwqAquu5nX6xSEqK2l/view
244  */
245 static void build_madt(GArray *table_data,
246                        BIOSLinker *linker,
247                        RISCVVirtState *s)
248 {
249     MachineClass *mc = MACHINE_GET_CLASS(s);
250     MachineState *ms = MACHINE(s);
251     const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms);
252 
253     AcpiTable table = { .sig = "APIC", .rev = 6, .oem_id = s->oem_id,
254                         .oem_table_id = s->oem_table_id };
255 
256     acpi_table_begin(&table, table_data);
257     /* Local Interrupt Controller Address */
258     build_append_int_noprefix(table_data, 0, 4);
259     build_append_int_noprefix(table_data, 0, 4);   /* MADT Flags */
260 
261     /* RISC-V Local INTC structures per HART */
262     for (int i = 0; i < arch_ids->len; i++) {
263         riscv_acpi_madt_add_rintc(i, arch_ids, table_data);
264     }
265 
266     acpi_table_end(linker, &table);
267 }
268 
269 static void virt_acpi_build(RISCVVirtState *s, AcpiBuildTables *tables)
270 {
271     GArray *table_offsets;
272     unsigned dsdt, xsdt;
273     GArray *tables_blob = tables->table_data;
274 
275     table_offsets = g_array_new(false, true,
276                                 sizeof(uint32_t));
277 
278     bios_linker_loader_alloc(tables->linker,
279                              ACPI_BUILD_TABLE_FILE, tables_blob,
280                              64, false);
281 
282     /* DSDT is pointed to by FADT */
283     dsdt = tables_blob->len;
284     build_dsdt(tables_blob, tables->linker, s);
285 
286     /* FADT and others pointed to by XSDT */
287     acpi_add_table(table_offsets, tables_blob);
288     build_fadt_rev6(tables_blob, tables->linker, s, dsdt);
289 
290     acpi_add_table(table_offsets, tables_blob);
291     build_madt(tables_blob, tables->linker, s);
292 
293     acpi_add_table(table_offsets, tables_blob);
294     build_rhct(tables_blob, tables->linker, s);
295 
296     /* XSDT is pointed to by RSDP */
297     xsdt = tables_blob->len;
298     build_xsdt(tables_blob, tables->linker, table_offsets, s->oem_id,
299                 s->oem_table_id);
300 
301     /* RSDP is in FSEG memory, so allocate it separately */
302     {
303         AcpiRsdpData rsdp_data = {
304             .revision = 2,
305             .oem_id = s->oem_id,
306             .xsdt_tbl_offset = &xsdt,
307             .rsdt_tbl_offset = NULL,
308         };
309         build_rsdp(tables->rsdp, tables->linker, &rsdp_data);
310     }
311 
312     /*
313      * The align size is 128, warn if 64k is not enough therefore
314      * the align size could be resized.
315      */
316     if (tables_blob->len > ACPI_BUILD_TABLE_SIZE / 2) {
317         warn_report("ACPI table size %u exceeds %d bytes,"
318                     " migration may not work",
319                     tables_blob->len, ACPI_BUILD_TABLE_SIZE / 2);
320         error_printf("Try removing some objects.");
321     }
322 
323     acpi_align_size(tables_blob, ACPI_BUILD_TABLE_SIZE);
324 
325     /* Clean up memory that's no longer used */
326     g_array_free(table_offsets, true);
327 }
328 
329 static void acpi_ram_update(MemoryRegion *mr, GArray *data)
330 {
331     uint32_t size = acpi_data_len(data);
332 
333     /*
334      * Make sure RAM size is correct - in case it got changed
335      * e.g. by migration
336      */
337     memory_region_ram_resize(mr, size, &error_abort);
338 
339     memcpy(memory_region_get_ram_ptr(mr), data->data, size);
340     memory_region_set_dirty(mr, 0, size);
341 }
342 
343 static void virt_acpi_build_update(void *build_opaque)
344 {
345     AcpiBuildState *build_state = build_opaque;
346     AcpiBuildTables tables;
347 
348     /* No state to update or already patched? Nothing to do. */
349     if (!build_state || build_state->patched) {
350         return;
351     }
352 
353     build_state->patched = true;
354 
355     acpi_build_tables_init(&tables);
356 
357     virt_acpi_build(RISCV_VIRT_MACHINE(qdev_get_machine()), &tables);
358 
359     acpi_ram_update(build_state->table_mr, tables.table_data);
360     acpi_ram_update(build_state->rsdp_mr, tables.rsdp);
361     acpi_ram_update(build_state->linker_mr, tables.linker->cmd_blob);
362 
363     acpi_build_tables_cleanup(&tables, true);
364 }
365 
366 static void virt_acpi_build_reset(void *build_opaque)
367 {
368     AcpiBuildState *build_state = build_opaque;
369     build_state->patched = false;
370 }
371 
372 static const VMStateDescription vmstate_virt_acpi_build = {
373     .name = "virt_acpi_build",
374     .version_id = 1,
375     .minimum_version_id = 1,
376     .fields = (VMStateField[]) {
377         VMSTATE_BOOL(patched, AcpiBuildState),
378         VMSTATE_END_OF_LIST()
379     },
380 };
381 
382 void virt_acpi_setup(RISCVVirtState *s)
383 {
384     AcpiBuildTables tables;
385     AcpiBuildState *build_state;
386 
387     build_state = g_malloc0(sizeof *build_state);
388 
389     acpi_build_tables_init(&tables);
390     virt_acpi_build(s, &tables);
391 
392     /* Now expose it all to Guest */
393     build_state->table_mr = acpi_add_rom_blob(virt_acpi_build_update,
394                                               build_state, tables.table_data,
395                                               ACPI_BUILD_TABLE_FILE);
396     assert(build_state->table_mr != NULL);
397 
398     build_state->linker_mr = acpi_add_rom_blob(virt_acpi_build_update,
399                                                build_state,
400                                                tables.linker->cmd_blob,
401                                                ACPI_BUILD_LOADER_FILE);
402 
403     build_state->rsdp_mr = acpi_add_rom_blob(virt_acpi_build_update,
404                                              build_state, tables.rsdp,
405                                              ACPI_BUILD_RSDP_FILE);
406 
407     qemu_register_reset(virt_acpi_build_reset, build_state);
408     virt_acpi_build_reset(build_state);
409     vmstate_register(NULL, 0, &vmstate_virt_acpi_build, build_state);
410 
411     /*
412      * Clean up tables but don't free the memory: we track it
413      * in build_state.
414      */
415     acpi_build_tables_cleanup(&tables, false);
416 }
417