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 */
29 struct 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 */
44 struct 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
51 int
acpi_device_create(struct acpi_device ** const new_dev,void * const softc,struct vmctx * const vm_ctx,const struct acpi_device_emul * const emul)52 acpi_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
80 void
acpi_device_destroy(struct acpi_device * const dev)81 acpi_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
97 int
acpi_device_add_res_fixed_ioport(struct acpi_device * const dev,const UINT16 port,const UINT8 length)98 acpi_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
119 int
acpi_device_add_res_fixed_memory32(struct acpi_device * const dev,const UINT8 write_protected,const UINT32 address,const UINT32 length)120 acpi_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
142 void *
acpi_device_get_softc(const struct acpi_device * const dev)143 acpi_device_get_softc(const struct acpi_device *const dev)
144 {
145 assert(dev != NULL);
146
147 return (dev->softc);
148 }
149
150 int
acpi_device_build_table(const struct acpi_device * const dev)151 acpi_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
163 static int
acpi_device_write_dsdt_crs(const struct acpi_device * const dev)164 acpi_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
186 int
acpi_device_write_dsdt(const struct acpi_device * const dev)187 acpi_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