diff --git a/include/uacpi/internal/io.h b/include/uacpi/internal/io.h index d9153db9..413adbdd 100644 --- a/include/uacpi/internal/io.h +++ b/include/uacpi/internal/io.h @@ -13,11 +13,21 @@ void uacpi_write_buffer_field( uacpi_buffer_field *field, const void *src, uacpi_size size ); +uacpi_status uacpi_field_unit_get_read_type( + struct uacpi_field_unit *field, uacpi_object_type *out_type +); + +uacpi_status uacpi_field_unit_get_bit_length( + struct uacpi_field_unit *field, uacpi_size *out_length +); + uacpi_status uacpi_read_field_unit( - uacpi_field_unit *field, void *dst, uacpi_size size + uacpi_field_unit *field, void *dst, uacpi_size size, + uacpi_data_view *wtr_response ); uacpi_status uacpi_write_field_unit( - uacpi_field_unit *field, const void *src, uacpi_size size + uacpi_field_unit *field, const void *src, uacpi_size size, + uacpi_data_view *wtr_response ); uacpi_status uacpi_system_io_read( diff --git a/include/uacpi/internal/opcodes.h b/include/uacpi/internal/opcodes.h index 0f46cd84..85320e25 100644 --- a/include/uacpi/internal/opcodes.h +++ b/include/uacpi/internal/opcodes.h @@ -665,9 +665,10 @@ UACPI_OP( \ StoreOp, 0x70, \ { \ UACPI_PARSE_OP_TERM_ARG, \ - UACPI_PARSE_OP_OBJECT_COPY_TO_PREV, \ UACPI_PARSE_OP_SUPERNAME, \ UACPI_PARSE_OP_INVOKE_HANDLER, \ + UACPI_PARSE_OP_ITEM_POP, \ + UACPI_PARSE_OP_OBJECT_COPY_TO_PREV, \ }, \ UACPI_OP_PROPERTY_TERM_ARG \ ) \ diff --git a/include/uacpi/internal/opregion.h b/include/uacpi/internal/opregion.h index c6285184..ae6ed986 100644 --- a/include/uacpi/internal/opregion.h +++ b/include/uacpi/internal/opregion.h @@ -9,10 +9,6 @@ void uacpi_deinitialize_opregion(void); void uacpi_trace_region_error( uacpi_namespace_node *node, uacpi_char *message, uacpi_status ret ); -void uacpi_trace_region_io( - uacpi_namespace_node *node, uacpi_address_space space, uacpi_region_op op, - uacpi_u64 offset, uacpi_u8 byte_size, uacpi_u64 ret -); uacpi_status uacpi_install_address_space_handler_with_flags( uacpi_namespace_node *device_node, enum uacpi_address_space space, @@ -36,7 +32,14 @@ uacpi_status uacpi_opregion_attach(uacpi_namespace_node *node); void uacpi_install_default_address_space_handlers(void); +uacpi_bool uacpi_is_buffer_access_address_space(uacpi_address_space space); + +union uacpi_opregion_io_data { + uacpi_u64 *integer; + uacpi_data_view buffer; +}; + uacpi_status uacpi_dispatch_opregion_io( uacpi_field_unit *field, uacpi_u32 offset, - uacpi_region_op op, uacpi_u64 *in_out + uacpi_region_op op, union uacpi_opregion_io_data data ); diff --git a/include/uacpi/types.h b/include/uacpi/types.h index 6c6aaa0d..5013976e 100644 --- a/include/uacpi/types.h +++ b/include/uacpi/types.h @@ -367,6 +367,8 @@ typedef enum uacpi_region_op { UACPI_REGION_OP_PCC_SEND, UACPI_REGION_OP_GPIO_READ, UACPI_REGION_OP_GPIO_WRITE, + + UACPI_REGION_OP_IPMI_COMMAND, } uacpi_region_op; typedef struct uacpi_generic_region_info { @@ -422,6 +424,14 @@ typedef struct uacpi_region_gpio_rw_data uacpi_u64 value; } uacpi_region_gpio_rw_data; +typedef struct uacpi_region_ipmi_rw_data +{ + void *handler_context; + void *region_context; + uacpi_data_view in_out_message; + uacpi_u64 command; +} uacpi_region_ipmi_rw_data; + typedef struct uacpi_region_detach_data { void *handler_context; void *region_context; diff --git a/source/interpreter.c b/source/interpreter.c index 202d1a22..760c09f5 100644 --- a/source/interpreter.c +++ b/source/interpreter.c @@ -867,18 +867,6 @@ static uacpi_status handle_package(struct execution_context *ctx) return UACPI_STATUS_OK; } -static uacpi_size field_byte_size(uacpi_object *obj) -{ - uacpi_size bit_length; - - if (obj->type == UACPI_OBJECT_BUFFER_FIELD) - bit_length = obj->buffer_field.bit_length; - else - bit_length = obj->field_unit->bit_length; - - return uacpi_round_up_bits_to_bytes(bit_length); -} - static uacpi_size sizeof_int(void) { return g_uacpi_rt_ctx.is_rev1 ? 4 : 8; @@ -941,8 +929,9 @@ static void write_buffer_index(uacpi_buffer_index *buf_idx, * the specification. In reality, we just copy one buffer to another * because that's what NT does. */ -static uacpi_status object_assign_with_implicit_cast(uacpi_object *dst, - uacpi_object *src) +static uacpi_status object_assign_with_implicit_cast( + uacpi_object *dst, uacpi_object *src, uacpi_data_view *wtr_response +) { uacpi_status ret; uacpi_data_view src_buf; @@ -975,7 +964,8 @@ static uacpi_status object_assign_with_implicit_cast(uacpi_object *dst, case UACPI_OBJECT_FIELD_UNIT: return uacpi_write_field_unit( - dst->field_unit, src_buf.bytes, src_buf.length + dst->field_unit, src_buf.bytes, src_buf.length, + wtr_response ); case UACPI_OBJECT_BUFFER_INDEX: @@ -2279,8 +2269,9 @@ static void object_replace_child(uacpi_object *parent, uacpi_object *new_child) * 3. NAME -> Store with implicit cast. * 4. RefOf -> Not allowed here. */ -static uacpi_status store_to_reference(uacpi_object *dst, - uacpi_object *src) +static uacpi_status store_to_reference( + uacpi_object *dst, uacpi_object *src, uacpi_data_view *wtr_response +) { uacpi_object *src_obj; uacpi_bool overwrite = UACPI_FALSE; @@ -2335,7 +2326,9 @@ static uacpi_status store_to_reference(uacpi_object *dst, return UACPI_STATUS_OK; } - return object_assign_with_implicit_cast(dst->inner_object, src_obj); + return object_assign_with_implicit_cast( + dst->inner_object, src_obj, wtr_response + ); } static uacpi_status handle_ref_or_deref_of(struct execution_context *ctx) @@ -3769,48 +3762,69 @@ static uacpi_object_type buffer_field_get_read_type( return UACPI_OBJECT_INTEGER; } -static uacpi_object_type field_unit_get_read_type( - struct uacpi_field_unit *field +static uacpi_status field_get_read_type( + uacpi_object *obj, uacpi_object_type *out_type ) { - if (field->bit_length > (g_uacpi_rt_ctx.is_rev1 ? 32u : 64u)) - return UACPI_OBJECT_BUFFER; + if (obj->type == UACPI_OBJECT_BUFFER_FIELD) { + *out_type = buffer_field_get_read_type(&obj->buffer_field); + return UACPI_STATUS_OK; + } - return UACPI_OBJECT_INTEGER; + return uacpi_field_unit_get_read_type(obj->field_unit, out_type); } -static uacpi_object_type field_get_read_type(uacpi_object *obj) +static uacpi_status field_byte_size( + uacpi_object *obj, uacpi_size *out_size +) { - if (obj->type == UACPI_OBJECT_BUFFER_FIELD) - return buffer_field_get_read_type(&obj->buffer_field); + uacpi_size bit_length; - return field_unit_get_read_type(obj->field_unit); + if (obj->type == UACPI_OBJECT_BUFFER_FIELD) { + bit_length = obj->buffer_field.bit_length; + } else { + uacpi_status ret; + + ret = uacpi_field_unit_get_bit_length(obj->field_unit, &bit_length); + if (uacpi_unlikely_error(ret)) + return ret; + } + + *out_size = uacpi_round_up_bits_to_bytes(bit_length); + return UACPI_STATUS_OK; } static uacpi_status handle_field_read(struct execution_context *ctx) { + uacpi_status ret; struct op_context *op_ctx = ctx->cur_op_ctx; struct uacpi_namespace_node *node; uacpi_object *src_obj, *dst_obj; uacpi_size dst_size; - void *dst; + void *dst = UACPI_NULL; + uacpi_data_view wtr_response = { 0 }; node = item_array_at(&op_ctx->items, 0)->node; src_obj = uacpi_namespace_node_get_object(node); dst_obj = item_array_at(&op_ctx->items, 1)->obj; - if (field_get_read_type(src_obj) == UACPI_OBJECT_BUFFER) { + if (op_ctx->op->code == UACPI_AML_OP_InternalOpReadFieldAsBuffer) { uacpi_buffer *buf; - buf = dst_obj->buffer; - dst_size = field_byte_size(src_obj); + ret = field_byte_size(src_obj, &dst_size); + if (uacpi_unlikely_error(ret)) + return ret; - dst = uacpi_kernel_alloc_zeroed(dst_size); - if (dst == UACPI_NULL) - return UACPI_STATUS_OUT_OF_MEMORY; + if (dst_size != 0) { + buf = dst_obj->buffer; + + dst = uacpi_kernel_alloc_zeroed(dst_size); + if (dst == UACPI_NULL) + return UACPI_STATUS_OUT_OF_MEMORY; - buf->data = dst; - buf->size = dst_size; + buf->data = dst; + buf->size = dst_size; + } } else { dst = &dst_obj->integer; dst_size = sizeof(uacpi_u64); @@ -3821,7 +3835,21 @@ static uacpi_status handle_field_read(struct execution_context *ctx) return UACPI_STATUS_OK; } - return uacpi_read_field_unit(src_obj->field_unit, dst, dst_size); + ret = uacpi_read_field_unit( + src_obj->field_unit, dst, dst_size, &wtr_response + ); + if (uacpi_unlikely_error(ret)) + return ret; + + if (wtr_response.data != UACPI_NULL) { + uacpi_buffer *buf; + + buf = dst_obj->buffer; + buf->data = wtr_response.data; + buf->size = wtr_response.length; + } + + return ret; } static uacpi_status handle_create_buffer_field(struct execution_context *ctx) @@ -4192,7 +4220,9 @@ static uacpi_bool maybe_end_block(struct execution_context *ctx) return UACPI_TRUE; } -static uacpi_status store_to_target(uacpi_object *dst, uacpi_object *src) +static uacpi_status store_to_target( + uacpi_object *dst, uacpi_object *src, uacpi_data_view *wtr_response +) { uacpi_status ret; @@ -4201,12 +4231,12 @@ static uacpi_status store_to_target(uacpi_object *dst, uacpi_object *src) ret = debug_store(src); break; case UACPI_OBJECT_REFERENCE: - ret = store_to_reference(dst, src); + ret = store_to_reference(dst, src, wtr_response); break; case UACPI_OBJECT_BUFFER_INDEX: src = uacpi_unwrap_internal_reference(src); - ret = object_assign_with_implicit_cast(dst, src); + ret = object_assign_with_implicit_cast(dst, src, wtr_response); break; case UACPI_OBJECT_INTEGER: @@ -4233,8 +4263,35 @@ static uacpi_status handle_copy_object_or_store(struct execution_context *ctx) src = item_array_at(&op_ctx->items, 0)->obj; dst = item_array_at(&op_ctx->items, 1)->obj; - if (op_ctx->op->code == UACPI_AML_OP_StoreOp) - return store_to_target(dst, src); + if (op_ctx->op->code == UACPI_AML_OP_StoreOp) { + uacpi_status ret; + uacpi_data_view wtr_response = { 0 }; + + ret = store_to_target(dst, src, &wtr_response); + if (uacpi_unlikely_error(ret)) + return ret; + + /* + * This was a write-then-read field access since we got a response + * buffer back from this store. Now we have to return this buffer + * as a prvalue from the StoreOp so that it can be used by AML to + * retrieve the response. + */ + if (wtr_response.data != UACPI_NULL) { + uacpi_object *wtr_response_obj; + + wtr_response_obj = uacpi_object_create_buffer(wtr_response); + if (wtr_response_obj == UACPI_NULL) { + uacpi_free(wtr_response.data, wtr_response.length); + return UACPI_STATUS_OUT_OF_MEMORY; + } + + uacpi_object_unref(src); + item_array_at(&op_ctx->items, 0)->obj = wtr_response_obj; + } + + return ret; + } if (dst->type != UACPI_OBJECT_REFERENCE) return UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE; @@ -4279,13 +4336,16 @@ static uacpi_status handle_inc_dec(struct execution_context *ctx) if (uacpi_unlikely(!field_allowed)) goto out_bad_type; - true_src_type = field_get_read_type(src); + ret = field_get_read_type(src, &true_src_type); + if (uacpi_unlikely_error(ret)) + goto out_bad_type; if (true_src_type != UACPI_OBJECT_INTEGER) goto out_bad_type; if (src->type == UACPI_OBJECT_FIELD_UNIT) { ret = uacpi_read_field_unit( - src->field_unit, &dst->integer, sizeof_int() + src->field_unit, &dst->integer, sizeof_int(), + UACPI_NULL ); if (uacpi_unlikely_error(ret)) return ret; @@ -5497,7 +5557,7 @@ static uacpi_status exec_op(struct execution_context *ctx) src = item->obj; } - ret = store_to_target(dst, src); + ret = store_to_target(dst, src, UACPI_NULL); break; } @@ -5589,11 +5649,28 @@ static uacpi_status exec_op(struct execution_context *ctx) } case UACPI_OBJECT_BUFFER_FIELD: - case UACPI_OBJECT_FIELD_UNIT: + case UACPI_OBJECT_FIELD_UNIT: { + uacpi_object_type type; + if (!op_wants_term_arg_or_operand(prev_op)) break; - switch (field_get_read_type(obj)) { + ret = field_get_read_type(obj, &type); + if (uacpi_unlikely_error(ret)) { + const uacpi_char *field_path; + + field_path = uacpi_namespace_node_generate_absolute_path( + item->node + ); + + uacpi_error( + "unable to perform a read from field %s: " + "parent opregion gone\n", field_path + ); + uacpi_free_absolute_path(field_path); + } + + switch (type) { case UACPI_OBJECT_BUFFER: new_op = UACPI_AML_OP_InternalOpReadFieldAsBuffer; break; @@ -5605,6 +5682,7 @@ static uacpi_status exec_op(struct execution_context *ctx) continue; } break; + } default: break; } diff --git a/source/io.c b/source/io.c index f1e62fd4..336fa071 100644 --- a/source/io.c +++ b/source/io.c @@ -176,7 +176,7 @@ void uacpi_write_buffer_field( static uacpi_status access_field_unit( uacpi_field_unit *field, uacpi_u32 offset, uacpi_region_op op, - uacpi_u64 *in_out + union uacpi_opregion_io_data data ) { uacpi_status ret = UACPI_STATUS_OK; @@ -192,14 +192,16 @@ static uacpi_status access_field_unit( switch (field->kind) { case UACPI_FIELD_UNIT_KIND_BANK: ret = uacpi_write_field_unit( - field->bank_selection, &field->bank_value, sizeof(field->bank_value) + field->bank_selection, &field->bank_value, sizeof(field->bank_value), + UACPI_NULL ); break; case UACPI_FIELD_UNIT_KIND_NORMAL: break; case UACPI_FIELD_UNIT_KIND_INDEX: ret = uacpi_write_field_unit( - field->index, &offset, sizeof(offset) + field->index, &offset, sizeof(offset), + UACPI_NULL ); if (uacpi_unlikely_error(ret)) goto out; @@ -207,12 +209,14 @@ static uacpi_status access_field_unit( switch (op) { case UACPI_REGION_OP_READ: ret = uacpi_read_field_unit( - field->data, in_out, field->access_width_bytes + field->data, data.integer, field->access_width_bytes, + UACPI_NULL ); break; case UACPI_REGION_OP_WRITE: ret = uacpi_write_field_unit( - field->data, in_out, field->access_width_bytes + field->data, data.integer, field->access_width_bytes, + UACPI_NULL ); break; default: @@ -230,7 +234,7 @@ static uacpi_status access_field_unit( if (uacpi_unlikely_error(ret)) goto out; - ret = uacpi_dispatch_opregion_io(field, offset, op, in_out); + ret = uacpi_dispatch_opregion_io(field, offset, op, data); out: if (field->lock_rule) @@ -238,59 +242,113 @@ static uacpi_status access_field_unit( return ret; } +#define OPREGION_IO_U64(x) (union uacpi_opregion_io_data) { .integer = x } + +#define SERIAL_HEADER_SIZE 2 +#define IPMI_DATA_SIZE 64 + +static uacpi_size wtr_buffer_size( + uacpi_field_unit *field, uacpi_address_space space +) +{ + UACPI_UNUSED(field); + + switch (space) { + case UACPI_ADDRESS_SPACE_IPMI: + return SERIAL_HEADER_SIZE + IPMI_DATA_SIZE; + default: + return 0; + } +} + static uacpi_status handle_special_field( uacpi_field_unit *field, uacpi_data_view buf, - uacpi_region_op op, uacpi_bool *did_handle + uacpi_region_op op, uacpi_data_view *wtr_response, + uacpi_bool *did_handle ) { uacpi_status ret = UACPI_STATUS_OK; uacpi_object *obj; uacpi_operation_region *region; - uacpi_namespace_node *region_node; uacpi_u64 in_out; *did_handle = UACPI_FALSE; - switch (field->kind) { - case UACPI_FIELD_UNIT_KIND_BANK: - region_node = field->region; - break; - case UACPI_FIELD_UNIT_KIND_NORMAL: - region_node = field->bank_region; - break; - case UACPI_FIELD_UNIT_KIND_INDEX: - default: + if (field->kind == UACPI_FIELD_UNIT_KIND_INDEX) return ret; - } obj = uacpi_namespace_node_get_object_typed( - region_node, UACPI_OBJECT_OPERATION_REGION_BIT + field->region, UACPI_OBJECT_OPERATION_REGION_BIT ); if (uacpi_unlikely(obj == UACPI_NULL)) { ret = UACPI_STATUS_INVALID_ARGUMENT; + uacpi_trace_region_error( + field->region, "attempted access to deleted", ret + ); goto out_handled; } region = obj->op_region; switch (region->space) { case UACPI_ADDRESS_SPACE_GENERAL_PURPOSE_IO: + if (op == UACPI_REGION_OP_WRITE) { + uacpi_memcpy_zerout( + &in_out, buf.const_data, sizeof(in_out), buf.length + ); + } + + ret = access_field_unit(field, 0, op, OPREGION_IO_U64(&in_out)); + if (uacpi_unlikely_error(ret)) + goto out_handled; + + if (op == UACPI_REGION_OP_READ) + uacpi_memcpy_zerout(buf.data, &in_out, buf.length, sizeof(in_out)); break; - default: - return ret; - } - if (op == UACPI_REGION_OP_WRITE) { + case UACPI_ADDRESS_SPACE_IPMI: { + uacpi_data_view wtr_buffer; + + if (uacpi_unlikely(op == UACPI_REGION_OP_READ)) { + const uacpi_char *path; + + path = uacpi_namespace_node_generate_absolute_path(field->region); + uacpi_error( + "aborting attempt to read from an IPMI opregion '%s'\n", + path + ); + uacpi_free_absolute_path(path); + + + ret = UACPI_STATUS_AML_BAD_ENCODING; + goto out_handled; + } + + wtr_buffer.length = wtr_buffer_size(field, region->space); + + wtr_buffer.data = uacpi_kernel_alloc(wtr_buffer.length); + if (uacpi_unlikely(wtr_buffer.data == UACPI_NULL)) { + ret = UACPI_STATUS_OUT_OF_MEMORY; + goto out_handled; + } + uacpi_memcpy_zerout( - &in_out, buf.const_data, sizeof(in_out), buf.length + wtr_buffer.data, buf.const_data, wtr_buffer.length, buf.length ); - } - - ret = access_field_unit(field, 0, op, &in_out); - if (uacpi_unlikely_error(ret)) - goto out_handled; + ret = access_field_unit(field, 0, op, (union uacpi_opregion_io_data) { + .buffer = wtr_buffer, + }); + if (uacpi_unlikely_error(ret)) { + uacpi_free(wtr_buffer.data, wtr_buffer.length); + goto out_handled; + } - if (op == UACPI_REGION_OP_READ) - uacpi_memcpy_zerout(buf.data, &in_out, buf.length, sizeof(in_out)); + if (wtr_response != UACPI_NULL) + *wtr_response = wtr_buffer; + break; + } + default: + return ret; + } out_handled: *did_handle = UACPI_TRUE; @@ -332,7 +390,7 @@ static uacpi_status do_read_misaligned_field_unit( ret = access_field_unit( field, byte_offset, UACPI_REGION_OP_READ, - &out + OPREGION_IO_U64(&out) ); if (uacpi_unlikely_error(ret)) return ret; @@ -349,7 +407,8 @@ static uacpi_status do_read_misaligned_field_unit( } uacpi_status uacpi_read_field_unit( - uacpi_field_unit *field, void *dst, uacpi_size size + uacpi_field_unit *field, void *dst, uacpi_size size, + uacpi_data_view *wtr_response ) { uacpi_status ret; @@ -360,7 +419,8 @@ uacpi_status uacpi_read_field_unit( field, (uacpi_data_view) { .data = dst, .length = size, - }, UACPI_REGION_OP_READ, &did_handle + }, UACPI_REGION_OP_READ, + wtr_response, &did_handle ); if (did_handle) return ret; @@ -379,7 +439,8 @@ uacpi_status uacpi_read_field_unit( uacpi_u64 out; ret = access_field_unit( - field, field->byte_offset, UACPI_REGION_OP_READ, &out + field, field->byte_offset, UACPI_REGION_OP_READ, + OPREGION_IO_U64(&out) ); if (uacpi_unlikely_error(ret)) return ret; @@ -426,7 +487,8 @@ static uacpi_status write_generic_field_unit( switch (field->update_rule) { case UACPI_UPDATE_RULE_PRESERVE: ret = access_field_unit( - field, byte_offset, UACPI_REGION_OP_READ, &in + field, byte_offset, UACPI_REGION_OP_READ, + OPREGION_IO_U64(&in) ); if (uacpi_unlikely_error(ret)) return ret; @@ -447,7 +509,8 @@ static uacpi_status write_generic_field_unit( bit_span_offset(&src_span, dst_span.length); ret = access_field_unit( - field, byte_offset, UACPI_REGION_OP_WRITE, &in + field, byte_offset, UACPI_REGION_OP_WRITE, + OPREGION_IO_U64(&in) ); if (uacpi_unlikely_error(ret)) return ret; @@ -461,7 +524,8 @@ static uacpi_status write_generic_field_unit( } uacpi_status uacpi_write_field_unit( - uacpi_field_unit *field, const void *src, uacpi_size size + uacpi_field_unit *field, const void *src, uacpi_size size, + uacpi_data_view *wtr_response ) { uacpi_status ret; @@ -471,7 +535,8 @@ uacpi_status uacpi_write_field_unit( field, (uacpi_data_view) { .const_data = src, .length = size, - }, UACPI_REGION_OP_WRITE, &did_handle + }, UACPI_REGION_OP_WRITE, + wtr_response, &did_handle ); if (did_handle) return ret; @@ -479,6 +544,64 @@ uacpi_status uacpi_write_field_unit( return write_generic_field_unit(field, src, size); } +uacpi_status uacpi_field_unit_get_read_type( + struct uacpi_field_unit *field, uacpi_object_type *out_type +) +{ + uacpi_object *obj; + + if (field->kind == UACPI_FIELD_UNIT_KIND_INDEX) + goto out_basic_field; + + obj = uacpi_namespace_node_get_object_typed( + field->region, UACPI_OBJECT_OPERATION_REGION_BIT + ); + if (uacpi_unlikely(obj == UACPI_NULL)) + return UACPI_STATUS_INVALID_ARGUMENT; + + if (uacpi_is_buffer_access_address_space(obj->op_region->space)) { + *out_type = UACPI_OBJECT_BUFFER; + return UACPI_STATUS_OK; + } + +out_basic_field: + if (field->bit_length > (g_uacpi_rt_ctx.is_rev1 ? 32u : 64u)) + *out_type = UACPI_OBJECT_BUFFER; + else + *out_type = UACPI_OBJECT_INTEGER; + + return UACPI_STATUS_OK; +} + +uacpi_status uacpi_field_unit_get_bit_length( + struct uacpi_field_unit *field, uacpi_size *out_length +) +{ + uacpi_object *obj; + + if (field->kind == UACPI_FIELD_UNIT_KIND_INDEX) + goto out_basic_field; + + obj = uacpi_namespace_node_get_object_typed( + field->region, UACPI_OBJECT_OPERATION_REGION_BIT + ); + if (uacpi_unlikely(obj == UACPI_NULL)) + return UACPI_STATUS_INVALID_ARGUMENT; + + if (uacpi_is_buffer_access_address_space(obj->op_region->space)) { + /* + * Bit length is protocol specific, the data will be returned + * via the write-then-read response buffer. + */ + *out_length = 0; + return UACPI_STATUS_OK; + } + +out_basic_field: + *out_length = field->bit_length; + return UACPI_STATUS_OK; +} + static uacpi_u8 gas_get_access_bit_width(const struct acpi_gas *gas) { /* diff --git a/source/opregion.c b/source/opregion.c index ebc461c2..be4f25e6 100644 --- a/source/opregion.c +++ b/source/opregion.c @@ -42,14 +42,11 @@ void uacpi_trace_region_error( uacpi_free_dynamic_string(path); } -#define UACPI_TRACE_REGION_IO - -void uacpi_trace_region_io( - uacpi_namespace_node *node, uacpi_address_space space, uacpi_region_op op, - uacpi_u64 offset, uacpi_u8 byte_size, uacpi_u64 ret +static void trace_region_io( + uacpi_field_unit *field, uacpi_address_space space, uacpi_u64 offset, + uacpi_region_op op, union uacpi_opregion_io_data data ) { -#ifdef UACPI_TRACE_REGION_IO const uacpi_char *path; const uacpi_char *type_str; @@ -67,23 +64,36 @@ void uacpi_trace_region_io( type_str = ""; } - path = uacpi_namespace_node_generate_absolute_path(node); + path = uacpi_namespace_node_generate_absolute_path(field->region); - uacpi_trace( - "%s [%s] (%d bytes) %s[0x%016"UACPI_PRIX64"] = 0x%"UACPI_PRIX64"\n", - type_str, path, byte_size, - uacpi_address_space_to_string(space), - UACPI_FMT64(offset), UACPI_FMT64(ret) - ); + switch (space) { + case UACPI_ADDRESS_SPACE_IPMI: + uacpi_trace( + "write-then-read from [%s] %s[0x%016"UACPI_PRIX64"] = " + "\n", path, + uacpi_address_space_to_string(space), + UACPI_FMT64(offset), data.buffer.length + ); + break; + case UACPI_ADDRESS_SPACE_GENERAL_PURPOSE_IO: + uacpi_trace( + "%s [%s] %s pins[%u..%u] = 0x%"UACPI_PRIX64"\n", + type_str, path, uacpi_address_space_to_string(space), + field->pin_offset, field->pin_offset + field->bit_length, + UACPI_FMT64(data.integer) + ); + break; + default: + uacpi_trace( + "%s [%s] (%d bytes) %s[0x%016"UACPI_PRIX64"] = 0x%"UACPI_PRIX64"\n", + type_str, path, field->access_width_bytes, + uacpi_address_space_to_string(space), + UACPI_FMT64(offset), UACPI_FMT64(*data.integer) + ); + break; + } uacpi_free_dynamic_string(path); -#else - UACPI_UNUSED(op); - UACPI_UNUSED(node); - UACPI_UNUSED(offset); - UACPI_UNUSED(byte_size); - UACPI_UNUSED(ret); -#endif } static uacpi_bool space_needs_reg(enum uacpi_address_space space) @@ -802,7 +812,7 @@ uacpi_status uacpi_initialize_opregion_node(uacpi_namespace_node *node) return ret; } -static uacpi_bool space_needs_bounds_checking(uacpi_address_space space) +uacpi_bool uacpi_is_buffer_access_address_space(uacpi_address_space space) { switch (space) { case UACPI_ADDRESS_SPACE_SMBUS: @@ -811,15 +821,20 @@ static uacpi_bool space_needs_bounds_checking(uacpi_address_space space) case UACPI_ADDRESS_SPACE_GENERIC_SERIAL_BUS: case UACPI_ADDRESS_SPACE_PRM: case UACPI_ADDRESS_SPACE_FFIXEDHW: - return UACPI_FALSE; - default: return UACPI_TRUE; + default: + return UACPI_FALSE; } } +static uacpi_bool space_needs_bounds_checking(uacpi_address_space space) +{ + return !uacpi_is_buffer_access_address_space(space); +} + uacpi_status uacpi_dispatch_opregion_io( uacpi_field_unit *field, uacpi_u32 offset, uacpi_region_op op, - uacpi_u64 *in_out + union uacpi_opregion_io_data data ) { uacpi_status ret; @@ -829,12 +844,14 @@ uacpi_status uacpi_dispatch_opregion_io( uacpi_address_space space; uacpi_u64 abs_offset, offset_end = offset; uacpi_bool is_oob = UACPI_FALSE; + uacpi_region_op orig_op = op; union { uacpi_region_rw_data rw; uacpi_region_pcc_send_data pcc; uacpi_region_gpio_rw_data gpio; - } data; + uacpi_region_ipmi_rw_data ipmi; + } handler_data; ret = upgrade_to_opregion_lock(); if (uacpi_unlikely_error(ret)) @@ -881,15 +898,8 @@ uacpi_status uacpi_dispatch_opregion_io( goto out; } - if (op == UACPI_REGION_OP_WRITE) { - uacpi_trace_region_io( - field->region, space, op, abs_offset, - field->access_width_bytes, *in_out - ); - } - - data.rw.region_context = region->user_context; - data.rw.handler_context = handler->user_context; + handler_data.rw.region_context = region->user_context; + handler_data.rw.handler_context = handler->user_context; switch (region->space) { case UACPI_ADDRESS_SPACE_PCC: { @@ -903,12 +913,13 @@ uacpi_status uacpi_dispatch_opregion_io( */ if (op == UACPI_REGION_OP_READ) { uacpi_memcpy_zerout( - in_out, cursor, sizeof(*in_out), field->access_width_bytes + data.integer, cursor, sizeof(*data.integer), + field->access_width_bytes ); goto io_done; } - uacpi_memcpy(cursor, in_out, field->access_width_bytes); + uacpi_memcpy(cursor, data.integer, field->access_width_bytes); /* * Dispatch a PCC send command if this was a write to the command field @@ -916,7 +927,7 @@ uacpi_status uacpi_dispatch_opregion_io( * ACPI 6.5: 14.3. Extended PCC Subspace Shared Memory Region */ if (offset >= 12 && offset < 16) { - data.pcc.buffer = (uacpi_data_view){ + handler_data.pcc.buffer = (uacpi_data_view){ .bytes = region->internal_buffer, .length = region->length, }; @@ -929,11 +940,11 @@ uacpi_status uacpi_dispatch_opregion_io( goto io_done; } case UACPI_ADDRESS_SPACE_GENERAL_PURPOSE_IO: { - data.gpio.pin_offset = field->pin_offset; - data.gpio.num_pins = field->bit_length; + handler_data.gpio.pin_offset = field->pin_offset; + handler_data.gpio.num_pins = field->bit_length; ret = uacpi_object_get_string_or_buffer( - field->connection, &data.gpio.connection + field->connection, &handler_data.gpio.connection ); if (uacpi_unlikely_error(ret)) goto io_done; @@ -942,17 +953,23 @@ uacpi_status uacpi_dispatch_opregion_io( UACPI_REGION_OP_GPIO_READ : UACPI_REGION_OP_GPIO_WRITE; break; } + case UACPI_ADDRESS_SPACE_IPMI: { + handler_data.ipmi.in_out_message = data.buffer; + handler_data.ipmi.command = abs_offset; + op = UACPI_REGION_OP_IPMI_COMMAND; + break; + } default: - data.rw.byte_width = field->access_width_bytes; - data.rw.offset = abs_offset; - data.rw.value = *in_out; + handler_data.rw.byte_width = field->access_width_bytes; + handler_data.rw.offset = abs_offset; + handler_data.rw.value = *data.integer; break; } uacpi_object_ref(obj); uacpi_namespace_write_unlock(); - ret = handler->callback(op, &data); + ret = handler->callback(op, &handler_data); uacpi_namespace_write_lock(); uacpi_object_unref(obj); @@ -968,16 +985,13 @@ uacpi_status uacpi_dispatch_opregion_io( case UACPI_ADDRESS_SPACE_PCC: break; default: - *in_out = data.rw.value; + *data.integer = handler_data.rw.value; break; } - - uacpi_trace_region_io( - field->region, space, op, abs_offset, - field->access_width_bytes, *in_out - ); } + trace_region_io(field, space, abs_offset, orig_op, data); + out: uacpi_recursive_lock_release(&g_opregion_lock); return ret;