1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Handles writing the declared ACPI tables
4 *
5 * Copyright 2021 Google LLC
6 */
7
8#define LOG_CATEGORY LOGC_ACPI
9
10#include <log.h>
11#include <malloc.h>
12#include <mapmem.h>
13#include <acpi/acpi_table.h>
14#include <asm/global_data.h>
15#include <dm/acpi.h>
16#include <linux/errno.h>
17
18DECLARE_GLOBAL_DATA_PTR;
19
20int acpi_write_one(struct acpi_ctx *ctx, const struct acpi_writer *entry)
21{
22	int ret;
23
24	log_debug("%s: writing table '%s'\n", entry->name,
25		  entry->table);
26	ctx->tab_start = ctx->current;
27	ret = entry->h_write(ctx, entry);
28	if (ret == -ENOENT) {
29		log_debug("%s: Omitted due to being empty\n",
30			  entry->name);
31		ret = 0;
32		ctx->current = ctx->tab_start;	/* drop the table */
33		return ret;
34	}
35	if (ret)
36		return log_msg_ret("write", ret);
37
38	if (entry->flags & ACPIWF_ALIGN64)
39		acpi_align64(ctx);
40	else
41		acpi_align(ctx);
42
43	/* Add the item to the internal list */
44	ret = acpi_add_other_item(ctx, entry, ctx->tab_start);
45	if (ret)
46		return log_msg_ret("add", ret);
47
48	return 0;
49}
50
51#ifndef CONFIG_QFW_ACPI
52static int acpi_write_all(struct acpi_ctx *ctx)
53{
54	const struct acpi_writer *writer =
55		 ll_entry_start(struct acpi_writer, acpi_writer);
56	const int n_ents = ll_entry_count(struct acpi_writer, acpi_writer);
57	const struct acpi_writer *entry;
58	int ret;
59
60	for (entry = writer; entry != writer + n_ents; entry++) {
61		ret = acpi_write_one(ctx, entry);
62		if (ret && ret != -ENOENT)
63			return log_msg_ret("one", ret);
64	}
65
66	return 0;
67}
68
69/*
70 * QEMU's version of write_acpi_tables is defined in drivers/misc/qfw.c
71 */
72ulong write_acpi_tables(ulong start_addr)
73{
74	struct acpi_ctx *ctx;
75	ulong addr;
76	int ret;
77
78	ctx = malloc(sizeof(*ctx));
79	if (!ctx)
80		return log_msg_ret("mem", -ENOMEM);
81
82	log_debug("ACPI: Writing ACPI tables at %lx\n", start_addr);
83
84	acpi_reset_items();
85	acpi_setup_ctx(ctx, start_addr);
86
87	ret = acpi_write_all(ctx);
88	if (ret) {
89		log_err("Failed to write ACPI tables (err=%d)\n", ret);
90		return log_msg_ret("write", -ENOMEM);
91	}
92
93	addr = map_to_sysmem(ctx->current);
94	log_debug("ACPI current = %lx\n", addr);
95
96	return addr;
97}
98
99int write_dev_tables(struct acpi_ctx *ctx, const struct acpi_writer *entry)
100{
101	int ret;
102
103	ret = acpi_write_dev_tables(ctx);
104	if (ret)
105		return log_msg_ret("write", ret);
106
107	return 0;
108}
109ACPI_WRITER(8dev, NULL, write_dev_tables, 0);
110
111ulong acpi_get_rsdp_addr(void)
112{
113	if (!gd->acpi_ctx)
114		return 0;
115
116	return map_to_sysmem(gd->acpi_ctx->rsdp);
117}
118#endif /* QFW_ACPI */
119
120void acpi_setup_ctx(struct acpi_ctx *ctx, ulong start)
121{
122	gd->acpi_ctx = ctx;
123	memset(ctx, '\0', sizeof(*ctx));
124
125	/* Align ACPI tables to 16-byte boundary */
126	start = ALIGN(start, 16);
127	ctx->base = map_sysmem(start, 0);
128	ctx->current = ctx->base;
129
130	gd_set_acpi_start(start);
131}
132