xref: /freebsd/usr.sbin/bhyve/acpi_device.c (revision eb1feadc201ab7c4dc3aee9938e272c068179a5f)
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 emul   Device emulation struct. It contains some information like the
39                  name of the ACPI device and some device specific functions.
40  * @param crs    Current resources used by the ACPI device.
41  */
42 struct acpi_device {
43 	struct vmctx *vm_ctx;
44 	const struct acpi_device_emul *emul;
45 	SLIST_HEAD(acpi_resource_list, acpi_resource_list_entry) crs;
46 };
47 
48 int
49 acpi_device_create(struct acpi_device **const new_dev,
50     struct vmctx *const vm_ctx, const struct acpi_device_emul *const emul)
51 {
52 	assert(new_dev != NULL);
53 	assert(vm_ctx != NULL);
54 	assert(emul != NULL);
55 
56 	struct acpi_device *const dev = calloc(1, sizeof(*dev));
57 	if (dev == NULL) {
58 		return (ENOMEM);
59 	}
60 
61 	dev->vm_ctx = vm_ctx;
62 	dev->emul = emul;
63 	SLIST_INIT(&dev->crs);
64 
65 	const int error = acpi_tables_add_device(dev);
66 	if (error) {
67 		acpi_device_destroy(dev);
68 		return (error);
69 	}
70 
71 	*new_dev = dev;
72 
73 	return (0);
74 }
75 
76 void
77 acpi_device_destroy(struct acpi_device *const dev)
78 {
79 	if (dev == NULL) {
80 		return;
81 	}
82 
83 	struct acpi_resource_list_entry *res;
84 	while (!SLIST_EMPTY(&dev->crs)) {
85 		res = SLIST_FIRST(&dev->crs);
86 		SLIST_REMOVE_HEAD(&dev->crs, chain);
87 		free(res);
88 	}
89 
90 	free(dev);
91 }
92 
93 int
94 acpi_device_add_res_fixed_ioport(struct acpi_device *const dev,
95     const UINT16 port, const UINT8 length)
96 {
97 	if (dev == NULL) {
98 		return (EINVAL);
99 	}
100 
101 	struct acpi_resource_list_entry *const res = calloc(1, sizeof(*res));
102 	if (res == NULL) {
103 		return (ENOMEM);
104 	}
105 
106 	res->type = ACPI_RESOURCE_TYPE_FIXED_IO;
107 	res->data.FixedIo.Address = port;
108 	res->data.FixedIo.AddressLength = length;
109 
110 	SLIST_INSERT_HEAD(&dev->crs, res, chain);
111 
112 	return (0);
113 }
114 
115 int
116 acpi_device_add_res_fixed_memory32(struct acpi_device *const dev,
117     const UINT8 write_protected, const UINT32 address, const UINT32 length)
118 {
119 	if (dev == NULL) {
120 		return (EINVAL);
121 	}
122 
123 	struct acpi_resource_list_entry *const res = calloc(1, sizeof(*res));
124 	if (res == NULL) {
125 		return (ENOMEM);
126 	}
127 
128 	res->type = ACPI_RESOURCE_TYPE_FIXED_MEMORY32;
129 	res->data.FixedMemory32.WriteProtect = write_protected;
130 	res->data.FixedMemory32.Address = address;
131 	res->data.FixedMemory32.AddressLength = length;
132 
133 	SLIST_INSERT_HEAD(&dev->crs, res, chain);
134 
135 	return (0);
136 }
137 
138 static void
139 acpi_device_write_dsdt_crs(const struct acpi_device *const dev)
140 {
141 	const struct acpi_resource_list_entry *res;
142 	SLIST_FOREACH(res, &dev->crs, chain) {
143 		switch (res->type) {
144 		case ACPI_RESOURCE_TYPE_FIXED_IO:
145 			dsdt_fixed_ioport(res->data.FixedIo.Address,
146 			    res->data.FixedIo.AddressLength);
147 			break;
148 		case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
149 			dsdt_fixed_mem32(res->data.FixedMemory32.Address,
150 			    res->data.FixedMemory32.AddressLength);
151 			break;
152 		default:
153 			assert(0);
154 			break;
155 		}
156 	}
157 }
158 
159 void
160 acpi_device_write_dsdt(const struct acpi_device *const dev)
161 {
162 	if (dev == NULL) {
163 		return;
164 	}
165 
166 	dsdt_line("");
167 	dsdt_line("  Scope (\\_SB)");
168 	dsdt_line("  {");
169 	dsdt_line("    Device (%s)", dev->emul->name);
170 	dsdt_line("    {");
171 	dsdt_line("      Name (_HID, \"%s\")", dev->emul->hid);
172 	dsdt_line("      Name (_STA, 0x0F)");
173 	dsdt_line("      Name (_CRS, ResourceTemplate ()");
174 	dsdt_line("      {");
175 	dsdt_indent(4);
176 	acpi_device_write_dsdt_crs(dev);
177 	dsdt_unindent(4);
178 	dsdt_line("      })");
179 	dsdt_line("    }");
180 	dsdt_line("  }");
181 }
182