xref: /freebsd/usr.sbin/bhyve/acpi_device.c (revision fd45b686f9d92f583366c75b22c04c7ee49709c0)
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
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
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
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
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 *
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
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
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
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