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