1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG
5 * Author: Corvin K��hne <c.koehne@beckhoff.com>
6 */
7
8#include <sys/param.h>
9#include <sys/queue.h>
10
11#include <machine/vmm.h>
12
13#include <assert.h>
14#include <err.h>
15#include <errno.h>
16#include <vmmapi.h>
17
18#include "acpi.h"
19#include "acpi_device.h"
20#include "basl.h"
21
22/**
23 * List entry to enumerate all resources used by an ACPI device.
24 *
25 * @param chain Used to chain multiple elements together.
26 * @param type  Type of the ACPI resource.
27 * @param data  Data of the ACPI resource.
28 */
29struct acpi_resource_list_entry {
30	SLIST_ENTRY(acpi_resource_list_entry) chain;
31	UINT32 type;
32	ACPI_RESOURCE_DATA data;
33};
34
35/**
36 * Holds information about an ACPI device.
37 *
38 * @param vm_ctx VM context the ACPI device was created in.
39 * @param softc  A pointer to the software context of the ACPI device.
40 * @param emul   Device emulation struct. It contains some information like the
41                 name of the ACPI device and some device specific functions.
42 * @param crs    Current resources used by the ACPI device.
43 */
44struct acpi_device {
45	struct vmctx *vm_ctx;
46	void *softc;
47	const struct acpi_device_emul *emul;
48	SLIST_HEAD(acpi_resource_list, acpi_resource_list_entry) crs;
49};
50
51int
52acpi_device_create(struct acpi_device **const new_dev, void *const softc,
53    struct vmctx *const vm_ctx, const struct acpi_device_emul *const emul)
54{
55	assert(new_dev != NULL);
56	assert(vm_ctx != NULL);
57	assert(emul != NULL);
58
59	struct acpi_device *const dev = calloc(1, sizeof(*dev));
60	if (dev == NULL) {
61		return (ENOMEM);
62	}
63
64	dev->vm_ctx = vm_ctx;
65	dev->softc = softc;
66	dev->emul = emul;
67	SLIST_INIT(&dev->crs);
68
69	const int error = acpi_tables_add_device(dev);
70	if (error) {
71		acpi_device_destroy(dev);
72		return (error);
73	}
74
75	*new_dev = dev;
76
77	return (0);
78}
79
80void
81acpi_device_destroy(struct acpi_device *const dev)
82{
83	if (dev == NULL) {
84		return;
85	}
86
87	struct acpi_resource_list_entry *res;
88	while (!SLIST_EMPTY(&dev->crs)) {
89		res = SLIST_FIRST(&dev->crs);
90		SLIST_REMOVE_HEAD(&dev->crs, chain);
91		free(res);
92	}
93
94	free(dev);
95}
96
97int
98acpi_device_add_res_fixed_ioport(struct acpi_device *const dev,
99    const UINT16 port, const UINT8 length)
100{
101	if (dev == NULL) {
102		return (EINVAL);
103	}
104
105	struct acpi_resource_list_entry *const res = calloc(1, sizeof(*res));
106	if (res == NULL) {
107		return (ENOMEM);
108	}
109
110	res->type = ACPI_RESOURCE_TYPE_FIXED_IO;
111	res->data.FixedIo.Address = port;
112	res->data.FixedIo.AddressLength = length;
113
114	SLIST_INSERT_HEAD(&dev->crs, res, chain);
115
116	return (0);
117}
118
119int
120acpi_device_add_res_fixed_memory32(struct acpi_device *const dev,
121    const UINT8 write_protected, const UINT32 address, const UINT32 length)
122{
123	if (dev == NULL) {
124		return (EINVAL);
125	}
126
127	struct acpi_resource_list_entry *const res = calloc(1, sizeof(*res));
128	if (res == NULL) {
129		return (ENOMEM);
130	}
131
132	res->type = ACPI_RESOURCE_TYPE_FIXED_MEMORY32;
133	res->data.FixedMemory32.WriteProtect = write_protected;
134	res->data.FixedMemory32.Address = address;
135	res->data.FixedMemory32.AddressLength = length;
136
137	SLIST_INSERT_HEAD(&dev->crs, res, chain);
138
139	return (0);
140}
141
142void *
143acpi_device_get_softc(const struct acpi_device *const dev)
144{
145	assert(dev != NULL);
146
147	return (dev->softc);
148}
149
150int
151acpi_device_build_table(const struct acpi_device *const dev)
152{
153	assert(dev != NULL);
154	assert(dev->emul != NULL);
155
156	if (dev->emul->build_table != NULL) {
157		return (dev->emul->build_table(dev));
158	}
159
160	return (0);
161}
162
163static int
164acpi_device_write_dsdt_crs(const struct acpi_device *const dev)
165{
166	const struct acpi_resource_list_entry *res;
167	SLIST_FOREACH(res, &dev->crs, chain) {
168		switch (res->type) {
169		case ACPI_RESOURCE_TYPE_FIXED_IO:
170			dsdt_fixed_ioport(res->data.FixedIo.Address,
171			    res->data.FixedIo.AddressLength);
172			break;
173		case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
174			dsdt_fixed_mem32(res->data.FixedMemory32.Address,
175			    res->data.FixedMemory32.AddressLength);
176			break;
177		default:
178			assert(0);
179			break;
180		}
181	}
182
183	return (0);
184}
185
186int
187acpi_device_write_dsdt(const struct acpi_device *const dev)
188{
189	assert(dev != NULL);
190
191	dsdt_line("");
192	dsdt_line("  Scope (\\_SB)");
193	dsdt_line("  {");
194	dsdt_line("    Device (%s)", dev->emul->name);
195	dsdt_line("    {");
196	dsdt_line("      Name (_HID, \"%s\")", dev->emul->hid);
197	dsdt_line("      Name (_STA, 0x0F)");
198	dsdt_line("      Name (_CRS, ResourceTemplate ()");
199	dsdt_line("      {");
200	dsdt_indent(4);
201	BASL_EXEC(acpi_device_write_dsdt_crs(dev));
202	dsdt_unindent(4);
203	dsdt_line("      })");
204	if (dev->emul->write_dsdt != NULL) {
205		dsdt_indent(3);
206		BASL_EXEC(dev->emul->write_dsdt(dev));
207		dsdt_unindent(3);
208	}
209	dsdt_line("    }");
210	dsdt_line("  }");
211
212	return (0);
213}
214