Files
my-os-project2/kernel/hal/x86_64/uACPI/source/utilities.c
2025-08-17 18:37:57 +02:00

1157 lines
32 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <uacpi/types.h>
#include <uacpi/status.h>
#include <uacpi/uacpi.h>
#include <uacpi/internal/context.h>
#include <uacpi/internal/utilities.h>
#include <uacpi/internal/log.h>
#include <uacpi/internal/namespace.h>
enum char_type {
CHAR_TYPE_CONTROL = 1 << 0,
CHAR_TYPE_SPACE = 1 << 1,
CHAR_TYPE_BLANK = 1 << 2,
CHAR_TYPE_PUNCTUATION = 1 << 3,
CHAR_TYPE_LOWER = 1 << 4,
CHAR_TYPE_UPPER = 1 << 5,
CHAR_TYPE_DIGIT = 1 << 6,
CHAR_TYPE_HEX_DIGIT = 1 << 7,
CHAR_TYPE_ALPHA = CHAR_TYPE_LOWER | CHAR_TYPE_UPPER,
CHAR_TYPE_ALHEX = CHAR_TYPE_ALPHA | CHAR_TYPE_HEX_DIGIT,
CHAR_TYPE_ALNUM = CHAR_TYPE_ALPHA | CHAR_TYPE_DIGIT,
};
static const uacpi_u8 ascii_map[256] = {
CHAR_TYPE_CONTROL, // 0
CHAR_TYPE_CONTROL, // 1
CHAR_TYPE_CONTROL, // 2
CHAR_TYPE_CONTROL, // 3
CHAR_TYPE_CONTROL, // 4
CHAR_TYPE_CONTROL, // 5
CHAR_TYPE_CONTROL, // 6
CHAR_TYPE_CONTROL, // 7
CHAR_TYPE_CONTROL, // -> 8 control codes
CHAR_TYPE_CONTROL | CHAR_TYPE_SPACE | CHAR_TYPE_BLANK, // 9 tab
CHAR_TYPE_CONTROL | CHAR_TYPE_SPACE, // 10
CHAR_TYPE_CONTROL | CHAR_TYPE_SPACE, // 11
CHAR_TYPE_CONTROL | CHAR_TYPE_SPACE, // 12
CHAR_TYPE_CONTROL | CHAR_TYPE_SPACE, // -> 13 whitespaces
CHAR_TYPE_CONTROL, // 14
CHAR_TYPE_CONTROL, // 15
CHAR_TYPE_CONTROL, // 16
CHAR_TYPE_CONTROL, // 17
CHAR_TYPE_CONTROL, // 18
CHAR_TYPE_CONTROL, // 19
CHAR_TYPE_CONTROL, // 20
CHAR_TYPE_CONTROL, // 21
CHAR_TYPE_CONTROL, // 22
CHAR_TYPE_CONTROL, // 23
CHAR_TYPE_CONTROL, // 24
CHAR_TYPE_CONTROL, // 25
CHAR_TYPE_CONTROL, // 26
CHAR_TYPE_CONTROL, // 27
CHAR_TYPE_CONTROL, // 28
CHAR_TYPE_CONTROL, // 29
CHAR_TYPE_CONTROL, // 30
CHAR_TYPE_CONTROL, // -> 31 control codes
CHAR_TYPE_SPACE | CHAR_TYPE_BLANK, // 32 space
CHAR_TYPE_PUNCTUATION, // 33
CHAR_TYPE_PUNCTUATION, // 34
CHAR_TYPE_PUNCTUATION, // 35
CHAR_TYPE_PUNCTUATION, // 36
CHAR_TYPE_PUNCTUATION, // 37
CHAR_TYPE_PUNCTUATION, // 38
CHAR_TYPE_PUNCTUATION, // 39
CHAR_TYPE_PUNCTUATION, // 40
CHAR_TYPE_PUNCTUATION, // 41
CHAR_TYPE_PUNCTUATION, // 42
CHAR_TYPE_PUNCTUATION, // 43
CHAR_TYPE_PUNCTUATION, // 44
CHAR_TYPE_PUNCTUATION, // 45
CHAR_TYPE_PUNCTUATION, // 46
CHAR_TYPE_PUNCTUATION, // -> 47 punctuation
CHAR_TYPE_DIGIT | CHAR_TYPE_HEX_DIGIT, // 48
CHAR_TYPE_DIGIT | CHAR_TYPE_HEX_DIGIT, // 49
CHAR_TYPE_DIGIT | CHAR_TYPE_HEX_DIGIT, // 50
CHAR_TYPE_DIGIT | CHAR_TYPE_HEX_DIGIT, // 51
CHAR_TYPE_DIGIT | CHAR_TYPE_HEX_DIGIT, // 52
CHAR_TYPE_DIGIT | CHAR_TYPE_HEX_DIGIT, // 53
CHAR_TYPE_DIGIT | CHAR_TYPE_HEX_DIGIT, // 54
CHAR_TYPE_DIGIT | CHAR_TYPE_HEX_DIGIT, // 55
CHAR_TYPE_DIGIT | CHAR_TYPE_HEX_DIGIT, // 56
CHAR_TYPE_DIGIT | CHAR_TYPE_HEX_DIGIT, // -> 57 digits
CHAR_TYPE_PUNCTUATION, // 58
CHAR_TYPE_PUNCTUATION, // 59
CHAR_TYPE_PUNCTUATION, // 60
CHAR_TYPE_PUNCTUATION, // 61
CHAR_TYPE_PUNCTUATION, // 62
CHAR_TYPE_PUNCTUATION, // 63
CHAR_TYPE_PUNCTUATION, // -> 64 punctuation
CHAR_TYPE_UPPER | CHAR_TYPE_HEX_DIGIT, // 65
CHAR_TYPE_UPPER | CHAR_TYPE_HEX_DIGIT, // 66
CHAR_TYPE_UPPER | CHAR_TYPE_HEX_DIGIT, // 67
CHAR_TYPE_UPPER | CHAR_TYPE_HEX_DIGIT, // 68
CHAR_TYPE_UPPER | CHAR_TYPE_HEX_DIGIT, // 69
CHAR_TYPE_UPPER | CHAR_TYPE_HEX_DIGIT, // -> 70 ABCDEF
CHAR_TYPE_UPPER, // 71
CHAR_TYPE_UPPER, // 72
CHAR_TYPE_UPPER, // 73
CHAR_TYPE_UPPER, // 74
CHAR_TYPE_UPPER, // 75
CHAR_TYPE_UPPER, // 76
CHAR_TYPE_UPPER, // 77
CHAR_TYPE_UPPER, // 78
CHAR_TYPE_UPPER, // 79
CHAR_TYPE_UPPER, // 80
CHAR_TYPE_UPPER, // 81
CHAR_TYPE_UPPER, // 82
CHAR_TYPE_UPPER, // 83
CHAR_TYPE_UPPER, // 84
CHAR_TYPE_UPPER, // 85
CHAR_TYPE_UPPER, // 86
CHAR_TYPE_UPPER, // 87
CHAR_TYPE_UPPER, // 88
CHAR_TYPE_UPPER, // 89
CHAR_TYPE_UPPER, // -> 90 the rest of UPPERCASE alphabet
CHAR_TYPE_PUNCTUATION, // 91
CHAR_TYPE_PUNCTUATION, // 92
CHAR_TYPE_PUNCTUATION, // 93
CHAR_TYPE_PUNCTUATION, // 94
CHAR_TYPE_PUNCTUATION, // 95
CHAR_TYPE_PUNCTUATION, // -> 96 punctuation
CHAR_TYPE_LOWER | CHAR_TYPE_HEX_DIGIT, // 97
CHAR_TYPE_LOWER | CHAR_TYPE_HEX_DIGIT, // 98
CHAR_TYPE_LOWER | CHAR_TYPE_HEX_DIGIT, // 99
CHAR_TYPE_LOWER | CHAR_TYPE_HEX_DIGIT, // 100
CHAR_TYPE_LOWER | CHAR_TYPE_HEX_DIGIT, // 101
CHAR_TYPE_LOWER | CHAR_TYPE_HEX_DIGIT, // -> 102 abcdef
CHAR_TYPE_LOWER, // 103
CHAR_TYPE_LOWER, // 104
CHAR_TYPE_LOWER, // 105
CHAR_TYPE_LOWER, // 106
CHAR_TYPE_LOWER, // 107
CHAR_TYPE_LOWER, // 108
CHAR_TYPE_LOWER, // 109
CHAR_TYPE_LOWER, // 110
CHAR_TYPE_LOWER, // 111
CHAR_TYPE_LOWER, // 112
CHAR_TYPE_LOWER, // 113
CHAR_TYPE_LOWER, // 114
CHAR_TYPE_LOWER, // 115
CHAR_TYPE_LOWER, // 116
CHAR_TYPE_LOWER, // 117
CHAR_TYPE_LOWER, // 118
CHAR_TYPE_LOWER, // 119
CHAR_TYPE_LOWER, // 120
CHAR_TYPE_LOWER, // 121
CHAR_TYPE_LOWER, // -> 122 the rest of UPPERCASE alphabet
CHAR_TYPE_PUNCTUATION, // 123
CHAR_TYPE_PUNCTUATION, // 124
CHAR_TYPE_PUNCTUATION, // 125
CHAR_TYPE_PUNCTUATION, // -> 126 punctuation
CHAR_TYPE_CONTROL // 127 backspace
};
static uacpi_bool is_char(uacpi_char c, enum char_type type)
{
return (ascii_map[(uacpi_u8)c] & type) == type;
}
static uacpi_char to_lower(uacpi_char c)
{
if (is_char(c, CHAR_TYPE_UPPER))
return c + ('a' - 'A');
return c;
}
static uacpi_bool peek_one(
const uacpi_char **str, const uacpi_size *size, uacpi_char *out_char
)
{
if (*size == 0)
return UACPI_FALSE;
*out_char = **str;
return UACPI_TRUE;
}
static uacpi_bool consume_one(
const uacpi_char **str, uacpi_size *size, uacpi_char *out_char
)
{
if (!peek_one(str, size, out_char))
return UACPI_FALSE;
*str += 1;
*size -= 1;
return UACPI_TRUE;
}
static uacpi_bool consume_if(
const uacpi_char **str, uacpi_size *size, enum char_type type
)
{
uacpi_char c;
if (!peek_one(str, size, &c) || !is_char(c, type))
return UACPI_FALSE;
*str += 1;
*size -= 1;
return UACPI_TRUE;
}
static uacpi_bool consume_if_equals(
const uacpi_char **str, uacpi_size *size, uacpi_char c
)
{
uacpi_char c1;
if (!peek_one(str, size, &c1) || to_lower(c1) != c)
return UACPI_FALSE;
*str += 1;
*size -= 1;
return UACPI_TRUE;
}
uacpi_status uacpi_string_to_integer(
const uacpi_char *str, uacpi_size max_chars, enum uacpi_base base,
uacpi_u64 *out_value
)
{
uacpi_status ret = UACPI_STATUS_INVALID_ARGUMENT;
uacpi_bool negative = UACPI_FALSE;
uacpi_u64 next, value = 0;
uacpi_char c = '\0';
while (consume_if(&str, &max_chars, CHAR_TYPE_SPACE));
if (consume_if_equals(&str, &max_chars, '-'))
negative = UACPI_TRUE;
else
consume_if_equals(&str, &max_chars, '+');
if (base == UACPI_BASE_AUTO) {
base = UACPI_BASE_DEC;
if (consume_if_equals(&str, &max_chars, '0')) {
base = UACPI_BASE_OCT;
if (consume_if_equals(&str, &max_chars, 'x'))
base = UACPI_BASE_HEX;
}
}
while (consume_one(&str, &max_chars, &c)) {
switch (ascii_map[(uacpi_u8)c] & (CHAR_TYPE_DIGIT | CHAR_TYPE_ALHEX)) {
case CHAR_TYPE_DIGIT | CHAR_TYPE_HEX_DIGIT:
next = c - '0';
if (base == UACPI_BASE_OCT && next > 7)
goto out;
break;
case CHAR_TYPE_LOWER | CHAR_TYPE_HEX_DIGIT:
case CHAR_TYPE_UPPER | CHAR_TYPE_HEX_DIGIT:
if (base != UACPI_BASE_HEX)
goto out;
next = 10 + (to_lower(c) - 'a');
break;
default:
goto out;
}
next = (value * base) + next;
if ((next / base) != value) {
value = 0xFFFFFFFFFFFFFFFF;
goto out;
}
value = next;
}
out:
if (negative)
value = -((uacpi_i64)value);
*out_value = value;
if (max_chars == 0 || c == '\0')
ret = UACPI_STATUS_OK;
return ret;
}
#ifndef UACPI_BAREBONES_MODE
static inline uacpi_bool is_valid_name_byte(uacpi_u8 c)
{
// _ := 0x5F
if (c == 0x5F)
return UACPI_TRUE;
/*
* LeadNameChar := A-Z | _
* DigitChar := 0 - 9
* NameChar := DigitChar | LeadNameChar
* A-Z := 0x41 - 0x5A
* 0-9 := 0x30 - 0x39
*/
return (ascii_map[c] & (CHAR_TYPE_DIGIT | CHAR_TYPE_UPPER)) != 0;
}
uacpi_bool uacpi_is_valid_nameseg(uacpi_u8 *nameseg)
{
return is_valid_name_byte(nameseg[0]) &&
is_valid_name_byte(nameseg[1]) &&
is_valid_name_byte(nameseg[2]) &&
is_valid_name_byte(nameseg[3]);
}
void uacpi_eisa_id_to_string(uacpi_u32 id, uacpi_char *out_string)
{
static uacpi_char hex_to_ascii[16] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F'
};
/*
* For whatever reason bits are encoded upper to lower here, swap
* them around so that we don't have to do ridiculous bit shifts
* everywhere.
*/
union {
uacpi_u8 bytes[4];
uacpi_u32 dword;
} orig, swapped;
orig.dword = id;
swapped.bytes[0] = orig.bytes[3];
swapped.bytes[1] = orig.bytes[2];
swapped.bytes[2] = orig.bytes[1];
swapped.bytes[3] = orig.bytes[0];
/*
* Bit 16 - 20: 3rd character (- 0x40) of mfg code
* Bit 21 - 25: 2nd character (- 0x40) of mfg code
* Bit 26 - 30: 1st character (- 0x40) of mfg code
*/
out_string[0] = (uacpi_char)(0x40 + ((swapped.dword >> 26) & 0x1F));
out_string[1] = (uacpi_char)(0x40 + ((swapped.dword >> 21) & 0x1F));
out_string[2] = (uacpi_char)(0x40 + ((swapped.dword >> 16) & 0x1F));
/*
* Bit 0 - 3 : 4th hex digit of product number
* Bit 4 - 7 : 3rd hex digit of product number
* Bit 8 - 11: 2nd hex digit of product number
* Bit 12 - 15: 1st hex digit of product number
*/
out_string[3] = hex_to_ascii[(swapped.dword >> 12) & 0x0F];
out_string[4] = hex_to_ascii[(swapped.dword >> 8 ) & 0x0F];
out_string[5] = hex_to_ascii[(swapped.dword >> 4 ) & 0x0F];
out_string[6] = hex_to_ascii[(swapped.dword >> 0 ) & 0x0F];
out_string[7] = '\0';
}
#define PNP_ID_LENGTH 8
uacpi_status uacpi_eval_hid(uacpi_namespace_node *node, uacpi_id_string **out_id)
{
uacpi_status ret;
uacpi_object *hid_ret;
uacpi_id_string *id = UACPI_NULL;
uacpi_u32 size;
ret = uacpi_eval_typed(
node, "_HID", UACPI_NULL,
UACPI_OBJECT_INTEGER_BIT | UACPI_OBJECT_STRING_BIT,
&hid_ret
);
if (ret != UACPI_STATUS_OK)
return ret;
size = sizeof(uacpi_id_string);
switch (hid_ret->type) {
case UACPI_OBJECT_STRING: {
uacpi_buffer *buf = hid_ret->buffer;
size += buf->size;
if (uacpi_unlikely(buf->size == 0 || size < buf->size)) {
uacpi_object_name name = uacpi_namespace_node_name(node);
uacpi_error(
"%.4s._HID: empty/invalid EISA ID string (%zu bytes)\n",
name.text, buf->size
);
ret = UACPI_STATUS_AML_BAD_ENCODING;
break;
}
id = uacpi_kernel_alloc(size);
if (uacpi_unlikely(id == UACPI_NULL)) {
ret = UACPI_STATUS_OUT_OF_MEMORY;
break;
}
id->size = buf->size;
id->value = UACPI_PTR_ADD(id, sizeof(uacpi_id_string));
uacpi_memcpy(id->value, buf->text, buf->size);
id->value[buf->size - 1] = '\0';
break;
}
case UACPI_OBJECT_INTEGER:
size += PNP_ID_LENGTH;
id = uacpi_kernel_alloc(size);
if (uacpi_unlikely(id == UACPI_NULL)) {
ret = UACPI_STATUS_OUT_OF_MEMORY;
break;
}
id->size = PNP_ID_LENGTH;
id->value = UACPI_PTR_ADD(id, sizeof(uacpi_id_string));
uacpi_eisa_id_to_string(hid_ret->integer, id->value);
break;
}
uacpi_object_unref(hid_ret);
if (uacpi_likely_success(ret))
*out_id = id;
return ret;
}
void uacpi_free_id_string(uacpi_id_string *id)
{
if (id == UACPI_NULL)
return;
uacpi_free(id, sizeof(uacpi_id_string) + id->size);
}
uacpi_status uacpi_eval_cid(
uacpi_namespace_node *node, uacpi_pnp_id_list **out_list
)
{
uacpi_status ret;
uacpi_object *object, *cid_ret;
uacpi_object **objects;
uacpi_size num_ids, i;
uacpi_u32 size;
uacpi_id_string *id;
uacpi_char *id_buffer;
uacpi_pnp_id_list *list;
ret = uacpi_eval_typed(
node, "_CID", UACPI_NULL,
UACPI_OBJECT_INTEGER_BIT | UACPI_OBJECT_STRING_BIT |
UACPI_OBJECT_PACKAGE_BIT,
&cid_ret
);
if (ret != UACPI_STATUS_OK)
return ret;
switch (cid_ret->type) {
case UACPI_OBJECT_PACKAGE:
objects = cid_ret->package->objects;
num_ids = cid_ret->package->count;
break;
default:
objects = &cid_ret;
num_ids = 1;
break;
}
size = sizeof(uacpi_pnp_id_list);
size += num_ids * sizeof(uacpi_id_string);
for (i = 0; i < num_ids; ++i) {
object = objects[i];
switch (object->type) {
case UACPI_OBJECT_STRING: {
uacpi_size buf_size = object->buffer->size;
if (uacpi_unlikely(buf_size == 0)) {
uacpi_object_name name = uacpi_namespace_node_name(node);
uacpi_error(
"%.4s._CID: empty EISA ID string (sub-object %zu)\n",
name.text, i
);
return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
}
size += buf_size;
if (uacpi_unlikely(size < buf_size)) {
uacpi_object_name name = uacpi_namespace_node_name(node);
uacpi_error(
"%.4s._CID: buffer size overflow (+ %zu)\n",
name.text, buf_size
);
return UACPI_STATUS_AML_BAD_ENCODING;
}
break;
}
case UACPI_OBJECT_INTEGER:
size += PNP_ID_LENGTH;
break;
default: {
uacpi_object_name name = uacpi_namespace_node_name(node);
uacpi_error(
"%.4s._CID: invalid package sub-object %zu type: %s\n",
name.text, i,
uacpi_object_type_to_string(object->type)
);
return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE;
}
}
}
list = uacpi_kernel_alloc(size);
if (uacpi_unlikely(list == UACPI_NULL))
return UACPI_STATUS_OUT_OF_MEMORY;
list->num_ids = num_ids;
list->size = size - sizeof(uacpi_pnp_id_list);
id_buffer = UACPI_PTR_ADD(list, sizeof(uacpi_pnp_id_list));
id_buffer += num_ids * sizeof(uacpi_id_string);
for (i = 0; i < num_ids; ++i) {
object = objects[i];
id = &list->ids[i];
switch (object->type) {
case UACPI_OBJECT_STRING: {
uacpi_buffer *buf = object->buffer;
id->size = buf->size;
id->value = id_buffer;
uacpi_memcpy(id->value, buf->text, id->size);
id->value[id->size - 1] = '\0';
break;
}
case UACPI_OBJECT_INTEGER:
id->size = PNP_ID_LENGTH;
id->value = id_buffer;
uacpi_eisa_id_to_string(object->integer, id_buffer);
break;
}
id_buffer += id->size;
}
uacpi_object_unref(cid_ret);
*out_list = list;
return ret;
}
void uacpi_free_pnp_id_list(uacpi_pnp_id_list *list)
{
if (list == UACPI_NULL)
return;
uacpi_free(list, sizeof(uacpi_pnp_id_list) + list->size);
}
uacpi_status uacpi_eval_sta(uacpi_namespace_node *node, uacpi_u32 *flags)
{
uacpi_status ret;
uacpi_u64 value = 0;
ret = uacpi_eval_integer(node, "_STA", UACPI_NULL, &value);
/*
* ACPI 6.5 specification:
* If a device object (including the processor object) does not have
* an _STA object, then OSPM assumes that all of the above bits are
* set (i.e., the device is present, enabled, shown in the UI,
* and functioning).
*/
if (ret == UACPI_STATUS_NOT_FOUND) {
value = 0xFFFFFFFF;
ret = UACPI_STATUS_OK;
}
*flags = value;
return ret;
}
uacpi_status uacpi_eval_adr(uacpi_namespace_node *node, uacpi_u64 *out)
{
return uacpi_eval_integer(node, "_ADR", UACPI_NULL, out);
}
#define CLS_REPR_SIZE 7
static uacpi_u8 extract_package_byte_or_zero(uacpi_package *pkg, uacpi_size i)
{
uacpi_object *obj;
if (uacpi_unlikely(pkg->count <= i))
return 0;
obj = pkg->objects[i];
if (uacpi_unlikely(obj->type != UACPI_OBJECT_INTEGER))
return 0;
return obj->integer;
}
uacpi_status uacpi_eval_cls(
uacpi_namespace_node *node, uacpi_id_string **out_id
)
{
uacpi_status ret;
uacpi_object *obj;
uacpi_package *pkg;
uacpi_u8 class_codes[3];
uacpi_id_string *id_string;
ret = uacpi_eval_typed(
node, "_CLS", UACPI_NULL, UACPI_OBJECT_PACKAGE_BIT, &obj
);
if (ret != UACPI_STATUS_OK)
return ret;
pkg = obj->package;
class_codes[0] = extract_package_byte_or_zero(pkg, 0);
class_codes[1] = extract_package_byte_or_zero(pkg, 1);
class_codes[2] = extract_package_byte_or_zero(pkg, 2);
id_string = uacpi_kernel_alloc(sizeof(uacpi_id_string) + CLS_REPR_SIZE);
if (uacpi_unlikely(id_string == UACPI_NULL)) {
ret = UACPI_STATUS_OUT_OF_MEMORY;
goto out;
}
id_string->size = CLS_REPR_SIZE;
id_string->value = UACPI_PTR_ADD(id_string, sizeof(uacpi_id_string));
uacpi_snprintf(
id_string->value, CLS_REPR_SIZE, "%02X%02X%02X",
class_codes[0], class_codes[1], class_codes[2]
);
out:
if (uacpi_likely_success(ret))
*out_id = id_string;
uacpi_object_unref(obj);
return ret;
}
uacpi_status uacpi_eval_uid(
uacpi_namespace_node *node, uacpi_id_string **out_uid
)
{
uacpi_status ret;
uacpi_object *obj;
uacpi_id_string *id_string;
uacpi_u32 size;
ret = uacpi_eval_typed(
node, "_UID", UACPI_NULL,
UACPI_OBJECT_INTEGER_BIT | UACPI_OBJECT_STRING_BIT,
&obj
);
if (ret != UACPI_STATUS_OK)
return ret;
if (obj->type == UACPI_OBJECT_STRING) {
size = obj->buffer->size;
if (uacpi_unlikely(size == 0 || size > 0xE0000000)) {
uacpi_object_name name = uacpi_namespace_node_name(node);
uacpi_error(
"invalid %.4s._UID string size: %u\n",
name.text, size
);
ret = UACPI_STATUS_AML_BAD_ENCODING;
goto out;
}
} else {
size = uacpi_snprintf(
UACPI_NULL, 0, "%"UACPI_PRIu64, UACPI_FMT64(obj->integer)
) + 1;
}
id_string = uacpi_kernel_alloc(sizeof(uacpi_id_string) + size);
if (uacpi_unlikely(id_string == UACPI_NULL)) {
ret = UACPI_STATUS_OUT_OF_MEMORY;
goto out;
}
id_string->value = UACPI_PTR_ADD(id_string, sizeof(uacpi_id_string));
id_string->size = size;
if (obj->type == UACPI_OBJECT_STRING) {
uacpi_memcpy(id_string->value, obj->buffer->text, size);
id_string->value[size - 1] = '\0';
} else {
uacpi_snprintf(
id_string->value, id_string->size, "%"UACPI_PRIu64,
UACPI_FMT64(obj->integer)
);
}
out:
if (uacpi_likely_success(ret))
*out_uid = id_string;
uacpi_object_unref(obj);
return ret;
}
static uacpi_bool matches_any(
uacpi_id_string *id, const uacpi_char *const *ids
)
{
uacpi_size i;
for (i = 0; ids[i]; ++i) {
if (uacpi_strcmp(id->value, ids[i]) == 0)
return UACPI_TRUE;
}
return UACPI_FALSE;
}
static uacpi_status uacpi_eval_dstate_method_template(
uacpi_namespace_node *parent, uacpi_char *template, uacpi_u8 num_methods,
uacpi_u8 *out_values
)
{
uacpi_u8 i;
uacpi_status ret = UACPI_STATUS_NOT_FOUND, eval_ret;
uacpi_object *obj;
// We expect either _SxD or _SxW, so increment template[2]
for (i = 0; i < num_methods; ++i, template[2]++) {
eval_ret = uacpi_eval_typed(
parent, template, UACPI_NULL, UACPI_OBJECT_INTEGER_BIT, &obj
);
if (eval_ret == UACPI_STATUS_OK) {
ret = UACPI_STATUS_OK;
out_values[i] = obj->integer;
uacpi_object_unref(obj);
continue;
}
out_values[i] = 0xFF;
if (uacpi_unlikely(eval_ret != UACPI_STATUS_NOT_FOUND)) {
const char *path;
path = uacpi_namespace_node_generate_absolute_path(parent);
uacpi_warn(
"failed to evaluate %s.%s: %s\n",
path, template, uacpi_status_to_string(eval_ret)
);
uacpi_free_dynamic_string(path);
}
}
return ret;
}
#define NODE_INFO_EVAL_ADD_ID(name) \
if (uacpi_eval_##name(node, &name) == UACPI_STATUS_OK) { \
size += name->size; \
if (uacpi_unlikely(size < name->size)) { \
ret = UACPI_STATUS_AML_BAD_ENCODING; \
goto out; \
} \
}
#define NODE_INFO_COPY_ID(name, flag) \
if (name != UACPI_NULL) { \
flags |= UACPI_NS_NODE_INFO_HAS_##flag; \
info->name.value = cursor; \
info->name.size = name->size; \
uacpi_memcpy(cursor, name->value, name->size); \
cursor += name->size; \
} else { \
uacpi_memzero(&info->name, sizeof(*name)); \
} \
uacpi_status uacpi_get_namespace_node_info(
uacpi_namespace_node *node, uacpi_namespace_node_info **out_info
)
{
uacpi_status ret = UACPI_STATUS_OK;
uacpi_u32 size = sizeof(uacpi_namespace_node_info);
uacpi_object *obj;
uacpi_namespace_node_info *info;
uacpi_id_string *hid = UACPI_NULL, *uid = UACPI_NULL, *cls = UACPI_NULL;
uacpi_pnp_id_list *cid = UACPI_NULL;
uacpi_char *cursor;
uacpi_u64 adr = 0;
uacpi_u8 flags = 0;
uacpi_u8 sxd[4], sxw[5];
obj = uacpi_namespace_node_get_object(node);
if (uacpi_unlikely(obj == UACPI_NULL))
return UACPI_STATUS_INVALID_ARGUMENT;
if (obj->type == UACPI_OBJECT_DEVICE ||
obj->type == UACPI_OBJECT_PROCESSOR) {
char dstate_method_template[5] = { '_', 'S', '1', 'D', '\0' };
NODE_INFO_EVAL_ADD_ID(hid)
NODE_INFO_EVAL_ADD_ID(uid)
NODE_INFO_EVAL_ADD_ID(cls)
NODE_INFO_EVAL_ADD_ID(cid)
if (uacpi_eval_adr(node, &adr) == UACPI_STATUS_OK)
flags |= UACPI_NS_NODE_INFO_HAS_ADR;
if (uacpi_eval_dstate_method_template(
node, dstate_method_template, sizeof(sxd), sxd
) == UACPI_STATUS_OK)
flags |= UACPI_NS_NODE_INFO_HAS_SXD;
dstate_method_template[2] = '0';
dstate_method_template[3] = 'W';
if (uacpi_eval_dstate_method_template(
node, dstate_method_template, sizeof(sxw), sxw
) == UACPI_STATUS_OK)
flags |= UACPI_NS_NODE_INFO_HAS_SXW;
}
info = uacpi_kernel_alloc(size);
if (uacpi_unlikely(info == UACPI_NULL)) {
ret = UACPI_STATUS_OUT_OF_MEMORY;
goto out;
}
info->size = size;
cursor = UACPI_PTR_ADD(info, sizeof(uacpi_namespace_node_info));
info->name = uacpi_namespace_node_name(node);
info->type = obj->type;
info->num_params = info->type == UACPI_OBJECT_METHOD ? obj->method->args : 0;
info->adr = adr;
if (flags & UACPI_NS_NODE_INFO_HAS_SXD)
uacpi_memcpy(info->sxd, sxd, sizeof(sxd));
else
uacpi_memzero(info->sxd, sizeof(info->sxd));
if (flags & UACPI_NS_NODE_INFO_HAS_SXW)
uacpi_memcpy(info->sxw, sxw, sizeof(sxw));
else
uacpi_memzero(info->sxw, sizeof(info->sxw));
if (cid != UACPI_NULL) {
uacpi_u32 i;
uacpi_memcpy(&info->cid, cid, cid->size + sizeof(*cid));
cursor += cid->num_ids * sizeof(uacpi_id_string);
for (i = 0; i < cid->num_ids; ++i) {
info->cid.ids[i].value = cursor;
cursor += info->cid.ids[i].size;
}
flags |= UACPI_NS_NODE_INFO_HAS_CID;
} else {
uacpi_memzero(&info->cid, sizeof(*cid));
}
NODE_INFO_COPY_ID(hid, HID)
NODE_INFO_COPY_ID(uid, UID)
NODE_INFO_COPY_ID(cls, CLS)
out:
if (uacpi_likely_success(ret)) {
info->flags = flags;
*out_info = info;
}
uacpi_free_id_string(hid);
uacpi_free_id_string(uid);
uacpi_free_id_string(cls);
uacpi_free_pnp_id_list(cid);
return ret;
}
void uacpi_free_namespace_node_info(uacpi_namespace_node_info *info)
{
if (info == UACPI_NULL)
return;
uacpi_free(info, info->size);
}
uacpi_bool uacpi_device_matches_pnp_id(
uacpi_namespace_node *node, const uacpi_char *const *ids
)
{
uacpi_status st;
uacpi_bool ret = UACPI_FALSE;
uacpi_id_string *id = UACPI_NULL;
uacpi_pnp_id_list *id_list = UACPI_NULL;
st = uacpi_eval_hid(node, &id);
if (st == UACPI_STATUS_OK && matches_any(id, ids)) {
ret = UACPI_TRUE;
goto out;
}
st = uacpi_eval_cid(node, &id_list);
if (st == UACPI_STATUS_OK) {
uacpi_size i;
for (i = 0; i < id_list->num_ids; ++i) {
if (matches_any(&id_list->ids[i], ids)) {
ret = UACPI_TRUE;
goto out;
}
}
}
out:
uacpi_free_id_string(id);
uacpi_free_pnp_id_list(id_list);
return ret;
}
struct device_find_ctx {
const uacpi_char *const *target_hids;
void *user;
uacpi_iteration_callback cb;
};
static uacpi_iteration_decision find_one_device(
void *opaque, uacpi_namespace_node *node, uacpi_u32 depth
)
{
struct device_find_ctx *ctx = opaque;
uacpi_status ret;
uacpi_u32 flags;
if (!uacpi_device_matches_pnp_id(node, ctx->target_hids))
return UACPI_ITERATION_DECISION_CONTINUE;
ret = uacpi_eval_sta(node, &flags);
if (uacpi_unlikely_error(ret))
return UACPI_ITERATION_DECISION_NEXT_PEER;
if (!(flags & ACPI_STA_RESULT_DEVICE_PRESENT) &&
!(flags & ACPI_STA_RESULT_DEVICE_FUNCTIONING))
return UACPI_ITERATION_DECISION_NEXT_PEER;
return ctx->cb(ctx->user, node, depth);
}
uacpi_status uacpi_find_devices_at(
uacpi_namespace_node *parent, const uacpi_char *const *hids,
uacpi_iteration_callback cb, void *user
)
{
struct device_find_ctx ctx = { 0 };
UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
ctx.target_hids = hids;
ctx.user = user;
ctx.cb = cb;
return uacpi_namespace_for_each_child(
parent, find_one_device, UACPI_NULL, UACPI_OBJECT_DEVICE_BIT,
UACPI_MAX_DEPTH_ANY, &ctx
);
}
uacpi_status uacpi_find_devices(
const uacpi_char *hid, uacpi_iteration_callback cb, void *user
)
{
const uacpi_char *hids[2] = {
UACPI_NULL, UACPI_NULL
};
hids[0] = hid;
return uacpi_find_devices_at(uacpi_namespace_root(), hids, cb, user);
}
uacpi_status uacpi_set_interrupt_model(uacpi_interrupt_model model)
{
uacpi_status ret;
uacpi_object *arg;
uacpi_object_array args;
UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
arg = uacpi_create_object(UACPI_OBJECT_INTEGER);
if (uacpi_unlikely(arg == UACPI_NULL))
return UACPI_STATUS_OUT_OF_MEMORY;
arg->integer = model;
args.objects = &arg;
args.count = 1;
ret = uacpi_eval(uacpi_namespace_root(), "_PIC", &args, UACPI_NULL);
uacpi_object_unref(arg);
if (ret == UACPI_STATUS_NOT_FOUND)
ret = UACPI_STATUS_OK;
return ret;
}
uacpi_status uacpi_get_pci_routing_table(
uacpi_namespace_node *parent, uacpi_pci_routing_table **out_table
)
{
uacpi_status ret;
uacpi_object *obj, *entry_obj, *elem_obj;
uacpi_package *table_pkg, *entry_pkg;
uacpi_pci_routing_table_entry *entry;
uacpi_pci_routing_table *table;
uacpi_size size, i;
UACPI_ENSURE_INIT_LEVEL_AT_LEAST(UACPI_INIT_LEVEL_NAMESPACE_LOADED);
obj = uacpi_namespace_node_get_object(parent);
if (uacpi_unlikely(obj == UACPI_NULL || obj->type != UACPI_OBJECT_DEVICE))
return UACPI_STATUS_INVALID_ARGUMENT;
ret = uacpi_eval_typed(
parent, "_PRT", UACPI_NULL, UACPI_OBJECT_PACKAGE_BIT, &obj
);
if (uacpi_unlikely_error(ret))
return ret;
table_pkg = obj->package;
if (uacpi_unlikely(table_pkg->count == 0 || table_pkg->count > 1024)) {
uacpi_error("invalid number of _PRT entries: %zu\n", table_pkg->count);
uacpi_object_unref(obj);
return UACPI_STATUS_AML_BAD_ENCODING;
}
size = table_pkg->count * sizeof(uacpi_pci_routing_table_entry);
table = uacpi_kernel_alloc(sizeof(uacpi_pci_routing_table) + size);
if (uacpi_unlikely(table == UACPI_NULL)) {
uacpi_object_unref(obj);
return UACPI_STATUS_OUT_OF_MEMORY;
}
table->num_entries = table_pkg->count;
for (i = 0; i < table_pkg->count; ++i) {
entry_obj = table_pkg->objects[i];
if (uacpi_unlikely(entry_obj->type != UACPI_OBJECT_PACKAGE)) {
uacpi_error("_PRT sub-object %zu is not a package: %s\n",
i, uacpi_object_type_to_string(entry_obj->type));
goto out_bad_encoding;
}
entry_pkg = entry_obj->package;
if (uacpi_unlikely(entry_pkg->count != 4)) {
uacpi_error("invalid _PRT sub-package entry count %zu\n",
entry_pkg->count);
goto out_bad_encoding;
}
entry = &table->entries[i];
elem_obj = entry_pkg->objects[0];
if (uacpi_unlikely(elem_obj->type != UACPI_OBJECT_INTEGER)) {
uacpi_error("invalid _PRT sub-package %zu address type: %s\n",
i, uacpi_object_type_to_string(elem_obj->type));
goto out_bad_encoding;
}
entry->address = elem_obj->integer;
elem_obj = entry_pkg->objects[1];
if (uacpi_unlikely(elem_obj->type != UACPI_OBJECT_INTEGER)) {
uacpi_error("invalid _PRT sub-package %zu pin type: %s\n",
i, uacpi_object_type_to_string(elem_obj->type));
goto out_bad_encoding;
}
entry->pin = elem_obj->integer;
elem_obj = entry_pkg->objects[2];
switch (elem_obj->type) {
case UACPI_OBJECT_STRING:
ret = uacpi_object_resolve_as_aml_namepath(
elem_obj, parent, &entry->source
);
if (uacpi_unlikely_error(ret)) {
uacpi_error("unable to lookup _PRT source %s: %s\n",
elem_obj->buffer->text, uacpi_status_to_string(ret));
goto out_bad_encoding;
}
break;
case UACPI_OBJECT_INTEGER:
entry->source = UACPI_NULL;
break;
default:
uacpi_error("invalid _PRT sub-package %zu source type: %s\n",
i, uacpi_object_type_to_string(elem_obj->type));
goto out_bad_encoding;
}
elem_obj = entry_pkg->objects[3];
if (uacpi_unlikely(elem_obj->type != UACPI_OBJECT_INTEGER)) {
uacpi_error("invalid _PRT sub-package %zu source index type: %s\n",
i, uacpi_object_type_to_string(elem_obj->type));
goto out_bad_encoding;
}
entry->index = elem_obj->integer;
}
uacpi_object_unref(obj);
*out_table = table;
return UACPI_STATUS_OK;
out_bad_encoding:
uacpi_object_unref(obj);
uacpi_free_pci_routing_table(table);
return UACPI_STATUS_AML_BAD_ENCODING;
}
void uacpi_free_pci_routing_table(uacpi_pci_routing_table *table)
{
if (table == UACPI_NULL)
return;
uacpi_free(
table,
sizeof(uacpi_pci_routing_table) +
table->num_entries * sizeof(uacpi_pci_routing_table_entry)
);
}
void uacpi_free_dynamic_string(const uacpi_char *str)
{
if (str == UACPI_NULL)
return;
uacpi_free((void*)str, uacpi_strlen(str) + 1);
}
#endif // !UACPI_BAREBONES_MODE