xref: /freebsd/usr.sbin/bhyve/acpi_device.c (revision 51015e6d0f570239b0c2088dc6cf2b018928375d)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 
21 /**
22  * List entry to enumerate all resources used by an ACPI device.
23  *
24  * @param chain Used to chain multiple elements together.
25  * @param type  Type of the ACPI resource.
26  * @param data  Data of the ACPI resource.
27  */
28 struct acpi_resource_list_entry {
29 	SLIST_ENTRY(acpi_resource_list_entry) chain;
30 	UINT32 type;
31 	ACPI_RESOURCE_DATA data;
32 };
33 
34 /**
35  * Holds information about an ACPI device.
36  *
37  * @param vm_ctx VM context the ACPI device was created in.
38  * @param name   Name of the ACPI device.
39  * @param hid    Hardware ID of the ACPI device.
40  * @param crs    Current resources used by the ACPI device.
41  */
42 struct acpi_device {
43 	struct vmctx *vm_ctx;
44 	const char *name;
45 	const char *hid;
46 	SLIST_HEAD(acpi_resource_list, acpi_resource_list_entry) crs;
47 };
48 
49 int
50 acpi_device_create(struct acpi_device **const new_dev,
51     struct vmctx *const vm_ctx, const char *const name, const char *const hid)
52 {
53 	if (new_dev == NULL || vm_ctx == NULL || name == NULL || hid == NULL) {
54 		return (EINVAL);
55 	}
56 
57 	struct acpi_device *const dev = calloc(1, sizeof(*dev));
58 	if (dev == NULL) {
59 		return (ENOMEM);
60 	}
61 
62 	dev->vm_ctx = vm_ctx;
63 	dev->name = strdup(name);
64 	dev->hid = strdup(hid);
65 	SLIST_INIT(&dev->crs);
66 	if (dev->name == NULL || dev->hid == NULL) {
67 		acpi_device_destroy(dev);
68 		return (ENOMEM);
69 	}
70 
71 	const int error = acpi_tables_add_device(dev);
72 	if (error) {
73 		acpi_device_destroy(dev);
74 		return (error);
75 	}
76 
77 	*new_dev = dev;
78 
79 	return (0);
80 }
81 
82 void
83 acpi_device_destroy(struct acpi_device *const dev)
84 {
85 	if (dev == NULL) {
86 		return;
87 	}
88 
89 	struct acpi_resource_list_entry *res;
90 	while (!SLIST_EMPTY(&dev->crs)) {
91 		res = SLIST_FIRST(&dev->crs);
92 		SLIST_REMOVE_HEAD(&dev->crs, chain);
93 		free(res);
94 	}
95 
96 	free(__DECONST(void *, dev->hid));
97 	free(__DECONST(void *, dev->name));
98 	free(dev);
99 }
100 
101 int
102 acpi_device_add_res_fixed_ioport(struct acpi_device *const dev,
103     const UINT16 port, const UINT8 length)
104 {
105 	if (dev == NULL) {
106 		return (EINVAL);
107 	}
108 
109 	struct acpi_resource_list_entry *const res = calloc(1, sizeof(*res));
110 	if (res == NULL) {
111 		return (ENOMEM);
112 	}
113 
114 	res->type = ACPI_RESOURCE_TYPE_FIXED_IO;
115 	res->data.FixedIo.Address = port;
116 	res->data.FixedIo.AddressLength = length;
117 
118 	SLIST_INSERT_HEAD(&dev->crs, res, chain);
119 
120 	return (0);
121 }
122 
123 int
124 acpi_device_add_res_fixed_memory32(struct acpi_device *const dev,
125     const UINT8 write_protected, const UINT32 address, const UINT32 length)
126 {
127 	if (dev == NULL) {
128 		return (EINVAL);
129 	}
130 
131 	struct acpi_resource_list_entry *const res = calloc(1, sizeof(*res));
132 	if (res == NULL) {
133 		return (ENOMEM);
134 	}
135 
136 	res->type = ACPI_RESOURCE_TYPE_FIXED_MEMORY32;
137 	res->data.FixedMemory32.WriteProtect = write_protected;
138 	res->data.FixedMemory32.Address = address;
139 	res->data.FixedMemory32.AddressLength = length;
140 
141 	SLIST_INSERT_HEAD(&dev->crs, res, chain);
142 
143 	return (0);
144 }
145 
146 static void
147 acpi_device_write_dsdt_crs(const struct acpi_device *const dev)
148 {
149 	const struct acpi_resource_list_entry *res;
150 	SLIST_FOREACH(res, &dev->crs, chain) {
151 		switch (res->type) {
152 		case ACPI_RESOURCE_TYPE_FIXED_IO:
153 			dsdt_fixed_ioport(res->data.FixedIo.Address,
154 			    res->data.FixedIo.AddressLength);
155 			break;
156 		case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
157 			dsdt_fixed_mem32(res->data.FixedMemory32.Address,
158 			    res->data.FixedMemory32.AddressLength);
159 			break;
160 		default:
161 			assert(0);
162 			break;
163 		}
164 	}
165 }
166 
167 void
168 acpi_device_write_dsdt(const struct acpi_device *const dev)
169 {
170 	if (dev == NULL) {
171 		return;
172 	}
173 
174 	dsdt_line("");
175 	dsdt_line("  Scope (\\_SB)");
176 	dsdt_line("  {");
177 	dsdt_line("    Device (%s)", dev->name);
178 	dsdt_line("    {");
179 	dsdt_line("      Name (_HID, \"%s\")", dev->hid);
180 	dsdt_line("      Name (_STA, 0x0F)");
181 	dsdt_line("      Name (_CRS, ResourceTemplate ()");
182 	dsdt_line("      {");
183 	dsdt_indent(4);
184 	acpi_device_write_dsdt_crs(dev);
185 	dsdt_unindent(4);
186 	dsdt_line("      })");
187 	dsdt_line("    }");
188 	dsdt_line("  }");
189 }
190