#include #include #include #include #include #include #include #ifndef UACPI_BAREBONES_MODE uacpi_size uacpi_round_up_bits_to_bytes(uacpi_size bit_length) { return UACPI_ALIGN_UP(bit_length, 8, uacpi_size) / 8; } static void cut_misaligned_tail( uacpi_u8 *data, uacpi_size offset, uacpi_u32 bit_length ) { uacpi_u8 remainder = bit_length & 7; if (remainder == 0) return; data[offset] &= ((1ull << remainder) - 1); } struct bit_span { union { uacpi_u8 *data; const uacpi_u8 *const_data; }; uacpi_u64 index; uacpi_u64 length; }; static uacpi_size bit_span_offset(struct bit_span *span, uacpi_size bits) { uacpi_size delta = UACPI_MIN(span->length, bits); span->index += delta; span->length -= delta; return delta; } static void bit_copy(struct bit_span *dst, struct bit_span *src) { uacpi_u8 src_shift, dst_shift, bits = 0; uacpi_u16 dst_mask; uacpi_u8 *dst_ptr, *src_ptr; uacpi_u64 dst_count, src_count; dst_ptr = dst->data + (dst->index / 8); src_ptr = src->data + (src->index / 8); dst_count = dst->length; dst_shift = dst->index & 7; src_count = src->length; src_shift = src->index & 7; while (dst_count) { bits = 0; if (src_count) { bits = *src_ptr >> src_shift; if (src_shift && src_count > (uacpi_u32)(8 - src_shift)) bits |= *(src_ptr + 1) << (8 - src_shift); if (src_count < 8) { bits &= (1 << src_count) - 1; src_count = 0; } else { src_count -= 8; src_ptr++; } } dst_mask = (dst_count < 8 ? (1 << dst_count) - 1 : 0xFF) << dst_shift; *dst_ptr = (*dst_ptr & ~dst_mask) | ((bits << dst_shift) & dst_mask); if (dst_shift && dst_count > (uacpi_u32)(8 - dst_shift)) { dst_mask >>= 8; *(dst_ptr + 1) &= ~dst_mask; *(dst_ptr + 1) |= (bits >> (8 - dst_shift)) & dst_mask; } dst_count = dst_count > 8 ? dst_count - 8 : 0; ++dst_ptr; } } static void do_misaligned_buffer_read( const uacpi_buffer_field *field, uacpi_u8 *dst ) { struct bit_span src_span = { 0 }; struct bit_span dst_span = { 0 }; src_span.index = field->bit_index; src_span.length = field->bit_length; src_span.const_data = field->backing->data; dst_span.data = dst; dst_span.length = uacpi_round_up_bits_to_bytes(field->bit_length) * 8; bit_copy(&dst_span, &src_span); } void uacpi_read_buffer_field( const uacpi_buffer_field *field, void *dst ) { if (!(field->bit_index & 7)) { uacpi_u8 *src = field->backing->data; uacpi_size count; count = uacpi_round_up_bits_to_bytes(field->bit_length); uacpi_memcpy(dst, src + (field->bit_index / 8), count); cut_misaligned_tail(dst, count - 1, field->bit_length); return; } do_misaligned_buffer_read(field, dst); } static void do_write_misaligned_buffer_field( uacpi_buffer_field *field, const void *src, uacpi_size size ) { struct bit_span src_span = { 0 }; struct bit_span dst_span = { 0 }; src_span.length = size * 8; src_span.const_data = src; dst_span.index = field->bit_index; dst_span.length = field->bit_length; dst_span.data = field->backing->data; bit_copy(&dst_span, &src_span); } void uacpi_write_buffer_field( uacpi_buffer_field *field, const void *src, uacpi_size size ) { if (!(field->bit_index & 7)) { uacpi_u8 *dst, last_byte, tail_shift; uacpi_size count; dst = field->backing->data; dst += field->bit_index / 8; count = uacpi_round_up_bits_to_bytes(field->bit_length); last_byte = dst[count - 1]; tail_shift = field->bit_length & 7; uacpi_memcpy_zerout(dst, src, count, size); if (tail_shift) { uacpi_u8 last_shift = 8 - tail_shift; dst[count - 1] = dst[count - 1] << last_shift; dst[count - 1] >>= last_shift; dst[count - 1] |= (last_byte >> tail_shift) << tail_shift; } return; } do_write_misaligned_buffer_field(field, src, size); } static uacpi_status access_field_unit( uacpi_field_unit *field, uacpi_u32 offset, uacpi_region_op op, union uacpi_opregion_io_data data ) { uacpi_status ret = UACPI_STATUS_OK; if (field->lock_rule) { ret = uacpi_acquire_aml_mutex( g_uacpi_rt_ctx.global_lock_mutex, 0xFFFF ); if (uacpi_unlikely_error(ret)) return ret; } switch (field->kind) { case UACPI_FIELD_UNIT_KIND_BANK: ret = uacpi_write_field_unit( 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), UACPI_NULL ); if (uacpi_unlikely_error(ret)) goto out; switch (op) { case UACPI_REGION_OP_READ: ret = uacpi_read_field_unit( field->data, data.integer, field->access_width_bytes, UACPI_NULL ); break; case UACPI_REGION_OP_WRITE: ret = uacpi_write_field_unit( field->data, data.integer, field->access_width_bytes, UACPI_NULL ); break; default: ret = UACPI_STATUS_INVALID_ARGUMENT; break; } goto out; default: uacpi_error("invalid field unit kind %d\n", field->kind); ret = UACPI_STATUS_INVALID_ARGUMENT; } if (uacpi_unlikely_error(ret)) goto out; ret = uacpi_dispatch_opregion_io(field, offset, op, data); out: if (field->lock_rule) uacpi_release_aml_mutex(g_uacpi_rt_ctx.global_lock_mutex); return ret; } #define SERIAL_HEADER_SIZE 2 #define IPMI_DATA_SIZE 64 static uacpi_status wtr_buffer_size( uacpi_field_unit *field, uacpi_address_space space, uacpi_size *out_size ) { switch (space) { case UACPI_ADDRESS_SPACE_IPMI: *out_size = SERIAL_HEADER_SIZE + IPMI_DATA_SIZE; break; case UACPI_ADDRESS_SPACE_PRM: *out_size = 26; break; case UACPI_ADDRESS_SPACE_FFIXEDHW: *out_size = 256; break; case UACPI_ADDRESS_SPACE_GENERIC_SERIAL_BUS: case UACPI_ADDRESS_SPACE_SMBUS: { uacpi_size size_for_protocol = SERIAL_HEADER_SIZE; switch (field->attributes) { case UACPI_ACCESS_ATTRIBUTE_QUICK: break; // + 0 case UACPI_ACCESS_ATTRIBUTE_SEND_RECEIVE: case UACPI_ACCESS_ATTRIBUTE_BYTE: size_for_protocol += 1; break; case UACPI_ACCESS_ATTRIBUTE_WORD: case UACPI_ACCESS_ATTRIBUTE_PROCESS_CALL: size_for_protocol += 2; break; case UACPI_ACCESS_ATTRIBUTE_BYTES: size_for_protocol += field->access_length; break; case UACPI_ACCESS_ATTRIBUTE_BLOCK: case UACPI_ACCESS_ATTRIBUTE_BLOCK_PROCESS_CALL: case UACPI_ACCESS_ATTRIBUTE_RAW_BYTES: case UACPI_ACCESS_ATTRIBUTE_RAW_PROCESS_BYTES: size_for_protocol += 255; break; default: uacpi_error( "unsupported field@%p access attribute %d\n", field, field->attributes ); return UACPI_STATUS_UNIMPLEMENTED; } *out_size = size_for_protocol; break; } default: return UACPI_STATUS_INVALID_ARGUMENT; } return UACPI_STATUS_OK; } static uacpi_status handle_special_field( uacpi_field_unit *field, uacpi_data_view buf, 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_u64 in_out; uacpi_data_view wtr_buffer; union uacpi_opregion_io_data data; *did_handle = UACPI_FALSE; if (field->kind == UACPI_FIELD_UNIT_KIND_INDEX) return ret; obj = uacpi_namespace_node_get_object_typed( 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 ); } data.integer = &in_out; ret = access_field_unit(field, 0, op, data); 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)); goto out_handled; case UACPI_ADDRESS_SPACE_IPMI: case UACPI_ADDRESS_SPACE_PRM: if (uacpi_unlikely(op == UACPI_REGION_OP_READ)) { ret = UACPI_STATUS_AML_INCOMPATIBLE_OBJECT_TYPE; uacpi_trace_region_error( field->region, "attempted to read from a write-only", ret ); goto out_handled; } UACPI_FALLTHROUGH; case UACPI_ADDRESS_SPACE_FFIXEDHW: case UACPI_ADDRESS_SPACE_GENERIC_SERIAL_BUS: case UACPI_ADDRESS_SPACE_SMBUS: goto do_wtr; default: return ret; } do_wtr: ret = wtr_buffer_size(field, region->space, &wtr_buffer.length); if (uacpi_unlikely_error(ret)) goto out_handled; 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( wtr_buffer.data, buf.const_data, wtr_buffer.length, buf.length ); data.buffer = wtr_buffer; ret = access_field_unit( field, field->byte_offset, op, data ); if (uacpi_unlikely_error(ret)) { uacpi_free(wtr_buffer.data, wtr_buffer.length); goto out_handled; } if (wtr_response != UACPI_NULL) *wtr_response = wtr_buffer; out_handled: *did_handle = UACPI_TRUE; return ret; } static uacpi_status do_read_misaligned_field_unit( uacpi_field_unit *field, uacpi_u8 *dst, uacpi_size size ) { uacpi_status ret; uacpi_size reads_to_do; uacpi_u64 out; uacpi_u32 byte_offset = field->byte_offset; uacpi_u32 bits_left = field->bit_length; uacpi_u8 width_access_bits = field->access_width_bytes * 8; struct bit_span src_span = { 0 }; struct bit_span dst_span = { 0 }; src_span.data = (uacpi_u8*)&out; src_span.index = field->bit_offset_within_first_byte; dst_span.data = dst; dst_span.index = 0; dst_span.length = size * 8; reads_to_do = UACPI_ALIGN_UP( field->bit_offset_within_first_byte + field->bit_length, width_access_bits, uacpi_u32 ); reads_to_do /= width_access_bits; while (reads_to_do-- > 0) { union uacpi_opregion_io_data data; src_span.length = UACPI_MIN( bits_left, width_access_bits - src_span.index ); data.integer = &out; ret = access_field_unit( field, byte_offset, UACPI_REGION_OP_READ, data ); if (uacpi_unlikely_error(ret)) return ret; bit_copy(&dst_span, &src_span); bits_left -= src_span.length; src_span.index = 0; bit_span_offset(&dst_span, src_span.length); byte_offset += field->access_width_bytes; } return UACPI_STATUS_OK; } uacpi_status uacpi_read_field_unit( uacpi_field_unit *field, void *dst, uacpi_size size, uacpi_data_view *wtr_response ) { uacpi_status ret; uacpi_u32 field_byte_length; uacpi_bool did_handle; uacpi_data_view data_view = { 0 }; data_view.data = dst; data_view.length = size; ret = handle_special_field( field, data_view, UACPI_REGION_OP_READ, wtr_response, &did_handle ); if (did_handle) return ret; field_byte_length = uacpi_round_up_bits_to_bytes(field->bit_length); /* * Very simple fast case: * - Bit offset within first byte is 0 * AND * - Field size is <= access width */ if (field->bit_offset_within_first_byte == 0 && field_byte_length <= field->access_width_bytes) { uacpi_u64 out; union uacpi_opregion_io_data data; data.integer = &out; ret = access_field_unit( field, field->byte_offset, UACPI_REGION_OP_READ, data ); if (uacpi_unlikely_error(ret)) return ret; uacpi_memcpy_zerout(dst, &out, size, field_byte_length); if (size >= field_byte_length) cut_misaligned_tail(dst, field_byte_length - 1, field->bit_length); return UACPI_STATUS_OK; } // Slow case return do_read_misaligned_field_unit(field, dst, size); } static uacpi_status write_generic_field_unit( uacpi_field_unit *field, const void *src, uacpi_size size ) { uacpi_status ret; uacpi_u32 bits_left, byte_offset = field->byte_offset; uacpi_u8 width_access_bits = field->access_width_bytes * 8; uacpi_u64 in; struct bit_span src_span = { 0 }; struct bit_span dst_span = { 0 }; src_span.const_data = src; src_span.index = 0; src_span.length = size * 8; dst_span.data = (uacpi_u8 *)∈ dst_span.index = field->bit_offset_within_first_byte; bits_left = field->bit_length; while (bits_left) { union uacpi_opregion_io_data data; in = 0; dst_span.length = UACPI_MIN( width_access_bits - dst_span.index, bits_left ); if (dst_span.index != 0 || dst_span.length < width_access_bits) { switch (field->update_rule) { case UACPI_UPDATE_RULE_PRESERVE: data.integer = ∈ ret = access_field_unit( field, byte_offset, UACPI_REGION_OP_READ, data ); if (uacpi_unlikely_error(ret)) return ret; break; case UACPI_UPDATE_RULE_WRITE_AS_ONES: in = ~in; break; case UACPI_UPDATE_RULE_WRITE_AS_ZEROES: break; default: uacpi_error("invalid field@%p update rule %d\n", field, field->update_rule); return UACPI_STATUS_INVALID_ARGUMENT; } } bit_copy(&dst_span, &src_span); bit_span_offset(&src_span, dst_span.length); data.integer = ∈ ret = access_field_unit( field, byte_offset, UACPI_REGION_OP_WRITE, data ); if (uacpi_unlikely_error(ret)) return ret; bits_left -= dst_span.length; dst_span.index = 0; byte_offset += field->access_width_bytes; } return UACPI_STATUS_OK; } uacpi_status uacpi_write_field_unit( uacpi_field_unit *field, const void *src, uacpi_size size, uacpi_data_view *wtr_response ) { uacpi_status ret; uacpi_bool did_handle; uacpi_data_view data_view = { 0 }; data_view.const_data = src; data_view.length = size; ret = handle_special_field( field, data_view, UACPI_REGION_OP_WRITE, wtr_response, &did_handle ); if (did_handle) return ret; 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) { /* * Same algorithm as ACPICA. * * The reason we do this is apparently GAS bit offset being non-zero means * that it's an APEI register, as opposed to FADT, which needs special * handling. In the case of a FADT register we want to ignore the specified * access size. */ uacpi_u8 access_bit_width; if (gas->register_bit_offset == 0 && UACPI_IS_POWER_OF_TWO(gas->register_bit_width, uacpi_u8) && UACPI_IS_ALIGNED(gas->register_bit_width, 8, uacpi_u8)) { access_bit_width = gas->register_bit_width; } else if (gas->access_size) { access_bit_width = gas->access_size * 8; } else { uacpi_u8 msb; msb = uacpi_bit_scan_backward( (gas->register_bit_offset + gas->register_bit_width) - 1 ); access_bit_width = 1 << msb; if (access_bit_width <= 8) { access_bit_width = 8; } else { /* * Keep backing off to previous power of two until we find one * that is aligned to the address specified in GAS. */ while (!UACPI_IS_ALIGNED( gas->address, access_bit_width / 8, uacpi_u64 )) access_bit_width /= 2; } } return UACPI_MIN( access_bit_width, gas->address_space_id == UACPI_ADDRESS_SPACE_SYSTEM_IO ? 32 : 64 ); } static uacpi_status gas_validate( const struct acpi_gas *gas, uacpi_u8 *access_bit_width, uacpi_u8 *bit_width ) { uacpi_size total_width, aligned_width; if (uacpi_unlikely(gas == UACPI_NULL)) return UACPI_STATUS_INVALID_ARGUMENT; if (!gas->address) return UACPI_STATUS_NOT_FOUND; if (gas->address_space_id != UACPI_ADDRESS_SPACE_SYSTEM_IO && gas->address_space_id != UACPI_ADDRESS_SPACE_SYSTEM_MEMORY) { uacpi_warn("unsupported GAS address space '%s' (%d)\n", uacpi_address_space_to_string(gas->address_space_id), gas->address_space_id); return UACPI_STATUS_UNIMPLEMENTED; } if (gas->access_size > 4) { uacpi_warn("unsupported GAS access size %d\n", gas->access_size); return UACPI_STATUS_UNIMPLEMENTED; } *access_bit_width = gas_get_access_bit_width(gas); total_width = gas->register_bit_offset + gas->register_bit_width; aligned_width = UACPI_ALIGN_UP(total_width, *access_bit_width, uacpi_size); if (uacpi_unlikely(aligned_width > 64)) { uacpi_warn( "GAS register total width is too large: %zu\n", total_width ); return UACPI_STATUS_UNIMPLEMENTED; } *bit_width = total_width; return UACPI_STATUS_OK; } /* * Apparently both reading and writing GAS works differently from operation * region in that bit offsets are not respected when writing the data. * * Let's follow ACPICA's approach here so that we don't accidentally * break any quirky hardware. */ uacpi_status uacpi_gas_read_mapped( const uacpi_mapped_gas *gas, uacpi_u64 *out_value ) { uacpi_status ret; uacpi_u8 access_byte_width; uacpi_u8 bit_offset, bits_left, index = 0; uacpi_u64 data, mask = 0xFFFFFFFFFFFFFFFF; uacpi_size offset = 0; bit_offset = gas->bit_offset; bits_left = gas->total_bit_width; access_byte_width = gas->access_bit_width / 8; if (access_byte_width < 8) mask = ~(mask << gas->access_bit_width); *out_value = 0; while (bits_left) { if (bit_offset >= gas->access_bit_width) { data = 0; bit_offset -= gas->access_bit_width; } else { ret = gas->read(gas->mapping, offset, access_byte_width, &data); if (uacpi_unlikely_error(ret)) return ret; } *out_value |= (data & mask) << (index * gas->access_bit_width); bits_left -= UACPI_MIN(bits_left, gas->access_bit_width); ++index; offset += access_byte_width; } return UACPI_STATUS_OK; } uacpi_status uacpi_gas_write_mapped( const uacpi_mapped_gas *gas, uacpi_u64 in_value ) { uacpi_status ret; uacpi_u8 access_byte_width; uacpi_u8 bit_offset, bits_left, index = 0; uacpi_u64 data, mask = 0xFFFFFFFFFFFFFFFF; uacpi_size offset = 0; bit_offset = gas->bit_offset; bits_left = gas->total_bit_width; access_byte_width = gas->access_bit_width / 8; if (access_byte_width < 8) mask = ~(mask << gas->access_bit_width); while (bits_left) { data = (in_value >> (index * gas->access_bit_width)) & mask; if (bit_offset >= gas->access_bit_width) { bit_offset -= gas->access_bit_width; } else { ret = gas->write(gas->mapping, offset, access_byte_width, data); if (uacpi_unlikely_error(ret)) return ret; } bits_left -= UACPI_MIN(bits_left, gas->access_bit_width); ++index; offset += access_byte_width; } return UACPI_STATUS_OK; } static void unmap_gas_io(uacpi_handle io_handle, uacpi_size size) { UACPI_UNUSED(size); uacpi_kernel_io_unmap(io_handle); } uacpi_status uacpi_map_gas_noalloc( const struct acpi_gas *gas, uacpi_mapped_gas *out_mapped ) { uacpi_status ret; uacpi_u8 access_bit_width, total_width; ret = gas_validate(gas, &access_bit_width, &total_width); if (ret != UACPI_STATUS_OK) return ret; if (gas->address_space_id == UACPI_ADDRESS_SPACE_SYSTEM_MEMORY) { out_mapped->mapping = uacpi_kernel_map(gas->address, total_width / 8); if (uacpi_unlikely(out_mapped->mapping == UACPI_NULL)) return UACPI_STATUS_MAPPING_FAILED; out_mapped->read = uacpi_system_memory_read; out_mapped->write = uacpi_system_memory_write; out_mapped->unmap = uacpi_kernel_unmap; } else { // IO, validated by gas_validate above ret = uacpi_kernel_io_map(gas->address, total_width / 8, &out_mapped->mapping); if (uacpi_unlikely_error(ret)) return ret; out_mapped->read = uacpi_system_io_read; out_mapped->write = uacpi_system_io_write; out_mapped->unmap = unmap_gas_io; } out_mapped->access_bit_width = access_bit_width; out_mapped->total_bit_width = total_width; out_mapped->bit_offset = gas->register_bit_offset; return UACPI_STATUS_OK; } uacpi_status uacpi_map_gas( const struct acpi_gas *gas, uacpi_mapped_gas **out_mapped ) { uacpi_status ret; uacpi_mapped_gas *mapping; mapping = uacpi_kernel_alloc(sizeof(*mapping)); if (uacpi_unlikely(mapping == UACPI_NULL)) return UACPI_STATUS_OUT_OF_MEMORY; ret = uacpi_map_gas_noalloc(gas, mapping); if (uacpi_unlikely_error(ret)) { uacpi_free(mapping, sizeof(*mapping)); return ret; } *out_mapped = mapping; return ret; } void uacpi_unmap_gas_nofree(uacpi_mapped_gas *gas) { gas->unmap(gas->mapping, gas->access_bit_width / 8); } void uacpi_unmap_gas(uacpi_mapped_gas *gas) { uacpi_unmap_gas_nofree(gas); uacpi_free(gas, sizeof(*gas)); } uacpi_status uacpi_gas_read(const struct acpi_gas *gas, uacpi_u64 *out_value) { uacpi_status ret; uacpi_mapped_gas mapping; ret = uacpi_map_gas_noalloc(gas, &mapping); if (uacpi_unlikely_error(ret)) return ret; ret = uacpi_gas_read_mapped(&mapping, out_value); uacpi_unmap_gas_nofree(&mapping); return ret; } uacpi_status uacpi_gas_write(const struct acpi_gas *gas, uacpi_u64 in_value) { uacpi_status ret; uacpi_mapped_gas mapping; ret = uacpi_map_gas_noalloc(gas, &mapping); if (uacpi_unlikely_error(ret)) return ret; ret = uacpi_gas_write_mapped(&mapping, in_value); uacpi_unmap_gas_nofree(&mapping); return ret; } uacpi_status uacpi_system_memory_read( void *ptr, uacpi_size offset, uacpi_u8 width, uacpi_u64 *out ) { ptr = UACPI_PTR_ADD(ptr, offset); switch (width) { case 1: *out = *(volatile uacpi_u8*)ptr; break; case 2: *out = *(volatile uacpi_u16*)ptr; break; case 4: *out = *(volatile uacpi_u32*)ptr; break; case 8: *out = *(volatile uacpi_u64*)ptr; break; default: return UACPI_STATUS_INVALID_ARGUMENT; } return UACPI_STATUS_OK; } uacpi_status uacpi_system_memory_write( void *ptr, uacpi_size offset, uacpi_u8 width, uacpi_u64 in ) { ptr = UACPI_PTR_ADD(ptr, offset); switch (width) { case 1: *(volatile uacpi_u8*)ptr = in; break; case 2: *(volatile uacpi_u16*)ptr = in; break; case 4: *(volatile uacpi_u32*)ptr = in; break; case 8: *(volatile uacpi_u64*)ptr = in; break; default: return UACPI_STATUS_INVALID_ARGUMENT; } return UACPI_STATUS_OK; } union integer_data { uacpi_u8 byte; uacpi_u16 word; uacpi_u32 dword; uacpi_u64 qword; }; uacpi_status uacpi_system_io_read( uacpi_handle handle, uacpi_size offset, uacpi_u8 width, uacpi_u64 *out ) { uacpi_status ret; union integer_data data = { .qword = 0, }; switch (width) { case 1: ret = uacpi_kernel_io_read8(handle, offset, &data.byte); break; case 2: ret = uacpi_kernel_io_read16(handle, offset, &data.word); break; case 4: ret = uacpi_kernel_io_read32(handle, offset, &data.dword); break; default: uacpi_error( "invalid SystemIO read %p@%zu width=%d\n", handle, offset, width ); return UACPI_STATUS_INVALID_ARGUMENT; } if (uacpi_likely_success(ret)) *out = data.qword; return ret; } uacpi_status uacpi_system_io_write( uacpi_handle handle, uacpi_size offset, uacpi_u8 width, uacpi_u64 in ) { uacpi_status ret; switch (width) { case 1: ret = uacpi_kernel_io_write8(handle, offset, in); break; case 2: ret = uacpi_kernel_io_write16(handle, offset, in); break; case 4: ret = uacpi_kernel_io_write32(handle, offset, in); break; default: uacpi_error( "invalid SystemIO write %p@%zu width=%d\n", handle, offset, width ); return UACPI_STATUS_INVALID_ARGUMENT; } return ret; } uacpi_status uacpi_pci_read( uacpi_handle handle, uacpi_size offset, uacpi_u8 width, uacpi_u64 *out ) { uacpi_status ret; union integer_data data = { .qword = 0, }; switch (width) { case 1: ret = uacpi_kernel_pci_read8(handle, offset, &data.byte); break; case 2: ret = uacpi_kernel_pci_read16(handle, offset, &data.word); break; case 4: ret = uacpi_kernel_pci_read32(handle, offset, &data.dword); break; default: uacpi_error( "invalid PCI_Config read %p@%zu width=%d\n", handle, offset, width ); return UACPI_STATUS_INVALID_ARGUMENT; } if (uacpi_likely_success(ret)) *out = data.qword; return ret; } uacpi_status uacpi_pci_write( uacpi_handle handle, uacpi_size offset, uacpi_u8 width, uacpi_u64 in ) { uacpi_status ret; switch (width) { case 1: ret = uacpi_kernel_pci_write8(handle, offset, in); break; case 2: ret = uacpi_kernel_pci_write16(handle, offset, in); break; case 4: ret = uacpi_kernel_pci_write32(handle, offset, in); break; default: uacpi_error( "invalid PCI_Config write %p@%zu width=%d\n", handle, offset, width ); return UACPI_STATUS_INVALID_ARGUMENT; } return ret; } #endif // !UACPI_BAREBONES_MODE