#include #include #include #include #include #include #include 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