summaryrefslogtreecommitdiff
path: root/sys/dev/acpi/uacpi/osi.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/acpi/uacpi/osi.c')
-rw-r--r--sys/dev/acpi/uacpi/osi.c388
1 files changed, 388 insertions, 0 deletions
diff --git a/sys/dev/acpi/uacpi/osi.c b/sys/dev/acpi/uacpi/osi.c
new file mode 100644
index 0000000..0940261
--- /dev/null
+++ b/sys/dev/acpi/uacpi/osi.c
@@ -0,0 +1,388 @@
+#include <uacpi/platform/atomic.h>
+#include <uacpi/internal/osi.h>
+#include <uacpi/internal/helpers.h>
+#include <uacpi/internal/stdlib.h>
+#include <uacpi/internal/utilities.h>
+#include <uacpi/internal/mutex.h>
+#include <uacpi/kernel_api.h>
+
+#ifndef UACPI_BAREBONES_MODE
+
+struct registered_interface {
+ const uacpi_char *name;
+ uacpi_u8 weight;
+ uacpi_u8 kind;
+
+ // Only applicable for predefined host interfaces
+ uacpi_u8 host_type;
+
+ // Only applicable for predefined interfaces
+ uacpi_u8 disabled : 1;
+ uacpi_u8 dynamic : 1;
+
+ struct registered_interface *next;
+};
+
+static uacpi_handle interface_mutex;
+static struct registered_interface *registered_interfaces;
+static uacpi_interface_handler interface_handler;
+static uacpi_u32 latest_queried_interface;
+
+#define WINDOWS(string, interface) \
+ { \
+ .name = "Windows "string, \
+ .weight = UACPI_VENDOR_INTERFACE_WINDOWS_##interface, \
+ .kind = UACPI_INTERFACE_KIND_VENDOR, \
+ .host_type = 0, \
+ .disabled = 0, \
+ .dynamic = 0, \
+ .next = UACPI_NULL \
+ }
+
+#define HOST_FEATURE(string, type) \
+ { \
+ .name = string, \
+ .weight = 0, \
+ .kind = UACPI_INTERFACE_KIND_FEATURE, \
+ .host_type = UACPI_HOST_INTERFACE_##type, \
+ .disabled = 1, \
+ .dynamic = 0, \
+ .next = UACPI_NULL, \
+ }
+
+static struct registered_interface predefined_interfaces[] = {
+ // Vendor strings
+ WINDOWS("2000", 2000),
+ WINDOWS("2001", XP),
+ WINDOWS("2001 SP1", XP_SP1),
+ WINDOWS("2001.1", SERVER_2003),
+ WINDOWS("2001 SP2", XP_SP2),
+ WINDOWS("2001.1 SP1", SERVER_2003_SP1),
+ WINDOWS("2006", VISTA),
+ WINDOWS("2006.1", SERVER_2008),
+ WINDOWS("2006 SP1", VISTA_SP1),
+ WINDOWS("2006 SP2", VISTA_SP2),
+ WINDOWS("2009", 7),
+ WINDOWS("2012", 8),
+ WINDOWS("2013", 8_1),
+ WINDOWS("2015", 10),
+ WINDOWS("2016", 10_RS1),
+ WINDOWS("2017", 10_RS2),
+ WINDOWS("2017.2", 10_RS3),
+ WINDOWS("2018", 10_RS4),
+ WINDOWS("2018.2", 10_RS5),
+ WINDOWS("2019", 10_19H1),
+ WINDOWS("2020", 10_20H1),
+ WINDOWS("2021", 11),
+ WINDOWS("2022", 11_22H2),
+
+ // Feature strings
+ HOST_FEATURE("Module Device", MODULE_DEVICE),
+ HOST_FEATURE("Processor Device", PROCESSOR_DEVICE),
+ HOST_FEATURE("3.0 Thermal Model", 3_0_THERMAL_MODEL),
+ HOST_FEATURE("3.0 _SCP Extensions", 3_0_SCP_EXTENSIONS),
+ HOST_FEATURE("Processor Aggregator Device", PROCESSOR_AGGREGATOR_DEVICE),
+
+ // Interpreter features
+ { .name = "Extended Address Space Descriptor" },
+};
+
+uacpi_status uacpi_initialize_interfaces(void)
+{
+ uacpi_size i;
+
+ registered_interfaces = &predefined_interfaces[0];
+
+ interface_mutex = uacpi_kernel_create_mutex();
+ if (uacpi_unlikely(interface_mutex == UACPI_NULL))
+ return UACPI_STATUS_OUT_OF_MEMORY;
+
+ for (i = 0; i < (UACPI_ARRAY_SIZE(predefined_interfaces) - 1); ++i)
+ predefined_interfaces[i].next = &predefined_interfaces[i + 1];
+
+ return UACPI_STATUS_OK;
+}
+
+void uacpi_deinitialize_interfaces(void)
+{
+ struct registered_interface *iface, *next_iface = registered_interfaces;
+
+ while (next_iface) {
+ iface = next_iface;
+ next_iface = iface->next;
+
+ iface->next = UACPI_NULL;
+
+ if (iface->dynamic) {
+ uacpi_free_dynamic_string(iface->name);
+ uacpi_free(iface, sizeof(*iface));
+ continue;
+ }
+
+ // Only features are disabled by default
+ iface->disabled = iface->kind == UACPI_INTERFACE_KIND_FEATURE ?
+ UACPI_TRUE : UACPI_FALSE;
+ }
+
+ if (interface_mutex)
+ uacpi_kernel_free_mutex(interface_mutex);
+
+ interface_mutex = UACPI_NULL;
+ interface_handler = UACPI_NULL;
+ latest_queried_interface = 0;
+ registered_interfaces = UACPI_NULL;
+}
+
+uacpi_vendor_interface uacpi_latest_queried_vendor_interface(void)
+{
+ return uacpi_atomic_load32(&latest_queried_interface);
+}
+
+static struct registered_interface *find_interface_unlocked(
+ const uacpi_char *name
+)
+{
+ struct registered_interface *interface = registered_interfaces;
+
+ while (interface) {
+ if (uacpi_strcmp(interface->name, name) == 0)
+ return interface;
+
+ interface = interface->next;
+ }
+
+ return UACPI_NULL;
+}
+
+static struct registered_interface *find_host_interface_unlocked(
+ uacpi_host_interface type
+)
+{
+ struct registered_interface *interface = registered_interfaces;
+
+ while (interface) {
+ if (interface->host_type == type)
+ return interface;
+
+ interface = interface->next;
+ }
+
+ return UACPI_NULL;
+}
+
+uacpi_status uacpi_install_interface(
+ const uacpi_char *name, uacpi_interface_kind kind
+)
+{
+ struct registered_interface *interface;
+ uacpi_status ret;
+ uacpi_char *name_copy;
+ uacpi_size name_size;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ ret = uacpi_acquire_native_mutex(interface_mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ interface = find_interface_unlocked(name);
+ if (interface != UACPI_NULL) {
+ if (interface->disabled)
+ interface->disabled = UACPI_FALSE;
+
+ ret = UACPI_STATUS_ALREADY_EXISTS;
+ goto out;
+ }
+
+ interface = uacpi_kernel_alloc(sizeof(*interface));
+ if (uacpi_unlikely(interface == UACPI_NULL)) {
+ ret = UACPI_STATUS_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ name_size = uacpi_strlen(name) + 1;
+ name_copy = uacpi_kernel_alloc(name_size);
+ if (uacpi_unlikely(name_copy == UACPI_NULL)) {
+ uacpi_free(interface, sizeof(*interface));
+ ret = UACPI_STATUS_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ uacpi_memcpy(name_copy, name, name_size);
+ interface->name = name_copy;
+ interface->weight = 0;
+ interface->kind = kind;
+ interface->host_type = 0;
+ interface->disabled = 0;
+ interface->dynamic = 1;
+ interface->next = registered_interfaces;
+ registered_interfaces = interface;
+
+out:
+ uacpi_release_native_mutex(interface_mutex);
+ return ret;
+}
+
+uacpi_status uacpi_uninstall_interface(const uacpi_char *name)
+{
+ struct registered_interface *cur, *prev;
+ uacpi_status ret;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ ret = uacpi_acquire_native_mutex(interface_mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ cur = registered_interfaces;
+ prev = cur;
+
+ ret = UACPI_STATUS_NOT_FOUND;
+ while (cur) {
+ if (uacpi_strcmp(cur->name, name) != 0) {
+ prev = cur;
+ cur = cur->next;
+ continue;
+ }
+
+ if (cur->dynamic) {
+ if (prev == cur) {
+ registered_interfaces = cur->next;
+ } else {
+ prev->next = cur->next;
+ }
+
+ uacpi_release_native_mutex(interface_mutex);
+ uacpi_free_dynamic_string(cur->name);
+ uacpi_free(cur, sizeof(*cur));
+ return UACPI_STATUS_OK;
+ }
+
+ /*
+ * If this interface was already disabled, pretend we didn't actually
+ * find it and keep ret as UACPI_STATUS_NOT_FOUND. The fact that it's
+ * still in the registered list is an implementation detail of
+ * predefined interfaces.
+ */
+ if (!cur->disabled) {
+ cur->disabled = UACPI_TRUE;
+ ret = UACPI_STATUS_OK;
+ }
+
+ break;
+ }
+
+ uacpi_release_native_mutex(interface_mutex);
+ return ret;
+}
+
+static uacpi_status configure_host_interface(
+ uacpi_host_interface type, uacpi_bool enabled
+)
+{
+ struct registered_interface *interface;
+ uacpi_status ret;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ ret = uacpi_acquire_native_mutex(interface_mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ interface = find_host_interface_unlocked(type);
+ if (interface == UACPI_NULL) {
+ ret = UACPI_STATUS_NOT_FOUND;
+ goto out;
+ }
+
+ interface->disabled = !enabled;
+out:
+ uacpi_release_native_mutex(interface_mutex);
+ return ret;
+}
+
+uacpi_status uacpi_enable_host_interface(uacpi_host_interface type)
+{
+ return configure_host_interface(type, UACPI_TRUE);
+}
+
+uacpi_status uacpi_disable_host_interface(uacpi_host_interface type)
+{
+ return configure_host_interface(type, UACPI_FALSE);
+}
+
+uacpi_status uacpi_set_interface_query_handler(
+ uacpi_interface_handler handler
+)
+{
+ uacpi_status ret;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ ret = uacpi_acquire_native_mutex(interface_mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ if (interface_handler != UACPI_NULL && handler != UACPI_NULL) {
+ ret = UACPI_STATUS_ALREADY_EXISTS;
+ goto out;
+ }
+
+ interface_handler = handler;
+out:
+ uacpi_release_native_mutex(interface_mutex);
+ return ret;
+}
+
+uacpi_status uacpi_bulk_configure_interfaces(
+ uacpi_interface_action action, uacpi_interface_kind kind
+)
+{
+ uacpi_status ret;
+ struct registered_interface *interface;
+
+ UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_SUBSYSTEM_INITIALIZED);
+
+ ret = uacpi_acquire_native_mutex(interface_mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ interface = registered_interfaces;
+ while (interface) {
+ if (kind & interface->kind)
+ interface->disabled = (action == UACPI_INTERFACE_ACTION_DISABLE);
+
+ interface = interface->next;
+ }
+
+ uacpi_release_native_mutex(interface_mutex);
+ return ret;
+}
+
+uacpi_status uacpi_handle_osi(const uacpi_char *string, uacpi_bool *out_value)
+{
+ uacpi_status ret;
+ struct registered_interface *interface;
+ uacpi_bool is_supported = UACPI_FALSE;
+
+ ret = uacpi_acquire_native_mutex(interface_mutex);
+ if (uacpi_unlikely_error(ret))
+ return ret;
+
+ interface = find_interface_unlocked(string);
+ if (interface == UACPI_NULL)
+ goto out;
+
+ if (interface->weight > latest_queried_interface)
+ uacpi_atomic_store32(&latest_queried_interface, interface->weight);
+
+ is_supported = !interface->disabled;
+ if (interface_handler)
+ is_supported = interface_handler(string, is_supported);
+out:
+ uacpi_release_native_mutex(interface_mutex);
+ *out_value = is_supported;
+ return UACPI_STATUS_OK;
+}
+
+#endif // !UACPI_BAREBONES_MODE