Integrate uACPI
This commit is contained in:
1
kernel/hal/x86_64/uACPI/tests/runner/.gitignore
vendored
Normal file
1
kernel/hal/x86_64/uACPI/tests/runner/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
build-*
|
168
kernel/hal/x86_64/uACPI/tests/runner/CMakeLists.txt
Normal file
168
kernel/hal/x86_64/uACPI/tests/runner/CMakeLists.txt
Normal file
@ -0,0 +1,168 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(TestRunner C)
|
||||
|
||||
set(CMAKE_C_STANDARD 17)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
|
||||
include(${CMAKE_CURRENT_SOURCE_DIR}/../../uacpi.cmake)
|
||||
|
||||
foreach(CONF_TYPE ${CMAKE_CONFIGURATION_TYPES})
|
||||
string(TOUPPER ${CONF_TYPE} CONF_TYPE)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CONF_TYPE} ${CMAKE_BINARY_DIR})
|
||||
endforeach(CONF_TYPE ${CMAKE_CONFIGURATION_TYPES})
|
||||
|
||||
macro (define_test_runner NAME)
|
||||
add_executable(
|
||||
${NAME}
|
||||
${ARGN}
|
||||
)
|
||||
target_sources(
|
||||
${NAME}
|
||||
PRIVATE
|
||||
${UACPI_SOURCES}
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
${NAME}
|
||||
PRIVATE
|
||||
${UACPI_INCLUDES}
|
||||
)
|
||||
|
||||
if (WATCOM)
|
||||
# Address sanitizer doesn't exist on Watcom.
|
||||
target_compile_definitions(
|
||||
${NAME}
|
||||
PRIVATE
|
||||
_LINUX_SOURCE
|
||||
)
|
||||
target_compile_options(
|
||||
${NAME}
|
||||
PRIVATE
|
||||
-we -wx
|
||||
)
|
||||
elseif (MSVC)
|
||||
# Address sanitizer on MSVC depends on a dynamic library that is not present in
|
||||
# PATH by default. Lets just not enable it here.
|
||||
target_compile_options(
|
||||
${NAME}
|
||||
PRIVATE
|
||||
/W3 /WX
|
||||
/wd4200 /wd4267 /wd4244
|
||||
)
|
||||
else ()
|
||||
target_compile_definitions(
|
||||
${NAME}
|
||||
PRIVATE
|
||||
_GNU_SOURCE
|
||||
)
|
||||
target_compile_options(
|
||||
${NAME}
|
||||
PRIVATE
|
||||
-fsanitize=address,undefined -g3 -Wall -Wextra -Werror
|
||||
)
|
||||
target_link_options(
|
||||
${NAME}
|
||||
PRIVATE
|
||||
-fsanitize=address,undefined -g3
|
||||
)
|
||||
add_compile_options(
|
||||
$<$<COMPILE_LANGUAGE:C>:-Wstrict-prototypes>
|
||||
)
|
||||
endif ()
|
||||
endmacro ()
|
||||
|
||||
define_test_runner(
|
||||
test-runner
|
||||
test_runner.c
|
||||
helpers.c
|
||||
interface_impl.c
|
||||
resource_tests.c
|
||||
api_tests.c
|
||||
)
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(test-runner PRIVATE Threads::Threads)
|
||||
|
||||
define_test_runner(
|
||||
barebones-test-runner
|
||||
helpers.c
|
||||
barebones_runner.c
|
||||
)
|
||||
target_compile_definitions(
|
||||
barebones-test-runner
|
||||
PRIVATE
|
||||
-DUACPI_BAREBONES_MODE
|
||||
)
|
||||
|
||||
if (NOT REDUCED_HARDWARE_BUILD)
|
||||
set(REDUCED_HARDWARE_BUILD 0)
|
||||
endif()
|
||||
|
||||
if (REDUCED_HARDWARE_BUILD)
|
||||
target_compile_definitions(
|
||||
test-runner
|
||||
PRIVATE
|
||||
-DUACPI_REDUCED_HARDWARE
|
||||
)
|
||||
endif ()
|
||||
|
||||
if (NOT DEFINED SIZED_FREES_BUILD)
|
||||
set(SIZED_FREES_BUILD 1)
|
||||
endif()
|
||||
|
||||
if (SIZED_FREES_BUILD)
|
||||
target_compile_definitions(
|
||||
test-runner
|
||||
PRIVATE
|
||||
-DUACPI_SIZED_FREES
|
||||
)
|
||||
endif ()
|
||||
|
||||
if (NOT FORMATTED_LOGGING_BUILD)
|
||||
set(FORMATTED_LOGGING_BUILD 0)
|
||||
endif()
|
||||
|
||||
if (FORMATTED_LOGGING_BUILD)
|
||||
target_compile_definitions(
|
||||
test-runner
|
||||
PRIVATE
|
||||
-DUACPI_FORMATTED_LOGGING
|
||||
)
|
||||
endif ()
|
||||
|
||||
|
||||
if (NOT NATIVE_ALLOC_ZEROED)
|
||||
set(NATIVE_ALLOC_ZEROED 0)
|
||||
endif()
|
||||
|
||||
if (NATIVE_ALLOC_ZEROED)
|
||||
target_compile_definitions(
|
||||
test-runner
|
||||
PRIVATE
|
||||
-DUACPI_NATIVE_ALLOC_ZEROED
|
||||
)
|
||||
endif ()
|
||||
|
||||
if (NOT KERNEL_INITIALIZATION)
|
||||
set(KERNEL_INITIALIZATION 1)
|
||||
endif()
|
||||
|
||||
if (KERNEL_INITIALIZATION)
|
||||
target_compile_definitions(
|
||||
test-runner
|
||||
PRIVATE
|
||||
-DUACPI_KERNEL_INITIALIZATION
|
||||
)
|
||||
endif ()
|
||||
|
||||
if (NOT BUILTIN_STRING)
|
||||
set(BUILTIN_STRING 0)
|
||||
endif()
|
||||
|
||||
if (BUILTIN_STRING)
|
||||
target_compile_definitions(
|
||||
test-runner
|
||||
PRIVATE
|
||||
-DUACPI_USE_BUILTIN_STRING
|
||||
)
|
||||
endif ()
|
475
kernel/hal/x86_64/uACPI/tests/runner/api_tests.c
Normal file
475
kernel/hal/x86_64/uACPI/tests/runner/api_tests.c
Normal file
@ -0,0 +1,475 @@
|
||||
#include "helpers.h"
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <uacpi/opregion.h>
|
||||
#include <uacpi/resources.h>
|
||||
#include <uacpi/types.h>
|
||||
|
||||
static void check_ok(uacpi_object **objects, uacpi_object_array *arr)
|
||||
{
|
||||
uacpi_u64 ret;
|
||||
uacpi_status st = uacpi_eval_integer(UACPI_NULL, "CHEK", arr, &ret);
|
||||
|
||||
ensure_ok_status(st);
|
||||
if (!ret)
|
||||
error("integer check failed");
|
||||
uacpi_object_unref(objects[1]);
|
||||
}
|
||||
|
||||
void test_object_api(void)
|
||||
{
|
||||
uacpi_status st;
|
||||
uacpi_object_array arr;
|
||||
uacpi_object *objects[2];
|
||||
uacpi_data_view view;
|
||||
uacpi_object *tmp;
|
||||
uint8_t buffer[4] = { 0xDE, 0xAD, 0xBE, 0xEF };
|
||||
uacpi_object *pkg[3];
|
||||
uacpi_object_array arr1;
|
||||
|
||||
arr.objects = objects;
|
||||
arr.count = UACPI_ARRAY_SIZE(objects);
|
||||
objects[0] = uacpi_object_create_integer(1);
|
||||
|
||||
st = uacpi_object_create_integer_safe(
|
||||
0xDEADBEEFDEADBEEF, UACPI_OVERFLOW_DISALLOW, &objects[1]
|
||||
);
|
||||
if (st != UACPI_STATUS_INVALID_ARGUMENT)
|
||||
error("expected integer creation to fail");
|
||||
|
||||
objects[1] = uacpi_object_create_integer(0xDEADBEEF);
|
||||
check_ok(objects, &arr);
|
||||
|
||||
st = uacpi_object_assign_integer(objects[0], 2);
|
||||
ensure_ok_status(st);
|
||||
|
||||
objects[1] = uacpi_object_create_cstring("Hello World");
|
||||
uacpi_object_ref(objects[1]);
|
||||
check_ok(objects, &arr);
|
||||
|
||||
view.const_text = "Hello World";
|
||||
// Don't include the null byte to check if this is accounted for
|
||||
view.length = 11;
|
||||
|
||||
uacpi_object_assign_string(objects[1], view);
|
||||
check_ok(objects, &arr);
|
||||
|
||||
st = uacpi_object_assign_integer(objects[0], 3);
|
||||
ensure_ok_status(st);
|
||||
tmp = uacpi_object_create_cstring("XXXX");
|
||||
objects[1] = uacpi_object_create_reference(tmp);
|
||||
uacpi_object_unref(tmp);
|
||||
check_ok(objects, &arr);
|
||||
|
||||
st = uacpi_object_assign_integer(objects[0], 4);
|
||||
ensure_ok_status(st);
|
||||
view.const_bytes = buffer;
|
||||
view.length = sizeof(buffer);
|
||||
objects[1] = uacpi_object_create_buffer(view);
|
||||
check_ok(objects, &arr);
|
||||
|
||||
st = uacpi_object_assign_integer(objects[0], 5);
|
||||
ensure_ok_status(st);
|
||||
|
||||
pkg[0] = uacpi_object_create_uninitialized();
|
||||
view.const_text = "First Element";
|
||||
view.length = strlen(view.const_text);
|
||||
uacpi_object_assign_string(pkg[0], view);
|
||||
|
||||
pkg[1] = uacpi_object_create_cstring("test");
|
||||
st = uacpi_object_assign_integer(pkg[1], 2);
|
||||
ensure_ok_status(st);
|
||||
|
||||
buffer[0] = 1;
|
||||
buffer[1] = 2;
|
||||
buffer[2] = 3;
|
||||
view.const_bytes = buffer;
|
||||
view.length = 3;
|
||||
pkg[2] = uacpi_object_create_buffer(view);
|
||||
st = uacpi_object_assign_buffer(pkg[2], view);
|
||||
|
||||
arr1.objects = pkg;
|
||||
arr1.count = 3;
|
||||
objects[1] = uacpi_object_create_package(arr1);
|
||||
uacpi_object_assign_package(objects[1], arr1);
|
||||
check_ok(objects, &arr);
|
||||
uacpi_object_unref(pkg[0]);
|
||||
uacpi_object_unref(pkg[1]);
|
||||
uacpi_object_unref(pkg[2]);
|
||||
|
||||
uacpi_object_unref(objects[0]);
|
||||
}
|
||||
|
||||
#define CHECK_VALUE(x, y) \
|
||||
if ((x) != (y)) \
|
||||
error("check at %d failed", __LINE__);
|
||||
#define CHECK_STRING(x, y) \
|
||||
if (strcmp((x), (y))) \
|
||||
error("check at %d failed", __LINE__);
|
||||
|
||||
static void eval_one(uacpi_object *arg, uacpi_address_space type)
|
||||
{
|
||||
uacpi_object_array arr = { 0 };
|
||||
uacpi_u64 out_value;
|
||||
uacpi_status st;
|
||||
|
||||
arr.objects = &arg;
|
||||
arr.count = 1;
|
||||
|
||||
st = uacpi_object_assign_integer(arg, type);
|
||||
ensure_ok_status(st);
|
||||
st = uacpi_eval_integer(NULL, "CHEK", &arr, &out_value);
|
||||
ensure_ok_status(st);
|
||||
|
||||
if (!out_value)
|
||||
error("%s test failed", uacpi_address_space_to_string(type));
|
||||
}
|
||||
|
||||
static uacpi_status ipmi_handler(uacpi_region_op op, uacpi_handle op_data)
|
||||
{
|
||||
uacpi_region_ipmi_rw_data *ipmi = op_data;
|
||||
uint64_t response;
|
||||
const char *command;
|
||||
|
||||
if (op == UACPI_REGION_OP_ATTACH || op == UACPI_REGION_OP_DETACH)
|
||||
return UACPI_STATUS_OK;
|
||||
CHECK_VALUE(op, UACPI_REGION_OP_IPMI_COMMAND);
|
||||
|
||||
CHECK_VALUE(ipmi->in_out_message.length, 66);
|
||||
|
||||
command = ipmi->in_out_message.const_text;
|
||||
if (!strcmp(command, "IPMICommandDEADBEE0"))
|
||||
response = 0xDEADBEE0;
|
||||
else if (!strcmp(command, "IPMICommandDEADBEEF"))
|
||||
response = 0xDEADBEEF;
|
||||
else
|
||||
error("invalid IPMI command %s", command);
|
||||
|
||||
CHECK_VALUE(ipmi->command, response);
|
||||
|
||||
memcpy(ipmi->in_out_message.data, &response, sizeof(response));
|
||||
return UACPI_STATUS_OK;
|
||||
}
|
||||
|
||||
static uacpi_status gpio_handler(uacpi_region_op op, uacpi_handle op_data)
|
||||
{
|
||||
uacpi_region_gpio_rw_data *rw_data = op_data;
|
||||
uacpi_resource *res;
|
||||
uacpi_status ret;
|
||||
uacpi_resource_gpio_connection *gpio;
|
||||
uacpi_namespace_node *gpio_node;
|
||||
uacpi_u64 bit_offset;
|
||||
uacpi_u64 *state;
|
||||
uacpi_u64 i;
|
||||
|
||||
switch (op) {
|
||||
case UACPI_REGION_OP_ATTACH: {
|
||||
uacpi_region_attach_data *att_data = op_data;
|
||||
|
||||
CHECK_VALUE(att_data->gpio_info.num_pins, 6);
|
||||
att_data->out_region_context = do_calloc(1, sizeof(uint64_t));
|
||||
return UACPI_STATUS_OK;
|
||||
}
|
||||
case UACPI_REGION_OP_DETACH: {
|
||||
uacpi_region_detach_data *det_data = op_data;
|
||||
|
||||
free(det_data->region_context);
|
||||
return UACPI_STATUS_OK;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ret = uacpi_get_resource_from_buffer(rw_data->connection, &res);
|
||||
ensure_ok_status(ret);
|
||||
|
||||
CHECK_VALUE(res->type, UACPI_RESOURCE_TYPE_GPIO_CONNECTION);
|
||||
gpio = &res->gpio_connection;
|
||||
|
||||
ret = uacpi_namespace_node_find(NULL, gpio->source.string, &gpio_node);
|
||||
ensure_ok_status(ret);
|
||||
|
||||
ret = uacpi_eval_simple_integer(gpio_node, "_UID", &bit_offset);
|
||||
ensure_ok_status(ret);
|
||||
|
||||
bit_offset *= 16;
|
||||
state = rw_data->region_context;
|
||||
|
||||
if (rw_data->num_pins == 0 || rw_data->num_pins > 3)
|
||||
error("bogus number of pins %d", rw_data->num_pins);
|
||||
|
||||
if (op == UACPI_REGION_OP_GPIO_READ)
|
||||
rw_data->value = 0;
|
||||
|
||||
for (i = 0; i < rw_data->num_pins; ++i) {
|
||||
uint64_t abs_pin = i + rw_data->pin_offset;
|
||||
bool value;
|
||||
|
||||
if (op == UACPI_REGION_OP_GPIO_READ) {
|
||||
value = (*state >> bit_offset) & (1ull << abs_pin);
|
||||
|
||||
if (value)
|
||||
rw_data->value |= (1ull << i);
|
||||
} else {
|
||||
unsigned long long mask = 1ull << abs_pin;
|
||||
|
||||
CHECK_VALUE(op, UACPI_REGION_OP_GPIO_WRITE);
|
||||
value = rw_data->value & (1ull << i);
|
||||
|
||||
if (value)
|
||||
*state |= mask;
|
||||
else
|
||||
*state &= ~mask;
|
||||
}
|
||||
}
|
||||
|
||||
uacpi_free_resource(res);
|
||||
return UACPI_STATUS_OK;
|
||||
}
|
||||
|
||||
static uacpi_status pcc_handler(uacpi_region_op op, uacpi_handle op_data)
|
||||
{
|
||||
uacpi_region_pcc_send_data *rw_data = op_data;
|
||||
uint32_t x;
|
||||
|
||||
if (op == UACPI_REGION_OP_ATTACH) {
|
||||
uacpi_region_attach_data *att_data = op_data;
|
||||
|
||||
CHECK_VALUE(att_data->pcc_info.buffer.length, 0xFF);
|
||||
CHECK_VALUE(att_data->pcc_info.subspace_id, 0xCA)
|
||||
|
||||
att_data->out_region_context = att_data->pcc_info.buffer.data;
|
||||
return UACPI_STATUS_OK;
|
||||
}
|
||||
|
||||
if (op == UACPI_REGION_OP_DETACH)
|
||||
return UACPI_STATUS_OK;
|
||||
CHECK_VALUE(op, UACPI_REGION_OP_PCC_SEND);
|
||||
|
||||
CHECK_VALUE(rw_data->buffer.data, rw_data->region_context);
|
||||
CHECK_STRING(rw_data->buffer.const_text, "HELLO");
|
||||
|
||||
memcpy(&x, rw_data->buffer.bytes + 12, sizeof(x));
|
||||
CHECK_VALUE(x, 0xDEADBEEF);
|
||||
|
||||
x = 0xBEEFDEAD;
|
||||
memcpy(rw_data->buffer.bytes + 12, &x, sizeof(x));
|
||||
|
||||
return UACPI_STATUS_OK;
|
||||
}
|
||||
|
||||
static uacpi_status prm_handler(uacpi_region_op op, uacpi_handle op_data)
|
||||
{
|
||||
static const char response[] = "goodbyeworld";
|
||||
uacpi_region_prm_rw_data *rw_data = op_data;
|
||||
|
||||
if (op == UACPI_REGION_OP_ATTACH || op == UACPI_REGION_OP_DETACH)
|
||||
return UACPI_STATUS_OK;
|
||||
CHECK_VALUE(op, UACPI_REGION_OP_PRM_COMMAND);
|
||||
|
||||
CHECK_VALUE(rw_data->in_out_message.length, 26);
|
||||
CHECK_STRING(rw_data->in_out_message.const_text, "helloworld");
|
||||
|
||||
memcpy(rw_data->in_out_message.text, response, sizeof(response));
|
||||
|
||||
return UACPI_STATUS_OK;
|
||||
}
|
||||
|
||||
static uacpi_status ffixedhw_handler(uacpi_region_op op, uacpi_handle op_data)
|
||||
{
|
||||
static const char response[] = "ok";
|
||||
uacpi_region_ffixedhw_rw_data *rw_data = op_data;
|
||||
|
||||
if (op == UACPI_REGION_OP_ATTACH) {
|
||||
uacpi_region_attach_data *att_data = op_data;
|
||||
|
||||
CHECK_VALUE(att_data->generic_info.base, 0xCAFEBABE);
|
||||
CHECK_VALUE(att_data->generic_info.length, 0xFEFECACA)
|
||||
return UACPI_STATUS_OK;
|
||||
}
|
||||
|
||||
if (op == UACPI_REGION_OP_DETACH)
|
||||
return UACPI_STATUS_OK;
|
||||
CHECK_VALUE(op, UACPI_REGION_OP_FFIXEDHW_COMMAND);
|
||||
|
||||
CHECK_VALUE(rw_data->in_out_message.length, 256);
|
||||
CHECK_STRING(rw_data->in_out_message.const_text, "someguidandstuff");
|
||||
|
||||
memcpy(rw_data->in_out_message.text, "ok", sizeof(response));
|
||||
|
||||
return UACPI_STATUS_OK;
|
||||
}
|
||||
|
||||
static uacpi_status generic_serial_bus_handler(
|
||||
uacpi_region_op op, uacpi_handle op_data
|
||||
)
|
||||
{
|
||||
uacpi_region_serial_rw_data *rw_data = op_data;
|
||||
uacpi_resource *res;
|
||||
uacpi_status ret;
|
||||
uacpi_resource_i2c_connection *gpio;
|
||||
uacpi_namespace_node *i2c_node;
|
||||
uacpi_u64 i2c_offset;
|
||||
uacpi_u16 response;
|
||||
|
||||
if (op == UACPI_REGION_OP_ATTACH || op == UACPI_REGION_OP_DETACH)
|
||||
return UACPI_STATUS_OK;
|
||||
CHECK_VALUE(
|
||||
true, (op == UACPI_REGION_OP_SERIAL_READ ||
|
||||
op == UACPI_REGION_OP_SERIAL_WRITE)
|
||||
);
|
||||
|
||||
ret = uacpi_get_resource_from_buffer(rw_data->connection, &res);
|
||||
ensure_ok_status(ret);
|
||||
|
||||
CHECK_VALUE(res->type, UACPI_RESOURCE_TYPE_SERIAL_I2C_CONNECTION);
|
||||
gpio = &res->i2c_connection;
|
||||
|
||||
ret = uacpi_namespace_node_find(
|
||||
NULL, gpio->common.source.string, &i2c_node
|
||||
);
|
||||
ensure_ok_status(ret);
|
||||
|
||||
ret = uacpi_eval_simple_integer(i2c_node, "_UID", &i2c_offset);
|
||||
ensure_ok_status(ret);
|
||||
|
||||
switch ((int)rw_data->command) {
|
||||
case 0x111:
|
||||
CHECK_VALUE(op, UACPI_REGION_OP_SERIAL_WRITE);
|
||||
CHECK_VALUE(i2c_offset, 0);
|
||||
CHECK_VALUE(rw_data->in_out_buffer.length, 2);
|
||||
CHECK_VALUE(rw_data->access_attribute, UACPI_ACCESS_ATTRIBUTE_QUICK);
|
||||
break;
|
||||
case 0x121:
|
||||
CHECK_VALUE(op, UACPI_REGION_OP_SERIAL_WRITE);
|
||||
CHECK_VALUE(i2c_offset, 0);
|
||||
CHECK_VALUE(rw_data->in_out_buffer.length, 3);
|
||||
CHECK_VALUE(
|
||||
rw_data->access_attribute, UACPI_ACCESS_ATTRIBUTE_SEND_RECEIVE
|
||||
);
|
||||
break;
|
||||
case 0x122:
|
||||
CHECK_VALUE(op, UACPI_REGION_OP_SERIAL_WRITE);
|
||||
CHECK_VALUE(i2c_offset, 0);
|
||||
CHECK_VALUE(rw_data->in_out_buffer.length, 3);
|
||||
CHECK_VALUE(rw_data->access_attribute, UACPI_ACCESS_ATTRIBUTE_BYTE);
|
||||
break;
|
||||
case 0x124:
|
||||
CHECK_VALUE(op, UACPI_REGION_OP_SERIAL_READ);
|
||||
CHECK_VALUE(i2c_offset, 0);
|
||||
CHECK_VALUE(rw_data->in_out_buffer.length, 4);
|
||||
CHECK_VALUE(rw_data->access_attribute, UACPI_ACCESS_ATTRIBUTE_WORD);
|
||||
break;
|
||||
case 0x128:
|
||||
CHECK_VALUE(op, UACPI_REGION_OP_SERIAL_READ);
|
||||
CHECK_VALUE(i2c_offset, 0);
|
||||
CHECK_VALUE(rw_data->in_out_buffer.length, 257);
|
||||
CHECK_VALUE(rw_data->access_attribute, UACPI_ACCESS_ATTRIBUTE_BLOCK);
|
||||
break;
|
||||
case 0x228:
|
||||
CHECK_VALUE(op, UACPI_REGION_OP_SERIAL_WRITE);
|
||||
CHECK_VALUE(i2c_offset, 0);
|
||||
CHECK_VALUE(rw_data->in_out_buffer.length, 4);
|
||||
CHECK_VALUE(
|
||||
rw_data->access_attribute, UACPI_ACCESS_ATTRIBUTE_PROCESS_CALL
|
||||
);
|
||||
break;
|
||||
case 0x229:
|
||||
CHECK_VALUE(op, UACPI_REGION_OP_SERIAL_READ);
|
||||
CHECK_VALUE(i2c_offset, 0);
|
||||
CHECK_VALUE(rw_data->in_out_buffer.length, 257);
|
||||
CHECK_VALUE(
|
||||
rw_data->access_attribute, UACPI_ACCESS_ATTRIBUTE_BLOCK_PROCESS_CALL
|
||||
);
|
||||
break;
|
||||
case 0x23B:
|
||||
CHECK_VALUE(op, UACPI_REGION_OP_SERIAL_WRITE);
|
||||
CHECK_VALUE(i2c_offset, 1);
|
||||
CHECK_VALUE(rw_data->in_out_buffer.length, 17);
|
||||
CHECK_VALUE(rw_data->access_attribute, UACPI_ACCESS_ATTRIBUTE_BYTES);
|
||||
CHECK_VALUE(rw_data->access_length, 15);
|
||||
break;
|
||||
case 0x23C:
|
||||
CHECK_VALUE(op, UACPI_REGION_OP_SERIAL_READ);
|
||||
CHECK_VALUE(i2c_offset, 1);
|
||||
CHECK_VALUE(rw_data->in_out_buffer.length, 257);
|
||||
CHECK_VALUE(
|
||||
rw_data->access_attribute, UACPI_ACCESS_ATTRIBUTE_RAW_BYTES
|
||||
);
|
||||
CHECK_VALUE(rw_data->access_length, 255);
|
||||
break;
|
||||
case 0x23D:
|
||||
CHECK_VALUE(op, UACPI_REGION_OP_SERIAL_READ);
|
||||
CHECK_VALUE(i2c_offset, 1);
|
||||
CHECK_VALUE(rw_data->in_out_buffer.length, 257);
|
||||
CHECK_VALUE(
|
||||
rw_data->access_attribute, UACPI_ACCESS_ATTRIBUTE_RAW_PROCESS_BYTES
|
||||
);
|
||||
CHECK_VALUE(rw_data->access_length, 123);
|
||||
break;
|
||||
default:
|
||||
error("bad serial command %" PRIu64, rw_data->command);
|
||||
}
|
||||
|
||||
if (op == UACPI_REGION_OP_SERIAL_WRITE) {
|
||||
uacpi_u16 value;
|
||||
|
||||
memcpy(&value, rw_data->in_out_buffer.const_bytes, sizeof(value));
|
||||
CHECK_VALUE(value, rw_data->command);
|
||||
}
|
||||
|
||||
response = rw_data->command + 1;
|
||||
memcpy(rw_data->in_out_buffer.bytes, &response, sizeof(response));
|
||||
|
||||
uacpi_free_resource(res);
|
||||
return UACPI_STATUS_OK;
|
||||
}
|
||||
|
||||
void test_address_spaces(void)
|
||||
{
|
||||
uacpi_status st;
|
||||
uacpi_object *arg;
|
||||
|
||||
arg = uacpi_object_create_integer(0);
|
||||
|
||||
st = uacpi_install_address_space_handler(
|
||||
uacpi_namespace_root(), UACPI_ADDRESS_SPACE_IPMI, ipmi_handler, NULL
|
||||
);
|
||||
ensure_ok_status(st);
|
||||
eval_one(arg, UACPI_ADDRESS_SPACE_IPMI);
|
||||
|
||||
st = uacpi_install_address_space_handler(
|
||||
uacpi_namespace_root(), UACPI_ADDRESS_SPACE_GENERAL_PURPOSE_IO,
|
||||
gpio_handler, NULL
|
||||
);
|
||||
ensure_ok_status(st);
|
||||
eval_one(arg, UACPI_ADDRESS_SPACE_GENERAL_PURPOSE_IO);
|
||||
|
||||
st = uacpi_install_address_space_handler(
|
||||
uacpi_namespace_root(), UACPI_ADDRESS_SPACE_PCC, pcc_handler, NULL
|
||||
);
|
||||
ensure_ok_status(st);
|
||||
eval_one(arg, UACPI_ADDRESS_SPACE_PCC);
|
||||
|
||||
st = uacpi_install_address_space_handler(
|
||||
uacpi_namespace_root(), UACPI_ADDRESS_SPACE_PRM, prm_handler, NULL
|
||||
);
|
||||
ensure_ok_status(st);
|
||||
eval_one(arg, UACPI_ADDRESS_SPACE_PRM);
|
||||
|
||||
st = uacpi_install_address_space_handler(
|
||||
uacpi_namespace_root(), UACPI_ADDRESS_SPACE_FFIXEDHW, ffixedhw_handler,
|
||||
NULL
|
||||
);
|
||||
ensure_ok_status(st);
|
||||
eval_one(arg, UACPI_ADDRESS_SPACE_FFIXEDHW);
|
||||
|
||||
st = uacpi_install_address_space_handler(
|
||||
uacpi_namespace_root(), UACPI_ADDRESS_SPACE_GENERIC_SERIAL_BUS,
|
||||
generic_serial_bus_handler, NULL
|
||||
);
|
||||
ensure_ok_status(st);
|
||||
eval_one(arg, UACPI_ADDRESS_SPACE_GENERIC_SERIAL_BUS);
|
||||
|
||||
uacpi_object_unref(arg);
|
||||
}
|
219
kernel/hal/x86_64/uACPI/tests/runner/argparser.h
Normal file
219
kernel/hal/x86_64/uACPI/tests/runner/argparser.h
Normal file
@ -0,0 +1,219 @@
|
||||
#pragma once
|
||||
|
||||
#include "helpers.h"
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef enum {
|
||||
ARG_FLAG,
|
||||
ARG_PARAM,
|
||||
ARG_LIST,
|
||||
ARG_HELP,
|
||||
ARG_POSITIONAL,
|
||||
} arg_type_t;
|
||||
|
||||
typedef struct {
|
||||
const char *as_full;
|
||||
char as_short;
|
||||
arg_type_t type;
|
||||
const char *description;
|
||||
bool is_optional;
|
||||
bool parsed;
|
||||
vector_t values;
|
||||
} arg_spec_t;
|
||||
|
||||
#define ARG_POS(name, desc) \
|
||||
{ \
|
||||
.as_full = name, \
|
||||
.type = ARG_POSITIONAL, \
|
||||
.description = desc, \
|
||||
}
|
||||
#define ARG_LIST(name, short, desc) \
|
||||
{ \
|
||||
.as_full = name, \
|
||||
.as_short = short, \
|
||||
.type = ARG_LIST, \
|
||||
.description = desc, \
|
||||
.is_optional = true, \
|
||||
}
|
||||
#define ARG_FLAG(name, short, desc) \
|
||||
{ \
|
||||
.as_full = name, \
|
||||
.as_short = short, \
|
||||
.type = ARG_FLAG, \
|
||||
.description = desc, \
|
||||
.is_optional = true, \
|
||||
}
|
||||
#define ARG_PARAM(name, short, desc) \
|
||||
{ \
|
||||
.as_full = name, \
|
||||
.as_short = short, \
|
||||
.type = ARG_PARAM, \
|
||||
.description = desc, \
|
||||
.is_optional = true, \
|
||||
}
|
||||
#define ARG_HELP(name, short, desc) \
|
||||
{ \
|
||||
.as_full = name, \
|
||||
.as_short = short, \
|
||||
.type = ARG_HELP, \
|
||||
.description = desc, \
|
||||
.is_optional = true, \
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
arg_spec_t *const *positional_args;
|
||||
size_t num_positional_args;
|
||||
arg_spec_t *const *option_args;
|
||||
size_t num_option_args;
|
||||
} arg_parser_t;
|
||||
|
||||
static inline void print_help(const arg_parser_t *parser)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
printf("uACPI test runner:\n");
|
||||
|
||||
for (i = 0; i < parser->num_positional_args; i++)
|
||||
printf(
|
||||
" [%s] %s\n", parser->positional_args[i]->as_full,
|
||||
parser->positional_args[i]->description
|
||||
);
|
||||
|
||||
for (i = 0; i < parser->num_option_args; i++)
|
||||
printf(
|
||||
"%s [--%s/-%c] %s\n",
|
||||
parser->option_args[i]->is_optional ? "(optional)" : " ",
|
||||
parser->option_args[i]->as_full,
|
||||
parser->option_args[i]->as_short,
|
||||
parser->option_args[i]->description
|
||||
);
|
||||
}
|
||||
|
||||
static inline bool is_arg(const char *arg)
|
||||
{
|
||||
size_t length = strlen(arg);
|
||||
|
||||
switch (length) {
|
||||
case 0:
|
||||
case 1:
|
||||
return false;
|
||||
case 2:
|
||||
return arg[0] == '-';
|
||||
default:
|
||||
return arg[0] == '-' && arg[1] == '-';
|
||||
}
|
||||
}
|
||||
|
||||
static inline void parse_args(
|
||||
const arg_parser_t *parser, int argc, char *argv[]
|
||||
)
|
||||
{
|
||||
size_t num_args = argc;
|
||||
arg_spec_t *active_spec = NULL;
|
||||
size_t arg_index;
|
||||
|
||||
if (num_args < 2) {
|
||||
print_help(parser);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (parser->num_positional_args) {
|
||||
if ((num_args - 1) < parser->num_positional_args)
|
||||
error(
|
||||
"expected at least %zu positional arguments",
|
||||
parser->num_positional_args
|
||||
);
|
||||
|
||||
for (arg_index = 0; arg_index < parser->num_positional_args;
|
||||
++arg_index)
|
||||
vector_add(
|
||||
&parser->positional_args[arg_index]->values,
|
||||
argv[1 + arg_index], 0
|
||||
);
|
||||
}
|
||||
|
||||
for (arg_index = 1 + parser->num_positional_args; arg_index < num_args;
|
||||
++arg_index) {
|
||||
char *current_arg = argv[arg_index];
|
||||
bool is_new_arg = is_arg(current_arg);
|
||||
arg_spec_t *new_spec = NULL;
|
||||
size_t length;
|
||||
|
||||
if (active_spec) {
|
||||
if (!is_new_arg) {
|
||||
if (active_spec->type == ARG_FLAG)
|
||||
error("unexpected argument %s", current_arg);
|
||||
|
||||
if (active_spec->type == ARG_PARAM &&
|
||||
active_spec->values.count == 1)
|
||||
error("too many arguments for %s", active_spec->as_full);
|
||||
|
||||
vector_add(&active_spec->values, current_arg, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((active_spec->type == ARG_PARAM ||
|
||||
active_spec->type == ARG_LIST) &&
|
||||
active_spec->values.count == 0)
|
||||
error("expected an argument for %s", active_spec->as_full);
|
||||
}
|
||||
|
||||
length = strlen(current_arg);
|
||||
|
||||
if (length >= 2) {
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < parser->num_option_args; i++) {
|
||||
arg_spec_t *spec = parser->option_args[i];
|
||||
|
||||
if (length == 2 && spec->as_short == current_arg[1]) {
|
||||
new_spec = spec;
|
||||
break;
|
||||
} else if (strcmp(spec->as_full, ¤t_arg[2]) == 0) {
|
||||
new_spec = spec;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (new_spec == NULL)
|
||||
error("unexpected argument %s", current_arg);
|
||||
|
||||
active_spec = new_spec;
|
||||
if (active_spec->type == ARG_HELP) {
|
||||
print_help(parser);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
active_spec->parsed = true;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool is_set(const arg_spec_t *spec)
|
||||
{
|
||||
return spec->parsed;
|
||||
}
|
||||
|
||||
static inline const char *get(const arg_spec_t *spec)
|
||||
{
|
||||
if (spec->values.count == 0)
|
||||
error("no argument provided for %s", spec->as_full);
|
||||
|
||||
return spec->values.blobs[0].data;
|
||||
}
|
||||
|
||||
static inline uint64_t get_uint(const arg_spec_t *spec)
|
||||
{
|
||||
return strtoull(get(spec), NULL, 10);
|
||||
}
|
||||
|
||||
static inline uint64_t get_uint_or(
|
||||
const arg_spec_t *spec, uint64_t default_value
|
||||
)
|
||||
{
|
||||
if (is_set(spec))
|
||||
return get_uint(spec);
|
||||
|
||||
return default_value;
|
||||
}
|
170
kernel/hal/x86_64/uACPI/tests/runner/barebones_runner.c
Normal file
170
kernel/hal/x86_64/uACPI/tests/runner/barebones_runner.c
Normal file
@ -0,0 +1,170 @@
|
||||
#include "argparser.h"
|
||||
#include "helpers.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <uacpi/acpi.h>
|
||||
#include <uacpi/kernel_api.h>
|
||||
#include <uacpi/tables.h>
|
||||
#include <uacpi/uacpi.h>
|
||||
|
||||
void uacpi_kernel_log(enum uacpi_log_level lvl, const char *text)
|
||||
{
|
||||
printf("[%s] %s", uacpi_log_level_to_string(lvl), text);
|
||||
}
|
||||
|
||||
void *uacpi_kernel_map(uacpi_phys_addr addr, uacpi_size len)
|
||||
{
|
||||
UACPI_UNUSED(len);
|
||||
|
||||
return (void*)((uintptr_t)addr);
|
||||
}
|
||||
|
||||
void uacpi_kernel_unmap(void *ptr, uacpi_size len)
|
||||
{
|
||||
UACPI_UNUSED(ptr);
|
||||
UACPI_UNUSED(len);
|
||||
}
|
||||
|
||||
uacpi_phys_addr g_rsdp;
|
||||
|
||||
uacpi_status uacpi_kernel_get_rsdp(uacpi_phys_addr *out_addr)
|
||||
{
|
||||
*out_addr = g_rsdp;
|
||||
return UACPI_STATUS_OK;
|
||||
}
|
||||
|
||||
static uint8_t test_dsdt[] = {
|
||||
0x53, 0x53, 0x44, 0x54, 0x35, 0x00, 0x00, 0x00,
|
||||
0x01, 0xa1, 0x75, 0x54, 0x45, 0x53, 0x54, 0x00,
|
||||
0x4f, 0x56, 0x45, 0x52, 0x52, 0x49, 0x44, 0x45,
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0x49, 0x4e, 0x54, 0x4c,
|
||||
0x25, 0x09, 0x20, 0x20, 0x08, 0x56, 0x41, 0x4c,
|
||||
0x5f, 0x0d, 0x54, 0x65, 0x73, 0x74, 0x52, 0x75,
|
||||
0x6e, 0x6e, 0x65, 0x72, 0x00
|
||||
};
|
||||
|
||||
static uint8_t test_mcfg[] = {
|
||||
0x4d, 0x43, 0x46, 0x47, 0x3c, 0x00, 0x00, 0x00,
|
||||
0x01, 0x39, 0x48, 0x50, 0x51, 0x4f, 0x45, 0x4d,
|
||||
0x38, 0x35, 0x34, 0x39, 0x20, 0x20, 0x20, 0x20,
|
||||
0x01, 0x00, 0x00, 0x00, 0x48, 0x50, 0x20, 0x20,
|
||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f,
|
||||
0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static void ensure_signature_is(const char *signature, uacpi_table tbl)
|
||||
{
|
||||
if (strncmp(tbl.hdr->signature, signature, 4) == 0)
|
||||
return;
|
||||
|
||||
error(
|
||||
"incorrect table signature: expected %s got %.4s\n", signature,
|
||||
tbl.hdr->signature
|
||||
);
|
||||
}
|
||||
|
||||
static void find_one_table(const char *signature)
|
||||
{
|
||||
uacpi_table tbl;
|
||||
uacpi_status st;
|
||||
|
||||
st = uacpi_table_find_by_signature(signature, &tbl);
|
||||
ensure_ok_status(st);
|
||||
|
||||
ensure_signature_is(signature, tbl);
|
||||
|
||||
printf("%4.4s OK\n", signature);
|
||||
uacpi_table_unref(&tbl);
|
||||
}
|
||||
|
||||
static void test_basic_operation(void)
|
||||
{
|
||||
find_one_table(ACPI_FADT_SIGNATURE);
|
||||
find_one_table(ACPI_DSDT_SIGNATURE);
|
||||
}
|
||||
|
||||
static void test_table_installation(void)
|
||||
{
|
||||
uacpi_status st;
|
||||
uacpi_table tbl;
|
||||
|
||||
st = uacpi_table_install(test_mcfg, &tbl);
|
||||
ensure_ok_status(st);
|
||||
ensure_signature_is(ACPI_MCFG_SIGNATURE, tbl);
|
||||
uacpi_table_unref(&tbl);
|
||||
|
||||
find_one_table(ACPI_MCFG_SIGNATURE);
|
||||
|
||||
st = uacpi_table_install_physical(
|
||||
(uacpi_phys_addr)((uintptr_t)test_mcfg), &tbl
|
||||
);
|
||||
ensure_ok_status(st);
|
||||
ensure_signature_is(ACPI_MCFG_SIGNATURE, tbl);
|
||||
uacpi_table_unref(&tbl);
|
||||
}
|
||||
|
||||
static struct {
|
||||
const char *name;
|
||||
void (*func)(void);
|
||||
} test_cases[] = {
|
||||
{ "basic-operation", test_basic_operation },
|
||||
{ "table-installation", test_table_installation },
|
||||
};
|
||||
|
||||
static arg_spec_t TEST_CASE_ARG = ARG_POS("test-case", "name of the test case");
|
||||
|
||||
static arg_spec_t HELP_ARG = ARG_HELP(
|
||||
"help", 'h', "Display this menu and exit"
|
||||
);
|
||||
|
||||
static arg_spec_t *const POSITIONAL_ARGS[] = {
|
||||
&TEST_CASE_ARG,
|
||||
};
|
||||
|
||||
static arg_spec_t *const OPTION_ARGS[] = {
|
||||
&HELP_ARG,
|
||||
};
|
||||
|
||||
static const arg_parser_t PARSER = {
|
||||
.positional_args = POSITIONAL_ARGS,
|
||||
.num_positional_args = UACPI_ARRAY_SIZE(POSITIONAL_ARGS),
|
||||
.option_args = OPTION_ARGS,
|
||||
.num_option_args = UACPI_ARRAY_SIZE(OPTION_ARGS),
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
static uint8_t early_table_buf[4096];
|
||||
struct acpi_rsdp rsdp = { 0 };
|
||||
struct full_xsdt *xsdt;
|
||||
uacpi_status st;
|
||||
const char *test_case;
|
||||
size_t i;
|
||||
|
||||
parse_args(&PARSER, argc, argv);
|
||||
|
||||
xsdt = make_xsdt_blob(&rsdp, test_dsdt, sizeof(test_dsdt));
|
||||
|
||||
g_rsdp = (uacpi_phys_addr)((uintptr_t)&rsdp);
|
||||
|
||||
st = uacpi_setup_early_table_access(
|
||||
early_table_buf, sizeof(early_table_buf)
|
||||
);
|
||||
ensure_ok_status(st);
|
||||
|
||||
test_case = get(&TEST_CASE_ARG);
|
||||
|
||||
for (i = 0; i < UACPI_ARRAY_SIZE(test_cases); i++) {
|
||||
if (strcmp(test_case, test_cases[i].name) == 0) {
|
||||
test_cases[i].func();
|
||||
uacpi_state_reset();
|
||||
delete_xsdt(xsdt, 0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
error("unknown test case '%s'", test_case);
|
||||
return 1;
|
||||
}
|
237
kernel/hal/x86_64/uACPI/tests/runner/helpers.c
Normal file
237
kernel/hal/x86_64/uACPI/tests/runner/helpers.c
Normal file
@ -0,0 +1,237 @@
|
||||
#include "helpers.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <uacpi/acpi.h>
|
||||
|
||||
static uacpi_u8 gen_checksum(void *table, uacpi_size size)
|
||||
{
|
||||
uacpi_u8 *bytes = table;
|
||||
uacpi_u8 csum = 0;
|
||||
uacpi_size i;
|
||||
|
||||
for (i = 0; i < size; ++i)
|
||||
csum += bytes[i];
|
||||
|
||||
return 256 - csum;
|
||||
}
|
||||
|
||||
void set_oem(char (*oemid)[6])
|
||||
{
|
||||
memcpy(oemid, "uOEMID", sizeof(*oemid));
|
||||
}
|
||||
|
||||
void set_oem_table_id(char (*oemid_table_id)[8])
|
||||
{
|
||||
memcpy(oemid_table_id, "uTESTTBL", sizeof(*oemid_table_id));
|
||||
}
|
||||
|
||||
static void get_table_path(blob_t *item, const char *path)
|
||||
{
|
||||
*item = read_entire_file(path, sizeof(struct acpi_sdt_hdr));
|
||||
}
|
||||
|
||||
static void get_table_blob(blob_t *item, const void *data, size_t size)
|
||||
{
|
||||
item->data = do_malloc(size);
|
||||
item->size = size;
|
||||
memcpy(item->data, data, size);
|
||||
}
|
||||
|
||||
static struct full_xsdt *do_make_xsdt(
|
||||
struct acpi_rsdp *rsdp, const blob_t *tables, size_t num_tables
|
||||
)
|
||||
{
|
||||
size_t xsdt_bytes = sizeof(struct full_xsdt);
|
||||
struct full_xsdt *xsdt;
|
||||
size_t i;
|
||||
struct acpi_fadt *fadt;
|
||||
struct acpi_facs *facs;
|
||||
struct acpi_sdt_hdr *dsdt;
|
||||
|
||||
memcpy(
|
||||
&rsdp->signature, ACPI_RSDP_SIGNATURE,
|
||||
sizeof(ACPI_RSDP_SIGNATURE) - 1
|
||||
);
|
||||
set_oem(&rsdp->oemid);
|
||||
|
||||
xsdt_bytes += (num_tables - 1) * sizeof(struct acpi_sdt_hdr*);
|
||||
|
||||
xsdt = do_calloc(xsdt_bytes, 1);
|
||||
|
||||
set_oem(&xsdt->hdr.oemid);
|
||||
set_oem_table_id(&xsdt->hdr.oem_table_id);
|
||||
|
||||
for (i = 0; i < num_tables; ++i) {
|
||||
struct acpi_sdt_hdr *hdr = tables[i].data;
|
||||
char *signature = ACPI_DSDT_SIGNATURE;
|
||||
|
||||
if (hdr->length > tables[i].size)
|
||||
error("invalid table %zu size", i);
|
||||
|
||||
if (i > 0) {
|
||||
signature = ACPI_SSDT_SIGNATURE;
|
||||
xsdt->ssdts[i - 1] = hdr;
|
||||
}
|
||||
|
||||
memcpy(hdr, signature, sizeof(uacpi_object_name));
|
||||
|
||||
hdr->checksum = 0;
|
||||
hdr->checksum = gen_checksum(hdr, hdr->length);
|
||||
}
|
||||
|
||||
fadt = do_calloc(1, sizeof(*fadt));
|
||||
set_oem(&fadt->hdr.oemid);
|
||||
set_oem_table_id(&fadt->hdr.oem_table_id);
|
||||
|
||||
fadt->hdr.length = sizeof(*fadt);
|
||||
fadt->hdr.revision = 6;
|
||||
|
||||
fadt->pm1a_cnt_blk = 0xFFEE;
|
||||
fadt->pm1_cnt_len = 2;
|
||||
|
||||
fadt->pm1a_evt_blk = 0xDEAD;
|
||||
fadt->pm1_evt_len = 4;
|
||||
|
||||
fadt->pm2_cnt_blk = 0xCCDD;
|
||||
fadt->pm2_cnt_len = 1;
|
||||
|
||||
fadt->gpe0_blk_len = 0x20;
|
||||
fadt->gpe0_blk = 0xDEAD;
|
||||
|
||||
fadt->gpe1_base = 128;
|
||||
fadt->gpe1_blk = 0xBEEF;
|
||||
fadt->gpe1_blk_len = 0x20;
|
||||
|
||||
fadt->x_dsdt = (uacpi_phys_addr)((uintptr_t)tables[0].data);
|
||||
memcpy(
|
||||
fadt->hdr.signature, ACPI_FADT_SIGNATURE,
|
||||
sizeof(ACPI_FADT_SIGNATURE) - 1
|
||||
);
|
||||
|
||||
facs = do_calloc(1, sizeof(*facs));
|
||||
facs->length = sizeof(*facs);
|
||||
memcpy(
|
||||
facs->signature, ACPI_FACS_SIGNATURE,
|
||||
sizeof(ACPI_FACS_SIGNATURE) - 1
|
||||
);
|
||||
|
||||
fadt->x_firmware_ctrl = (uintptr_t)facs;
|
||||
|
||||
fadt->hdr.checksum = gen_checksum(fadt, sizeof(*fadt));
|
||||
|
||||
xsdt->fadt = fadt;
|
||||
xsdt->hdr.length = sizeof(*xsdt) +
|
||||
sizeof(struct acpi_sdt_hdr*) * (num_tables - 1);
|
||||
|
||||
dsdt = tables[0].data;
|
||||
xsdt->hdr.revision = dsdt->revision;
|
||||
memcpy(xsdt->hdr.oemid, dsdt->oemid, sizeof(dsdt->oemid));
|
||||
xsdt->hdr.oem_revision = dsdt->oem_revision;
|
||||
|
||||
if (sizeof(void*) == 4) {
|
||||
memcpy(
|
||||
xsdt->hdr.signature, ACPI_RSDT_SIGNATURE,
|
||||
sizeof(ACPI_XSDT_SIGNATURE) - 1
|
||||
);
|
||||
|
||||
rsdp->rsdt_addr = (size_t)xsdt;
|
||||
rsdp->revision = 1;
|
||||
rsdp->checksum = gen_checksum(rsdp, offsetof(struct acpi_rsdp, length));
|
||||
} else {
|
||||
memcpy(
|
||||
xsdt->hdr.signature, ACPI_XSDT_SIGNATURE,
|
||||
sizeof(ACPI_XSDT_SIGNATURE) - 1
|
||||
);
|
||||
|
||||
rsdp->xsdt_addr = (size_t)xsdt;
|
||||
rsdp->length = sizeof(*rsdp);
|
||||
rsdp->revision = 2;
|
||||
rsdp->checksum = gen_checksum(rsdp, offsetof(struct acpi_rsdp, length));
|
||||
rsdp->extended_checksum = gen_checksum(rsdp, sizeof(*rsdp));
|
||||
}
|
||||
xsdt->hdr.checksum = gen_checksum(xsdt, xsdt->hdr.length);
|
||||
|
||||
return xsdt;
|
||||
}
|
||||
|
||||
struct full_xsdt *make_xsdt(
|
||||
struct acpi_rsdp *rsdp, const char *dsdt_path, const vector_t *ssdts
|
||||
)
|
||||
{
|
||||
vector_t tables;
|
||||
size_t i;
|
||||
struct full_xsdt *xsdt;
|
||||
|
||||
vector_init(&tables, ssdts->count + 1);
|
||||
|
||||
get_table_path(&tables.blobs[0], dsdt_path);
|
||||
|
||||
for (i = 0; i < ssdts->count; ++i)
|
||||
get_table_path(&tables.blobs[1 + i], ssdts->blobs[i].data);
|
||||
|
||||
xsdt = do_make_xsdt(rsdp, tables.blobs, tables.count);
|
||||
vector_cleanup(&tables);
|
||||
return xsdt;
|
||||
}
|
||||
|
||||
struct full_xsdt *make_xsdt_blob(
|
||||
struct acpi_rsdp *rsdp, const void *dsdt, size_t dsdt_size
|
||||
)
|
||||
{
|
||||
blob_t blob;
|
||||
|
||||
get_table_blob(&blob, dsdt, dsdt_size);
|
||||
return do_make_xsdt(rsdp, &blob, 1);
|
||||
}
|
||||
|
||||
void delete_xsdt(struct full_xsdt *xsdt, size_t num_tables)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (xsdt->fadt) {
|
||||
free((void*)((uintptr_t)xsdt->fadt->x_dsdt));
|
||||
free((struct acpi_facs*)((uintptr_t)xsdt->fadt->x_firmware_ctrl));
|
||||
free(xsdt->fadt);
|
||||
}
|
||||
|
||||
for (i = 0; i < num_tables; i++)
|
||||
free(xsdt->ssdts[i]);
|
||||
|
||||
free(xsdt);
|
||||
}
|
||||
|
||||
blob_t read_entire_file(const char *path, size_t min_size)
|
||||
{
|
||||
FILE *file = fopen(path, "rb");
|
||||
long size;
|
||||
void *buf;
|
||||
blob_t blob = { 0 };
|
||||
|
||||
if (!file)
|
||||
error("failed to open file %s", path);
|
||||
|
||||
if (fseek(file, 0, SEEK_END))
|
||||
error("failed to seek file %s", path);
|
||||
|
||||
size = ftell(file);
|
||||
if (size < 0)
|
||||
error("failed to get size of file %s", path);
|
||||
if (size < (long)min_size)
|
||||
error("file %s is too small", path);
|
||||
if (fseek(file, 0, SEEK_SET))
|
||||
error("failed to seek file %s", path);
|
||||
|
||||
buf = do_malloc(size);
|
||||
|
||||
if (fread(buf, size, 1, file) != 1)
|
||||
error("failed to read from %s", path);
|
||||
|
||||
if (fclose(file))
|
||||
error("failed to close file %s", path);
|
||||
|
||||
blob.data = buf;
|
||||
blob.size = size;
|
||||
return blob;
|
||||
}
|
294
kernel/hal/x86_64/uACPI/tests/runner/helpers.h
Normal file
294
kernel/hal/x86_64/uACPI/tests/runner/helpers.h
Normal file
@ -0,0 +1,294 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <uacpi/acpi.h>
|
||||
#include <uacpi/internal/helpers.h>
|
||||
#include <uacpi/status.h>
|
||||
#include <uacpi/uacpi.h>
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define NORETURN __attribute__((__noreturn__))
|
||||
#else
|
||||
#define NORETURN
|
||||
#endif
|
||||
|
||||
NORETURN static inline void error(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
fprintf(stderr, "unexpected error: ");
|
||||
va_start(args, format);
|
||||
vfprintf(stderr, format, args);
|
||||
va_end(args);
|
||||
fputc('\n', stderr);
|
||||
uacpi_state_reset();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static inline void *do_malloc(size_t size)
|
||||
{
|
||||
void *ptr = malloc(size);
|
||||
|
||||
if (!ptr)
|
||||
error("failed to allocate %zu bytes of memory", size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static inline void *do_calloc(size_t nmemb, size_t size)
|
||||
{
|
||||
void *ptr = calloc(nmemb, size);
|
||||
|
||||
if (!ptr)
|
||||
error("failed to allocate %zu bytes of memory", nmemb * size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static inline void *do_realloc(void *ptr, size_t size)
|
||||
{
|
||||
ptr = realloc(ptr, size);
|
||||
|
||||
if (!ptr)
|
||||
error("failed to allocate %zu bytes of memory", size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
void *data;
|
||||
size_t size;
|
||||
} blob_t;
|
||||
|
||||
typedef struct {
|
||||
blob_t *blobs;
|
||||
size_t capacity;
|
||||
size_t count;
|
||||
} vector_t;
|
||||
|
||||
static inline void vector_init(vector_t *vector, size_t items)
|
||||
{
|
||||
vector->blobs = do_calloc(items, sizeof(*vector->blobs));
|
||||
vector->capacity = items;
|
||||
vector->count = items;
|
||||
}
|
||||
|
||||
static inline void vector_add(vector_t *vector, void *data, size_t size)
|
||||
{
|
||||
if (vector->count >= vector->capacity) {
|
||||
vector->capacity = vector->capacity ? vector->capacity * 2 : 8;
|
||||
vector->blobs = do_realloc(
|
||||
vector->blobs, vector->capacity * sizeof(*vector->blobs)
|
||||
);
|
||||
}
|
||||
|
||||
vector->blobs[vector->count].data = data;
|
||||
vector->blobs[vector->count].size = size;
|
||||
vector->count += 1;
|
||||
}
|
||||
|
||||
static inline void vector_cleanup(vector_t *vector)
|
||||
{
|
||||
free(vector->blobs);
|
||||
vector->capacity = 0;
|
||||
vector->count = 0;
|
||||
}
|
||||
|
||||
static inline void *get_container(void *value, size_t offset)
|
||||
{
|
||||
return value ? (void*)((char*)value - offset) : NULL;
|
||||
}
|
||||
|
||||
#define CONTAINER(type, field, value) \
|
||||
((type*)get_container((value), offsetof(type, field)))
|
||||
|
||||
typedef struct hash_node {
|
||||
struct hash_node *prev;
|
||||
struct hash_node *next;
|
||||
uint64_t key;
|
||||
} hash_node_t;
|
||||
|
||||
typedef struct {
|
||||
hash_node_t **entries;
|
||||
size_t capacity;
|
||||
size_t count;
|
||||
} hash_table_t;
|
||||
|
||||
static inline uint64_t make_hash(uint64_t x)
|
||||
{
|
||||
x *= 0xe9770214b82cf957;
|
||||
x ^= x >> 47;
|
||||
x *= 0x2bdd9d20d060fc9b;
|
||||
x ^= x >> 44;
|
||||
x *= 0x65c487023b406173;
|
||||
return x;
|
||||
}
|
||||
|
||||
static inline hash_node_t *hash_table_find(hash_table_t *table, uint64_t key)
|
||||
{
|
||||
hash_node_t *current;
|
||||
|
||||
if (!table->capacity)
|
||||
return NULL;
|
||||
|
||||
current = table->entries[make_hash(key) % table->capacity];
|
||||
|
||||
while (current != NULL && current->key != key)
|
||||
current = current->next;
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
#define HASH_TABLE_FIND(table, key, type, field) \
|
||||
CONTAINER(type, field, hash_table_find((table), (key)))
|
||||
|
||||
static inline hash_node_t *hash_table_get_or_add(
|
||||
hash_table_t *table, uint64_t key, size_t size, size_t offset
|
||||
)
|
||||
{
|
||||
uint64_t hash = make_hash(key);
|
||||
void *value;
|
||||
hash_node_t *node;
|
||||
size_t bucket;
|
||||
|
||||
if (table->capacity) {
|
||||
hash_node_t *current = table->entries[hash % table->capacity];
|
||||
|
||||
while (current != NULL) {
|
||||
if (current->key == key)
|
||||
return current;
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (table->count >= table->capacity - (table->capacity / 4)) {
|
||||
size_t new_cap = table->capacity ? table->capacity * 2 : 8;
|
||||
hash_node_t **new_entries = do_calloc(new_cap, sizeof(*table->entries));
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < table->capacity; i++) {
|
||||
hash_node_t *current = table->entries[i];
|
||||
|
||||
while (current != NULL) {
|
||||
hash_node_t *next = current->next;
|
||||
size_t bucket = make_hash(current->key) % new_cap;
|
||||
|
||||
current->prev = NULL;
|
||||
current->next = new_entries[bucket];
|
||||
if (current->next)
|
||||
current->next->prev = current;
|
||||
new_entries[bucket] = current;
|
||||
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
|
||||
free(table->entries);
|
||||
table->entries = new_entries;
|
||||
table->capacity = new_cap;
|
||||
}
|
||||
|
||||
value = do_calloc(1, size);
|
||||
node = (void*)((char*)value + offset);
|
||||
node->key = key;
|
||||
|
||||
bucket = hash % table->capacity;
|
||||
node->prev = NULL;
|
||||
node->next = table->entries[bucket];
|
||||
if (node->next)
|
||||
node->next->prev = node;
|
||||
table->entries[bucket] = node;
|
||||
|
||||
table->count += 1;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
#define HASH_TABLE_GET_OR_ADD(table, key, type, field) \
|
||||
CONTAINER( \
|
||||
type, field, \
|
||||
hash_table_get_or_add( \
|
||||
(table), (key), sizeof(type), offsetof(type, field) \
|
||||
) \
|
||||
)
|
||||
|
||||
static inline void hash_table_remove(
|
||||
hash_table_t *table, hash_node_t *node, size_t offset
|
||||
)
|
||||
{
|
||||
if (node->prev)
|
||||
node->prev->next = node->next;
|
||||
else
|
||||
table->entries[make_hash(node->key) % table->capacity] = node->next;
|
||||
|
||||
if (node->next)
|
||||
node->next->prev = node->prev;
|
||||
|
||||
table->count -= 1;
|
||||
free((void*)((char*)node - offset));
|
||||
}
|
||||
|
||||
#define HASH_TABLE_REMOVE(table, value, type, field) \
|
||||
hash_table_remove((table), &(value)->field, offsetof(type, field))
|
||||
|
||||
static inline bool hash_table_empty(hash_table_t *table)
|
||||
{
|
||||
return table->count == 0;
|
||||
}
|
||||
|
||||
static inline void hash_table_cleanup(hash_table_t *table)
|
||||
{
|
||||
free(table->entries);
|
||||
table->capacity = 0;
|
||||
table->count = 0;
|
||||
}
|
||||
|
||||
extern bool g_expect_virtual_addresses;
|
||||
extern uacpi_phys_addr g_rsdp;
|
||||
|
||||
UACPI_PACKED(struct full_xsdt {
|
||||
struct acpi_sdt_hdr hdr;
|
||||
struct acpi_fadt *fadt;
|
||||
struct acpi_sdt_hdr *ssdts[];
|
||||
})
|
||||
|
||||
void set_oem(char (*oemid)[6]);
|
||||
void set_oem_table_id(char (*oemid_table_id)[8]);
|
||||
|
||||
struct full_xsdt *make_xsdt(
|
||||
struct acpi_rsdp *rsdp, const char *dsdt_path, const vector_t *ssdt_paths
|
||||
);
|
||||
struct full_xsdt *make_xsdt_blob(
|
||||
struct acpi_rsdp *rsdp, const void *dsdt, size_t dsdt_size
|
||||
);
|
||||
void delete_xsdt(struct full_xsdt *xsdt, size_t num_tables);
|
||||
|
||||
blob_t read_entire_file(const char *path, size_t min_size);
|
||||
|
||||
static inline void ensure_ok_status(uacpi_status st)
|
||||
{
|
||||
if (st == UACPI_STATUS_OK)
|
||||
return;
|
||||
|
||||
error("uACPI error: %s", uacpi_status_to_string(st));
|
||||
}
|
||||
|
||||
static inline const char *uacpi_log_level_to_string(uacpi_log_level lvl)
|
||||
{
|
||||
switch (lvl) {
|
||||
case UACPI_LOG_DEBUG:
|
||||
return "DEBUG";
|
||||
case UACPI_LOG_TRACE:
|
||||
return "TRACE";
|
||||
case UACPI_LOG_INFO:
|
||||
return "INFO";
|
||||
case UACPI_LOG_WARN:
|
||||
return "WARN";
|
||||
case UACPI_LOG_ERROR:
|
||||
return "ERROR";
|
||||
default:
|
||||
abort();
|
||||
return NULL;
|
||||
}
|
||||
}
|
605
kernel/hal/x86_64/uACPI/tests/runner/interface_impl.c
Normal file
605
kernel/hal/x86_64/uACPI/tests/runner/interface_impl.c
Normal file
@ -0,0 +1,605 @@
|
||||
#include "helpers.h"
|
||||
#include "os.h"
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <uacpi/kernel_api.h>
|
||||
#include <uacpi/status.h>
|
||||
#include <uacpi/types.h>
|
||||
|
||||
uacpi_phys_addr g_rsdp;
|
||||
|
||||
uacpi_status uacpi_kernel_get_rsdp(uacpi_phys_addr *out_rsdp_address)
|
||||
{
|
||||
*out_rsdp_address = g_rsdp;
|
||||
return UACPI_STATUS_OK;
|
||||
}
|
||||
|
||||
static uint8_t *io_space;
|
||||
|
||||
#ifdef UACPI_KERNEL_INITIALIZATION
|
||||
uacpi_status uacpi_kernel_initialize(uacpi_init_level lvl)
|
||||
{
|
||||
if (lvl == UACPI_INIT_LEVEL_EARLY)
|
||||
io_space = do_malloc(UINT16_MAX + 1);
|
||||
return UACPI_STATUS_OK;
|
||||
}
|
||||
|
||||
void uacpi_kernel_deinitialize(void)
|
||||
{
|
||||
free(io_space);
|
||||
io_space = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
uacpi_status uacpi_kernel_io_map(
|
||||
uacpi_io_addr base, uacpi_size len, uacpi_handle *out_handle
|
||||
)
|
||||
{
|
||||
UACPI_UNUSED(len);
|
||||
|
||||
*out_handle = (uacpi_handle)((uintptr_t)base);
|
||||
return UACPI_STATUS_OK;
|
||||
}
|
||||
|
||||
void uacpi_kernel_io_unmap(uacpi_handle handle)
|
||||
{
|
||||
UACPI_UNUSED(handle);
|
||||
}
|
||||
|
||||
#define UACPI_IO_READ(bits) \
|
||||
uacpi_status uacpi_kernel_io_read##bits( \
|
||||
uacpi_handle handle, uacpi_size offset, uacpi_u##bits *out_value \
|
||||
) \
|
||||
{ \
|
||||
uacpi_io_addr addr = (uacpi_io_addr)((uintptr_t)handle) + offset; \
|
||||
\
|
||||
if (io_space && addr <= UINT16_MAX) \
|
||||
memcpy(out_value, &io_space[addr], bits / 8); \
|
||||
else \
|
||||
*out_value = (uacpi_u##bits)0xFFFFFFFFFFFFFFFF; \
|
||||
\
|
||||
return UACPI_STATUS_OK; \
|
||||
}
|
||||
|
||||
#define UACPI_IO_WRITE(bits) \
|
||||
uacpi_status uacpi_kernel_io_write##bits( \
|
||||
uacpi_handle handle, uacpi_size offset, uacpi_u##bits in_value \
|
||||
) \
|
||||
{ \
|
||||
uacpi_io_addr addr = (uacpi_io_addr)((uintptr_t)handle) + offset; \
|
||||
\
|
||||
if (io_space && addr <= UINT16_MAX) \
|
||||
memcpy(&io_space[addr], &in_value, bits / 8); \
|
||||
\
|
||||
return UACPI_STATUS_OK; \
|
||||
}
|
||||
|
||||
#define UACPI_PCI_READ(bits) \
|
||||
uacpi_status uacpi_kernel_pci_read##bits( \
|
||||
uacpi_handle handle, uacpi_size offset, uacpi_u##bits *value \
|
||||
) \
|
||||
{ \
|
||||
UACPI_UNUSED(handle); \
|
||||
UACPI_UNUSED(offset); \
|
||||
\
|
||||
*value = (uacpi_u##bits)0xFFFFFFFFFFFFFFFF; \
|
||||
return UACPI_STATUS_OK; \
|
||||
}
|
||||
|
||||
#define UACPI_PCI_WRITE(bits) \
|
||||
uacpi_status uacpi_kernel_pci_write##bits( \
|
||||
uacpi_handle handle, uacpi_size offset, uacpi_u##bits value \
|
||||
) \
|
||||
{ \
|
||||
UACPI_UNUSED(handle); \
|
||||
UACPI_UNUSED(offset); \
|
||||
UACPI_UNUSED(value); \
|
||||
\
|
||||
return UACPI_STATUS_OK; \
|
||||
}
|
||||
|
||||
UACPI_IO_READ(8)
|
||||
UACPI_IO_READ(16)
|
||||
UACPI_IO_READ(32)
|
||||
|
||||
UACPI_IO_WRITE(8)
|
||||
UACPI_IO_WRITE(16)
|
||||
UACPI_IO_WRITE(32)
|
||||
|
||||
UACPI_PCI_READ(8)
|
||||
UACPI_PCI_READ(16)
|
||||
UACPI_PCI_READ(32)
|
||||
|
||||
UACPI_PCI_WRITE(8)
|
||||
UACPI_PCI_WRITE(16)
|
||||
UACPI_PCI_WRITE(32)
|
||||
|
||||
uacpi_status uacpi_kernel_pci_device_open(
|
||||
uacpi_pci_address address, uacpi_handle *out_handle
|
||||
)
|
||||
{
|
||||
UACPI_UNUSED(address);
|
||||
|
||||
*out_handle = NULL;
|
||||
return UACPI_STATUS_OK;
|
||||
}
|
||||
|
||||
void uacpi_kernel_pci_device_close(uacpi_handle handle)
|
||||
{
|
||||
UACPI_UNUSED(handle);
|
||||
}
|
||||
|
||||
bool g_expect_virtual_addresses = true;
|
||||
|
||||
typedef struct {
|
||||
hash_node_t node;
|
||||
uint64_t phys;
|
||||
size_t references;
|
||||
} virt_location_t;
|
||||
|
||||
typedef struct {
|
||||
hash_node_t node;
|
||||
void *virt;
|
||||
} mapping_t;
|
||||
|
||||
typedef struct {
|
||||
hash_node_t node;
|
||||
hash_table_t mappings;
|
||||
} phys_location_t;
|
||||
|
||||
static hash_table_t virt_locations;
|
||||
static hash_table_t phys_locations;
|
||||
|
||||
void *uacpi_kernel_map(uacpi_phys_addr addr, uacpi_size len)
|
||||
{
|
||||
if (!g_expect_virtual_addresses) {
|
||||
phys_location_t *phys_location = HASH_TABLE_FIND(
|
||||
&phys_locations, addr, phys_location_t, node
|
||||
);
|
||||
void *virt;
|
||||
virt_location_t *location;
|
||||
mapping_t *mapping;
|
||||
|
||||
if (phys_location != NULL) {
|
||||
mapping = HASH_TABLE_FIND(
|
||||
&phys_location->mappings, len, mapping_t, node
|
||||
);
|
||||
|
||||
if (mapping != NULL) {
|
||||
location = HASH_TABLE_FIND(
|
||||
&virt_locations, (uintptr_t)mapping->virt, virt_location_t,
|
||||
node
|
||||
);
|
||||
|
||||
location->references += 1;
|
||||
return mapping->virt;
|
||||
}
|
||||
|
||||
printf(
|
||||
"WARN: remapping physical 0x%016" PRIX64 " with size %zu\n",
|
||||
addr, len
|
||||
);
|
||||
}
|
||||
|
||||
virt = do_calloc(len, 1);
|
||||
|
||||
location = HASH_TABLE_GET_OR_ADD(
|
||||
&virt_locations, (uintptr_t)virt, virt_location_t, node
|
||||
);
|
||||
location->phys = addr;
|
||||
location->references = 1;
|
||||
|
||||
phys_location = HASH_TABLE_GET_OR_ADD(
|
||||
&phys_locations, addr, phys_location_t, node
|
||||
);
|
||||
mapping = HASH_TABLE_GET_OR_ADD(
|
||||
&phys_location->mappings, len, mapping_t, node
|
||||
);
|
||||
mapping->virt = virt;
|
||||
|
||||
return virt;
|
||||
}
|
||||
|
||||
return (void*)((uintptr_t)addr);
|
||||
}
|
||||
|
||||
void uacpi_kernel_unmap(void *addr, uacpi_size len)
|
||||
{
|
||||
virt_location_t *virt_location = HASH_TABLE_FIND(
|
||||
&virt_locations, (uintptr_t)addr, virt_location_t, node
|
||||
);
|
||||
phys_location_t *phys_location;
|
||||
mapping_t *mapping;
|
||||
|
||||
if (!virt_location)
|
||||
return;
|
||||
if (--virt_location->references > 0)
|
||||
return;
|
||||
|
||||
phys_location = HASH_TABLE_FIND(
|
||||
&phys_locations, virt_location->phys, phys_location_t, node
|
||||
);
|
||||
mapping = HASH_TABLE_FIND(&phys_location->mappings, len, mapping_t, node);
|
||||
if (!mapping) {
|
||||
printf(
|
||||
"WARN: cannot identify mapping virt=%p phys=0x%016" PRIX64 " with "
|
||||
"size %zu\n", addr, phys_location->node.key, len
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
HASH_TABLE_REMOVE(&phys_location->mappings, mapping, mapping_t, node);
|
||||
if (hash_table_empty(&phys_location->mappings)) {
|
||||
hash_table_cleanup(&phys_location->mappings);
|
||||
HASH_TABLE_REMOVE(
|
||||
&phys_locations, phys_location, phys_location_t, node
|
||||
);
|
||||
}
|
||||
|
||||
free((void*)((uintptr_t)virt_location->node.key));
|
||||
HASH_TABLE_REMOVE(&virt_locations, virt_location, virt_location_t, node);
|
||||
}
|
||||
|
||||
void interface_cleanup(void)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < phys_locations.capacity; i++) {
|
||||
phys_location_t *location = CONTAINER(
|
||||
phys_location_t, node, phys_locations.entries[i]
|
||||
);
|
||||
|
||||
while (location) {
|
||||
hash_table_cleanup(&location->mappings);
|
||||
location = CONTAINER(phys_location_t, node, location->node.next);
|
||||
}
|
||||
}
|
||||
|
||||
hash_table_cleanup(&phys_locations);
|
||||
hash_table_cleanup(&virt_locations);
|
||||
}
|
||||
|
||||
#ifdef UACPI_SIZED_FREES
|
||||
|
||||
typedef struct {
|
||||
hash_node_t node;
|
||||
size_t size;
|
||||
} allocation_t;
|
||||
|
||||
static hash_table_t allocations;
|
||||
|
||||
void *uacpi_kernel_alloc(uacpi_size size)
|
||||
{
|
||||
void *ret;
|
||||
allocation_t *allocation;
|
||||
|
||||
if (size == 0)
|
||||
abort();
|
||||
|
||||
ret = malloc(size);
|
||||
if (ret == NULL)
|
||||
return ret;
|
||||
|
||||
allocation = HASH_TABLE_GET_OR_ADD(
|
||||
&allocations, (uintptr_t)ret, allocation_t, node
|
||||
);
|
||||
allocation->size = size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void uacpi_kernel_free(void *mem, uacpi_size size_hint)
|
||||
{
|
||||
allocation_t *allocation;
|
||||
|
||||
if (mem == NULL)
|
||||
return;
|
||||
|
||||
allocation = HASH_TABLE_FIND(
|
||||
&allocations, (uintptr_t)mem, allocation_t, node
|
||||
);
|
||||
if (!allocation)
|
||||
error("unable to find heap allocation %p\n", mem);
|
||||
|
||||
if (allocation->size != size_hint)
|
||||
error(
|
||||
"invalid free size: originally allocated %zu bytes, freeing as %zu",
|
||||
allocation->size, size_hint
|
||||
);
|
||||
|
||||
HASH_TABLE_REMOVE(&allocations, allocation, allocation_t, node);
|
||||
free(mem);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void *uacpi_kernel_alloc(uacpi_size size)
|
||||
{
|
||||
if (size == 0)
|
||||
error("attempted to allocate zero bytes");
|
||||
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void uacpi_kernel_free(void *mem)
|
||||
{
|
||||
free(mem);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef UACPI_NATIVE_ALLOC_ZEROED
|
||||
|
||||
void *uacpi_kernel_alloc_zeroed(uacpi_size size)
|
||||
{
|
||||
void *ret = uacpi_kernel_alloc(size);
|
||||
|
||||
if (ret == NULL)
|
||||
return ret;
|
||||
|
||||
memset(ret, 0, size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef UACPI_FORMATTED_LOGGING
|
||||
|
||||
void uacpi_kernel_vlog(
|
||||
uacpi_log_level level, const uacpi_char *format, va_list args
|
||||
)
|
||||
{
|
||||
printf("[uACPI][%s] ", uacpi_log_level_to_string(level));
|
||||
vprintf(format, args);
|
||||
}
|
||||
|
||||
void uacpi_kernel_log(uacpi_log_level level, const uacpi_char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
uacpi_kernel_vlog(level, format, args);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void uacpi_kernel_log(uacpi_log_level level, const uacpi_char *str)
|
||||
{
|
||||
printf("[uACPI][%s] %s", uacpi_log_level_to_string(level), str);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
uacpi_u64 uacpi_kernel_get_nanoseconds_since_boot(void)
|
||||
{
|
||||
return get_nanosecond_timer();
|
||||
}
|
||||
|
||||
void uacpi_kernel_stall(uacpi_u8 usec)
|
||||
{
|
||||
uint64_t end = get_nanosecond_timer() + (uint64_t)usec * 1000;
|
||||
|
||||
for (;;)
|
||||
if (get_nanosecond_timer() >= end)
|
||||
break;
|
||||
}
|
||||
|
||||
void uacpi_kernel_sleep(uacpi_u64 msec)
|
||||
{
|
||||
millisecond_sleep(msec);
|
||||
}
|
||||
|
||||
uacpi_handle uacpi_kernel_create_mutex(void)
|
||||
{
|
||||
mutex_t *mutex = do_malloc(sizeof(*mutex));
|
||||
|
||||
mutex_init(mutex);
|
||||
return mutex;
|
||||
}
|
||||
|
||||
void uacpi_kernel_free_mutex(uacpi_handle handle)
|
||||
{
|
||||
mutex_free(handle);
|
||||
free(handle);
|
||||
}
|
||||
|
||||
uacpi_thread_id uacpi_kernel_get_thread_id(void)
|
||||
{
|
||||
return get_thread_id();
|
||||
}
|
||||
|
||||
uacpi_status uacpi_kernel_acquire_mutex(uacpi_handle handle, uacpi_u16 timeout)
|
||||
{
|
||||
if (timeout == 0)
|
||||
return mutex_try_lock(handle) ? UACPI_STATUS_OK : UACPI_STATUS_TIMEOUT;
|
||||
|
||||
if (timeout == 0xFFFF) {
|
||||
mutex_lock(handle);
|
||||
return UACPI_STATUS_OK;
|
||||
}
|
||||
|
||||
if (mutex_lock_timeout(handle, timeout * 1000000ull))
|
||||
return UACPI_STATUS_OK;
|
||||
|
||||
return UACPI_STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
void uacpi_kernel_release_mutex(uacpi_handle handle)
|
||||
{
|
||||
mutex_unlock(handle);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
mutex_t mutex;
|
||||
condvar_t condvar;
|
||||
size_t counter;
|
||||
} event_t;
|
||||
|
||||
uacpi_handle uacpi_kernel_create_event(void)
|
||||
{
|
||||
event_t *event = do_calloc(1, sizeof(*event));
|
||||
|
||||
mutex_init(&event->mutex);
|
||||
condvar_init(&event->condvar);
|
||||
return event;
|
||||
}
|
||||
|
||||
void uacpi_kernel_free_event(uacpi_handle handle)
|
||||
{
|
||||
event_t *event = handle;
|
||||
|
||||
condvar_free(&event->condvar);
|
||||
mutex_free(&event->mutex);
|
||||
free(handle);
|
||||
}
|
||||
|
||||
static bool event_pred(void *ptr)
|
||||
{
|
||||
event_t *event = ptr;
|
||||
|
||||
return event->counter != 0;
|
||||
}
|
||||
|
||||
uacpi_bool uacpi_kernel_wait_for_event(uacpi_handle handle, uacpi_u16 timeout)
|
||||
{
|
||||
event_t *event = handle;
|
||||
bool ok;
|
||||
|
||||
mutex_lock(&event->mutex);
|
||||
|
||||
if (event->counter > 0) {
|
||||
event->counter -= 1;
|
||||
mutex_unlock(&event->mutex);
|
||||
return UACPI_TRUE;
|
||||
}
|
||||
|
||||
if (timeout == 0) {
|
||||
mutex_unlock(&event->mutex);
|
||||
return UACPI_FALSE;
|
||||
}
|
||||
|
||||
if (timeout == 0xFFFF) {
|
||||
condvar_wait(&event->condvar, &event->mutex, event_pred, event);
|
||||
|
||||
event->counter -= 1;
|
||||
mutex_unlock(&event->mutex);
|
||||
return UACPI_TRUE;
|
||||
}
|
||||
|
||||
ok = condvar_wait_timeout(
|
||||
&event->condvar, &event->mutex, event_pred, event, timeout * 1000000ull
|
||||
);
|
||||
if (ok)
|
||||
event->counter -= 1;
|
||||
|
||||
mutex_unlock(&event->mutex);
|
||||
return ok ? UACPI_TRUE : UACPI_FALSE;
|
||||
}
|
||||
|
||||
void uacpi_kernel_signal_event(uacpi_handle handle)
|
||||
{
|
||||
event_t *event = handle;
|
||||
|
||||
mutex_lock(&event->mutex);
|
||||
|
||||
event->counter += 1;
|
||||
condvar_signal(&event->condvar);
|
||||
|
||||
mutex_unlock(&event->mutex);
|
||||
}
|
||||
|
||||
void uacpi_kernel_reset_event(uacpi_handle handle)
|
||||
{
|
||||
event_t *event = handle;
|
||||
|
||||
mutex_lock(&event->mutex);
|
||||
|
||||
event->counter = 0;
|
||||
|
||||
mutex_unlock(&event->mutex);
|
||||
}
|
||||
|
||||
uacpi_status uacpi_kernel_handle_firmware_request(uacpi_firmware_request *req)
|
||||
{
|
||||
switch (req->type) {
|
||||
case UACPI_FIRMWARE_REQUEST_TYPE_BREAKPOINT:
|
||||
printf("Ignoring breakpoint\n");
|
||||
break;
|
||||
case UACPI_FIRMWARE_REQUEST_TYPE_FATAL:
|
||||
printf(
|
||||
"Fatal firmware error: type: %" PRIx8 " code: %" PRIx32 " arg: "
|
||||
"%" PRIx64 "\n", req->fatal.type, req->fatal.code, req->fatal.arg
|
||||
);
|
||||
break;
|
||||
default:
|
||||
error("unknown firmware request type %d", req->type);
|
||||
}
|
||||
|
||||
return UACPI_STATUS_OK;
|
||||
}
|
||||
|
||||
uacpi_status uacpi_kernel_install_interrupt_handler(
|
||||
uacpi_u32 irq, uacpi_interrupt_handler handler, uacpi_handle ctx,
|
||||
uacpi_handle *out_irq_handle
|
||||
)
|
||||
{
|
||||
UACPI_UNUSED(irq);
|
||||
UACPI_UNUSED(handler);
|
||||
UACPI_UNUSED(ctx);
|
||||
UACPI_UNUSED(out_irq_handle);
|
||||
|
||||
return UACPI_STATUS_OK;
|
||||
}
|
||||
|
||||
uacpi_status uacpi_kernel_uninstall_interrupt_handler(
|
||||
uacpi_interrupt_handler handler, uacpi_handle irq_handle
|
||||
)
|
||||
{
|
||||
UACPI_UNUSED(handler);
|
||||
UACPI_UNUSED(irq_handle);
|
||||
|
||||
return UACPI_STATUS_OK;
|
||||
}
|
||||
|
||||
uacpi_handle uacpi_kernel_create_spinlock(void)
|
||||
{
|
||||
return uacpi_kernel_create_mutex();
|
||||
}
|
||||
|
||||
void uacpi_kernel_free_spinlock(uacpi_handle handle)
|
||||
{
|
||||
uacpi_kernel_free_mutex(handle);
|
||||
}
|
||||
|
||||
uacpi_cpu_flags uacpi_kernel_lock_spinlock(uacpi_handle handle)
|
||||
{
|
||||
uacpi_kernel_acquire_mutex(handle, 0xFFFF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uacpi_kernel_unlock_spinlock(uacpi_handle handle, uacpi_cpu_flags flags)
|
||||
{
|
||||
UACPI_UNUSED(flags);
|
||||
|
||||
uacpi_kernel_release_mutex(handle);
|
||||
}
|
||||
|
||||
uacpi_status uacpi_kernel_schedule_work(
|
||||
uacpi_work_type type, uacpi_work_handler handler, uacpi_handle ctx
|
||||
)
|
||||
{
|
||||
UACPI_UNUSED(type);
|
||||
|
||||
handler(ctx);
|
||||
return UACPI_STATUS_OK;
|
||||
}
|
||||
|
||||
uacpi_status uacpi_kernel_wait_for_work_completion(void)
|
||||
{
|
||||
return UACPI_STATUS_OK;
|
||||
}
|
290
kernel/hal/x86_64/uACPI/tests/runner/os.h
Normal file
290
kernel/hal/x86_64/uACPI/tests/runner/os.h
Normal file
@ -0,0 +1,290 @@
|
||||
#pragma once
|
||||
|
||||
#include "helpers.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#else
|
||||
#ifdef __WATCOMC__
|
||||
#include <process.h> // provides gettid
|
||||
#elif defined(__APPLE__)
|
||||
#include <mach/mach_time.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
typedef CRITICAL_SECTION mutex_t;
|
||||
typedef CONDITION_VARIABLE condvar_t;
|
||||
#define HAVE_TIMED_WAIT 0
|
||||
#else
|
||||
typedef pthread_mutex_t mutex_t;
|
||||
typedef pthread_cond_t condvar_t;
|
||||
|
||||
#if defined(__WATCOMC__) || defined(__APPLE__)
|
||||
#define HAVE_TIMED_WAIT 0
|
||||
#else
|
||||
#define HAVE_TIMED_WAIT 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define NANOSECONDS_PER_SECOND 1000000000ull
|
||||
|
||||
static inline uint64_t get_nanosecond_timer(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
static LARGE_INTEGER frequency;
|
||||
LARGE_INTEGER counter;
|
||||
|
||||
if (frequency.QuadPart == 0)
|
||||
if (!QueryPerformanceFrequency(&frequency))
|
||||
error("QueryPerformanceFrequency failed");
|
||||
|
||||
if (!QueryPerformanceCounter(&counter))
|
||||
error("QueryPerformanceCounter failed");
|
||||
|
||||
counter.QuadPart *= NANOSECONDS_PER_SECOND;
|
||||
return counter.QuadPart / frequency.QuadPart;
|
||||
#elif defined(__APPLE__)
|
||||
static struct mach_timebase_info tb;
|
||||
static bool initialized;
|
||||
|
||||
if (!initialized) {
|
||||
if (mach_timebase_info(&tb) != KERN_SUCCESS)
|
||||
error("mach_timebase_info failed");
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
return (mach_absolute_time() * tb.numer) / tb.denom;
|
||||
#else
|
||||
struct timespec ts;
|
||||
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts))
|
||||
error("clock_gettime failed");
|
||||
|
||||
return ts.tv_sec * NANOSECONDS_PER_SECOND + ts.tv_nsec;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void *get_thread_id(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return (void*)((uintptr_t)GetCurrentThreadId());
|
||||
#elif defined(__APPLE__)
|
||||
uint64_t id;
|
||||
|
||||
if (pthread_threadid_np(NULL, &id))
|
||||
error("pthread_threadid_np failed");
|
||||
return (void*)id;
|
||||
#else
|
||||
return (void*)((uintptr_t)gettid());
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void millisecond_sleep(uint64_t milliseconds)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
Sleep(milliseconds);
|
||||
#else
|
||||
if (usleep(milliseconds * 1000))
|
||||
error("usleep failed");
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void mutex_init(mutex_t *mutex)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
InitializeCriticalSection(mutex);
|
||||
#else
|
||||
if (pthread_mutex_init(mutex, NULL))
|
||||
error("pthread_mutex_init failed");
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void mutex_free(mutex_t *mutex)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DeleteCriticalSection(mutex);
|
||||
#else
|
||||
if (pthread_mutex_destroy(mutex))
|
||||
error("pthread_mutex_destroy failed");
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool mutex_try_lock(mutex_t *mutex)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return TryEnterCriticalSection(mutex);
|
||||
#else
|
||||
int err = pthread_mutex_trylock(mutex);
|
||||
|
||||
if (err == 0)
|
||||
return true;
|
||||
if (err != EBUSY)
|
||||
error("pthread_mutex_trylock failed");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void mutex_lock(mutex_t *mutex)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
EnterCriticalSection(mutex);
|
||||
#else
|
||||
if (pthread_mutex_lock(mutex))
|
||||
error("pthread_mutex_lock failed");
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool mutex_lock_timeout(mutex_t *mutex, uint64_t timeout_ns)
|
||||
{
|
||||
#if !HAVE_TIMED_WAIT
|
||||
uint64_t end = get_nanosecond_timer() + timeout_ns;
|
||||
|
||||
do {
|
||||
if (mutex_try_lock(mutex))
|
||||
return true;
|
||||
millisecond_sleep(1);
|
||||
} while (get_nanosecond_timer() < end);
|
||||
|
||||
return false;
|
||||
#else
|
||||
struct timespec spec;
|
||||
int err;
|
||||
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &spec))
|
||||
error("clock_gettime failed");
|
||||
|
||||
spec.tv_nsec += timeout_ns;
|
||||
spec.tv_sec += spec.tv_nsec / NANOSECONDS_PER_SECOND;
|
||||
spec.tv_nsec %= NANOSECONDS_PER_SECOND;
|
||||
|
||||
err = pthread_mutex_clocklock(mutex, CLOCK_MONOTONIC, &spec);
|
||||
if (err == 0)
|
||||
return true;
|
||||
if (err != ETIMEDOUT)
|
||||
error("pthread_mutex_clocklock failed");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void mutex_unlock(mutex_t *mutex)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
LeaveCriticalSection(mutex);
|
||||
#else
|
||||
if (pthread_mutex_unlock(mutex))
|
||||
error("pthread_mutex_unlock failed");
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void condvar_init(condvar_t *var)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
InitializeConditionVariable(var);
|
||||
#else
|
||||
if (pthread_cond_init(var, NULL))
|
||||
error("pthread_cond_init failed");
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void condvar_free(condvar_t *var)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
UACPI_UNUSED(var);
|
||||
#else
|
||||
if (pthread_cond_destroy(var))
|
||||
error("pthread_cond_destroy failed");
|
||||
#endif
|
||||
}
|
||||
|
||||
typedef bool (*condvar_pred_t)(void *ctx);
|
||||
|
||||
static inline void condvar_wait(
|
||||
condvar_t *var, mutex_t *mutex, condvar_pred_t pred, void *ctx
|
||||
)
|
||||
{
|
||||
while (!pred(ctx))
|
||||
#ifdef _WIN32
|
||||
if (!SleepConditionVariableCS(var, mutex, INFINITE))
|
||||
error("SleepConditionVariableCS failed");
|
||||
#else
|
||||
if (pthread_cond_wait(var, mutex))
|
||||
error("pthread_cond_wait failed");
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool condvar_wait_timeout(
|
||||
condvar_t *var, mutex_t *mutex, condvar_pred_t pred, void *ctx,
|
||||
uint64_t timeout_ns
|
||||
)
|
||||
{
|
||||
#if !HAVE_TIMED_WAIT
|
||||
uint64_t end = get_nanosecond_timer() + timeout_ns;
|
||||
|
||||
while (!pred(ctx)) {
|
||||
uint64_t cur = get_nanosecond_timer();
|
||||
#ifdef _WIN32
|
||||
DWORD milliseconds;
|
||||
#endif
|
||||
|
||||
if (cur >= end)
|
||||
return false;
|
||||
|
||||
#ifdef _WIN32
|
||||
milliseconds = (end - cur) / 1000;
|
||||
if (milliseconds == 0)
|
||||
milliseconds = 1;
|
||||
|
||||
if (!SleepConditionVariableCS(var, mutex, milliseconds) &&
|
||||
GetLastError() != ERROR_TIMEOUT) {
|
||||
error("SleepConditionVariableCS failed");
|
||||
}
|
||||
#else
|
||||
UACPI_UNUSED(var);
|
||||
mutex_unlock(mutex);
|
||||
millisecond_sleep(1);
|
||||
mutex_lock(mutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
struct timespec spec;
|
||||
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &spec))
|
||||
error("clock_gettime failed");
|
||||
|
||||
spec.tv_nsec += timeout_ns;
|
||||
spec.tv_sec += spec.tv_nsec / NANOSECONDS_PER_SECOND;
|
||||
spec.tv_nsec %= NANOSECONDS_PER_SECOND;
|
||||
|
||||
while (!pred(ctx)) {
|
||||
int err = pthread_cond_clockwait(var, mutex, CLOCK_MONOTONIC, &spec);
|
||||
|
||||
if (err == 0)
|
||||
continue;
|
||||
if (err != ETIMEDOUT)
|
||||
error("pthread_cond_clockwait failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void condvar_signal(condvar_t *var)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WakeConditionVariable(var);
|
||||
#else
|
||||
if (pthread_cond_signal(var))
|
||||
error("pthread_cond_signal failed");
|
||||
#endif
|
||||
}
|
2476
kernel/hal/x86_64/uACPI/tests/runner/resource_tests.c
Normal file
2476
kernel/hal/x86_64/uACPI/tests/runner/resource_tests.c
Normal file
File diff suppressed because it is too large
Load Diff
570
kernel/hal/x86_64/uACPI/tests/runner/test_runner.c
Normal file
570
kernel/hal/x86_64/uACPI/tests/runner/test_runner.c
Normal file
@ -0,0 +1,570 @@
|
||||
#include "argparser.h"
|
||||
#include "helpers.h"
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <uacpi/acpi.h>
|
||||
#include <uacpi/context.h>
|
||||
#include <uacpi/event.h>
|
||||
#include <uacpi/log.h>
|
||||
#include <uacpi/namespace.h>
|
||||
#include <uacpi/notify.h>
|
||||
#include <uacpi/opregion.h>
|
||||
#include <uacpi/osi.h>
|
||||
#include <uacpi/platform/types.h>
|
||||
#include <uacpi/resources.h>
|
||||
#include <uacpi/status.h>
|
||||
#include <uacpi/tables.h>
|
||||
#include <uacpi/types.h>
|
||||
#include <uacpi/uacpi.h>
|
||||
#include <uacpi/utilities.h>
|
||||
|
||||
void run_resource_tests(void);
|
||||
void test_object_api(void);
|
||||
void test_address_spaces(void);
|
||||
void interface_cleanup(void);
|
||||
|
||||
static uacpi_object_type string_to_object_type(const char *str)
|
||||
{
|
||||
if (strcmp(str, "int") == 0)
|
||||
return UACPI_OBJECT_INTEGER;
|
||||
if (strcmp(str, "str") == 0)
|
||||
return UACPI_OBJECT_STRING;
|
||||
|
||||
error("Unsupported type for validation: %s", str);
|
||||
return UACPI_OBJECT_UNINITIALIZED;
|
||||
}
|
||||
|
||||
static void validate_ret_against_expected(
|
||||
uacpi_object *obj, uacpi_object_type expected_type, const char *expected_val
|
||||
)
|
||||
{
|
||||
uacpi_object_type type = uacpi_object_get_type(obj);
|
||||
|
||||
if (type != expected_type)
|
||||
error(
|
||||
"returned type '%s' doesn't match expected '%s",
|
||||
uacpi_object_type_to_string(expected_type),
|
||||
uacpi_object_type_to_string(type)
|
||||
);
|
||||
|
||||
switch (type) {
|
||||
case UACPI_OBJECT_INTEGER: {
|
||||
uacpi_u64 expected_int = strtoull(expected_val, NULL, 0);
|
||||
uacpi_u64 actual_int;
|
||||
|
||||
uacpi_object_get_integer(obj, &actual_int);
|
||||
|
||||
if (expected_int != actual_int)
|
||||
error(
|
||||
"returned value '%" PRIu64 "' doesn't match expected '%" PRIu64
|
||||
"'", actual_int, expected_int
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
case UACPI_OBJECT_STRING: {
|
||||
uacpi_data_view view;
|
||||
const char *actual_str;
|
||||
|
||||
uacpi_object_get_string_or_buffer(obj, &view);
|
||||
actual_str = view.text;
|
||||
|
||||
if (strcmp(expected_val, actual_str) != 0)
|
||||
error(
|
||||
"returned value '%s' doesn't match expected '%s'",
|
||||
actual_str, expected_val
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
static void nested_printf(uacpi_u32 depth, const char *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
size_t padding = depth * 4;
|
||||
|
||||
while (padding-- > 0)
|
||||
printf(" ");
|
||||
|
||||
va_start(va, fmt);
|
||||
vprintf(fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
static void dump_resources(
|
||||
uacpi_u32 depth, uacpi_namespace_node *node,
|
||||
uacpi_status (*cb)(uacpi_namespace_node *, uacpi_resources **),
|
||||
const char *name
|
||||
)
|
||||
{
|
||||
uacpi_resources *res;
|
||||
uacpi_status ret = cb(node, &res);
|
||||
|
||||
if (ret == UACPI_STATUS_OK) {
|
||||
// TODO: dump resources here
|
||||
nested_printf(depth, " %s: <%u bytes>\n", name, res->length);
|
||||
uacpi_free_resources(res);
|
||||
} else if (ret != UACPI_STATUS_NOT_FOUND)
|
||||
nested_printf(
|
||||
depth, " %s: unable to evaluate (%s)\n", name,
|
||||
uacpi_status_to_string(ret)
|
||||
);
|
||||
}
|
||||
|
||||
static uacpi_iteration_decision dump_one_node(
|
||||
void *ptr, uacpi_namespace_node *node, uacpi_u32 depth
|
||||
)
|
||||
{
|
||||
struct uacpi_namespace_node_info *info;
|
||||
uacpi_status ret = uacpi_get_namespace_node_info(node, &info);
|
||||
const char *path;
|
||||
|
||||
UACPI_UNUSED(ptr);
|
||||
|
||||
if (uacpi_unlikely_error(ret)) {
|
||||
uacpi_object_name name = uacpi_namespace_node_name(node);
|
||||
|
||||
fprintf(
|
||||
stderr, "unable to get node %.4s info: %s\n", name.text,
|
||||
uacpi_status_to_string(ret)
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
path = uacpi_namespace_node_generate_absolute_path(node);
|
||||
nested_printf(
|
||||
depth, "%s [%s]", path, uacpi_object_type_to_string(info->type)
|
||||
);
|
||||
uacpi_free_absolute_path(path);
|
||||
|
||||
if (info->type == UACPI_OBJECT_METHOD)
|
||||
printf(" (%d args)", info->num_params);
|
||||
|
||||
if (info->flags)
|
||||
printf(" {\n");
|
||||
|
||||
if (info->flags)
|
||||
nested_printf(depth, " _ADR: %016" PRIX64 "\n", info->adr);
|
||||
|
||||
if (info->flags & UACPI_NS_NODE_INFO_HAS_HID)
|
||||
nested_printf(depth, " _HID: %s\n", info->hid.value);
|
||||
|
||||
if (info->flags & UACPI_NS_NODE_INFO_HAS_CID) {
|
||||
size_t i;
|
||||
|
||||
nested_printf(depth, " _CID: ");
|
||||
for (i = 0; i < info->cid.num_ids; ++i)
|
||||
printf("%s ", info->cid.ids[i].value);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (info->flags & UACPI_NS_NODE_INFO_HAS_UID)
|
||||
nested_printf(depth, " _UID: %s\n", info->uid.value);
|
||||
|
||||
if (info->flags & UACPI_NS_NODE_INFO_HAS_CLS)
|
||||
nested_printf(depth, " _CLS: %s\n", info->cls.value);
|
||||
|
||||
if (info->flags & UACPI_NS_NODE_INFO_HAS_SXD)
|
||||
nested_printf(
|
||||
depth, " _SxD: S1->D%d S2->D%d S3->D%d S4->D%d\n", info->sxd[0],
|
||||
info->sxd[1], info->sxd[2], info->sxd[3]
|
||||
);
|
||||
|
||||
if (info->flags & UACPI_NS_NODE_INFO_HAS_SXW)
|
||||
nested_printf(
|
||||
depth, " _SxW: S0->D%d S1->D%d S2->D%d S3->D%d S4->D%d\n",
|
||||
info->sxw[0], info->sxw[1], info->sxw[2], info->sxw[3], info->sxw[4]
|
||||
);
|
||||
|
||||
if (info->flags) {
|
||||
if (info->type == UACPI_OBJECT_DEVICE) {
|
||||
dump_resources(depth, node, uacpi_get_current_resources, "_CRS");
|
||||
dump_resources(depth, node, uacpi_get_possible_resources, "_PRS");
|
||||
}
|
||||
|
||||
nested_printf(depth, "}\n");
|
||||
} else
|
||||
printf("\n");
|
||||
|
||||
uacpi_free_namespace_node_info(info);
|
||||
return UACPI_ITERATION_DECISION_CONTINUE;
|
||||
}
|
||||
|
||||
static void enumerate_namespace(void)
|
||||
{
|
||||
uacpi_namespace_node *root = uacpi_namespace_root();
|
||||
|
||||
dump_one_node(NULL, root, 0);
|
||||
uacpi_namespace_for_each_child_simple(root, dump_one_node, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* DefinitionBlock ("x.aml", "SSDT", 1, "uTEST", "OVERRIDE", 0xF0F0F0F0)
|
||||
* {
|
||||
* Name (VAL, "TestRunner")
|
||||
* }
|
||||
*/
|
||||
static uint8_t table_override[] = {
|
||||
0x53, 0x53, 0x44, 0x54, 0x35, 0x00, 0x00, 0x00,
|
||||
0x01, 0xa1, 0x75, 0x54, 0x45, 0x53, 0x54, 0x00,
|
||||
0x4f, 0x56, 0x45, 0x52, 0x52, 0x49, 0x44, 0x45,
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0x49, 0x4e, 0x54, 0x4c,
|
||||
0x25, 0x09, 0x20, 0x20, 0x08, 0x56, 0x41, 0x4c,
|
||||
0x5f, 0x0d, 0x54, 0x65, 0x73, 0x74, 0x52, 0x75,
|
||||
0x6e, 0x6e, 0x65, 0x72, 0x00
|
||||
};
|
||||
|
||||
/*
|
||||
* DefinitionBlock ("x.aml", "SSDT", 1, "uTEST", "RUNRIDTB", 0xF0F0F0F0)
|
||||
* {
|
||||
* Name (\_SI.TID, "uACPI")
|
||||
* Printf("TestRunner ID SSDT loaded!")
|
||||
* }
|
||||
*/
|
||||
static uint8_t runner_id_table[] = {
|
||||
0x53, 0x53, 0x44, 0x54, 0x55, 0x00, 0x00, 0x00,
|
||||
0x01, 0x45, 0x75, 0x54, 0x45, 0x53, 0x54, 0x00,
|
||||
0x52, 0x55, 0x4e, 0x52, 0x49, 0x44, 0x54, 0x42,
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0x49, 0x4e, 0x54, 0x4c,
|
||||
0x25, 0x09, 0x20, 0x20, 0x08, 0x5c, 0x2e, 0x5f,
|
||||
0x53, 0x49, 0x5f, 0x54, 0x49, 0x44, 0x5f, 0x0d,
|
||||
0x75, 0x41, 0x43, 0x50, 0x49, 0x00, 0x70, 0x0d,
|
||||
0x54, 0x65, 0x73, 0x74, 0x52, 0x75, 0x6e, 0x6e,
|
||||
0x65, 0x72, 0x20, 0x49, 0x44, 0x20, 0x53, 0x53,
|
||||
0x44, 0x54, 0x20, 0x6c, 0x6f, 0x61, 0x64, 0x65,
|
||||
0x64, 0x21, 0x00, 0x5b, 0x31
|
||||
};
|
||||
|
||||
static uacpi_table_installation_disposition handle_table_install(
|
||||
struct acpi_sdt_hdr *hdr, uacpi_u64 *out_override
|
||||
)
|
||||
{
|
||||
if (!strncmp(hdr->oem_table_id, "DENYTABL", sizeof(hdr->oem_table_id)))
|
||||
return UACPI_TABLE_INSTALLATION_DISPOSITON_DENY;
|
||||
|
||||
if (strncmp(hdr->oem_table_id, "OVERTABL", sizeof(hdr->oem_table_id)))
|
||||
return UACPI_TABLE_INSTALLATION_DISPOSITON_ALLOW;
|
||||
|
||||
*out_override = (uacpi_virt_addr)table_override;
|
||||
return UACPI_TABLE_INSTALLATION_DISPOSITON_VIRTUAL_OVERRIDE;
|
||||
}
|
||||
|
||||
static uacpi_status handle_notify(
|
||||
uacpi_handle handle, uacpi_namespace_node *node, uacpi_u64 value
|
||||
)
|
||||
{
|
||||
const char *path = uacpi_namespace_node_generate_absolute_path(node);
|
||||
|
||||
UACPI_UNUSED(handle);
|
||||
|
||||
printf("Received a notification from %s %" PRIx64 "\n", path, value);
|
||||
|
||||
free((void*)path);
|
||||
return UACPI_STATUS_OK;
|
||||
}
|
||||
|
||||
static uacpi_status handle_ec(uacpi_region_op op, uacpi_handle op_data)
|
||||
{
|
||||
switch (op) {
|
||||
case UACPI_REGION_OP_READ: {
|
||||
uacpi_region_rw_data *rw_data = (uacpi_region_rw_data*)op_data;
|
||||
|
||||
rw_data->value = 0;
|
||||
UACPI_FALLTHROUGH;
|
||||
}
|
||||
case UACPI_REGION_OP_ATTACH:
|
||||
case UACPI_REGION_OP_DETACH:
|
||||
case UACPI_REGION_OP_WRITE:
|
||||
return UACPI_STATUS_OK;
|
||||
default:
|
||||
return UACPI_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
}
|
||||
|
||||
static uacpi_interrupt_ret handle_gpe(
|
||||
uacpi_handle handle, uacpi_namespace_node *node, uint16_t idx
|
||||
)
|
||||
{
|
||||
UACPI_UNUSED(handle);
|
||||
UACPI_UNUSED(node);
|
||||
UACPI_UNUSED(idx);
|
||||
|
||||
return UACPI_INTERRUPT_HANDLED | UACPI_GPE_REENABLE;
|
||||
}
|
||||
|
||||
static void run_test(
|
||||
const char *dsdt_path, const vector_t *ssdt_paths,
|
||||
uacpi_object_type expected_type, const char *expected_value,
|
||||
bool dump_namespace
|
||||
)
|
||||
{
|
||||
static uint8_t early_table_buf[4096];
|
||||
struct acpi_rsdp rsdp = { 0 };
|
||||
struct full_xsdt *xsdt = make_xsdt(&rsdp, dsdt_path, ssdt_paths);
|
||||
uacpi_status st;
|
||||
uacpi_table tbl;
|
||||
bool is_test_mode;
|
||||
uacpi_object *ret = NULL;
|
||||
|
||||
g_rsdp = (uacpi_phys_addr)((uintptr_t)&rsdp);
|
||||
|
||||
st = uacpi_setup_early_table_access(
|
||||
early_table_buf, sizeof(early_table_buf)
|
||||
);
|
||||
ensure_ok_status(st);
|
||||
|
||||
st = uacpi_table_find_by_signature(ACPI_DSDT_SIGNATURE, &tbl);
|
||||
ensure_ok_status(st);
|
||||
|
||||
if (strncmp(tbl.hdr->signature, ACPI_DSDT_SIGNATURE, 4) != 0)
|
||||
error("broken early table access!");
|
||||
|
||||
st = uacpi_table_unref(&tbl);
|
||||
ensure_ok_status(st);
|
||||
|
||||
st = uacpi_initialize(UACPI_FLAG_NO_ACPI_MODE);
|
||||
ensure_ok_status(st);
|
||||
|
||||
/*
|
||||
* Go through all AML tables and manually bump their reference counts here
|
||||
* so that they're mapped before the call to uacpi_namespace_load(). The
|
||||
* reason we need this is to disambiguate calls to uacpi_kernel_map() with
|
||||
* a synthetic physical address (that is actually a virtual address for
|
||||
* tables that we constructed earlier) or a real physical address that comes
|
||||
* from some operation region or any other AML code or action.
|
||||
*/
|
||||
uacpi_table_find_by_signature(ACPI_DSDT_SIGNATURE, &tbl);
|
||||
|
||||
st = uacpi_table_find_by_signature(ACPI_SSDT_SIGNATURE, &tbl);
|
||||
while (st == UACPI_STATUS_OK) {
|
||||
uacpi_table_ref(&tbl);
|
||||
st = uacpi_table_find_next_with_same_signature(&tbl);
|
||||
}
|
||||
|
||||
g_expect_virtual_addresses = false;
|
||||
|
||||
st = uacpi_install_notify_handler(
|
||||
uacpi_namespace_root(), handle_notify, NULL
|
||||
);
|
||||
ensure_ok_status(st);
|
||||
|
||||
st = uacpi_set_table_installation_handler(handle_table_install);
|
||||
ensure_ok_status(st);
|
||||
|
||||
st = uacpi_install_interface("TestRunner", UACPI_INTERFACE_KIND_FEATURE);
|
||||
ensure_ok_status(st);
|
||||
|
||||
st = uacpi_uninstall_interface("Windows 2006");
|
||||
ensure_ok_status(st);
|
||||
|
||||
st = uacpi_uninstall_interface("Windows 2006");
|
||||
if (st != UACPI_STATUS_NOT_FOUND)
|
||||
error("couldn't uninstall interface");
|
||||
|
||||
st = uacpi_enable_host_interface(UACPI_HOST_INTERFACE_3_0_THERMAL_MODEL);
|
||||
ensure_ok_status(st);
|
||||
|
||||
st = uacpi_enable_host_interface(UACPI_HOST_INTERFACE_MODULE_DEVICE);
|
||||
ensure_ok_status(st);
|
||||
|
||||
is_test_mode = expected_type != UACPI_OBJECT_UNINITIALIZED;
|
||||
if (is_test_mode) {
|
||||
st = uacpi_table_install(runner_id_table, NULL);
|
||||
ensure_ok_status(st);
|
||||
}
|
||||
|
||||
st = uacpi_namespace_load();
|
||||
ensure_ok_status(st);
|
||||
|
||||
if (is_test_mode) {
|
||||
uacpi_object *runner_id = UACPI_NULL;
|
||||
uacpi_data_view view;
|
||||
|
||||
st = uacpi_eval_typed(
|
||||
UACPI_NULL, "\\_SI.TID", UACPI_NULL, UACPI_OBJECT_STRING_BIT,
|
||||
&runner_id
|
||||
);
|
||||
ensure_ok_status(st);
|
||||
|
||||
st = uacpi_object_get_string_or_buffer(runner_id, &view);
|
||||
ensure_ok_status(st);
|
||||
|
||||
if (strcmp(view.text, "uACPI") != 0)
|
||||
error("invalid test runner id");
|
||||
uacpi_object_unref(runner_id);
|
||||
}
|
||||
|
||||
st = uacpi_install_address_space_handler(
|
||||
uacpi_namespace_root(), UACPI_ADDRESS_SPACE_EMBEDDED_CONTROLLER,
|
||||
handle_ec, NULL
|
||||
);
|
||||
ensure_ok_status(st);
|
||||
|
||||
st = uacpi_install_gpe_handler(
|
||||
UACPI_NULL, 123, UACPI_GPE_TRIGGERING_EDGE, handle_gpe, NULL
|
||||
);
|
||||
ensure_ok_status(st);
|
||||
|
||||
st = uacpi_enable_gpe(UACPI_NULL, 123);
|
||||
ensure_ok_status(st);
|
||||
|
||||
st = uacpi_disable_gpe(UACPI_NULL, 123);
|
||||
ensure_ok_status(st);
|
||||
|
||||
st = uacpi_uninstall_gpe_handler(UACPI_NULL, 123, handle_gpe);
|
||||
ensure_ok_status(st);
|
||||
|
||||
st = uacpi_namespace_initialize();
|
||||
ensure_ok_status(st);
|
||||
|
||||
if (dump_namespace)
|
||||
enumerate_namespace();
|
||||
|
||||
if (!is_test_mode)
|
||||
goto done;
|
||||
|
||||
if (strcmp(expected_value, "check-object-api-works") == 0) {
|
||||
test_object_api();
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (strcmp(expected_value, "check-address-spaces-work") == 0) {
|
||||
test_address_spaces();
|
||||
goto done;
|
||||
}
|
||||
|
||||
st = uacpi_eval(UACPI_NULL, "\\MAIN", UACPI_NULL, &ret);
|
||||
|
||||
ensure_ok_status(st);
|
||||
if (ret == NULL)
|
||||
error("\\MAIN didn't return a value");
|
||||
validate_ret_against_expected(ret, expected_type, expected_value);
|
||||
|
||||
uacpi_object_unref(ret);
|
||||
done:
|
||||
uacpi_state_reset();
|
||||
delete_xsdt(xsdt, ssdt_paths->count);
|
||||
interface_cleanup();
|
||||
}
|
||||
|
||||
static uacpi_log_level log_level_from_string(const char *arg)
|
||||
{
|
||||
static struct {
|
||||
const char *str;
|
||||
uacpi_log_level level;
|
||||
} log_levels[] = {
|
||||
{ "debug", UACPI_LOG_DEBUG },
|
||||
{ "trace", UACPI_LOG_TRACE },
|
||||
{ "info", UACPI_LOG_INFO },
|
||||
{ "warning", UACPI_LOG_WARN },
|
||||
{ "error", UACPI_LOG_ERROR },
|
||||
};
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < UACPI_ARRAY_SIZE(log_levels); i++)
|
||||
if (strcmp(log_levels[i].str, arg) == 0)
|
||||
return log_levels[i].level;
|
||||
|
||||
error("invalid log level %s", arg);
|
||||
return UACPI_LOG_INFO;
|
||||
}
|
||||
|
||||
static arg_spec_t DSDT_PATH_ARG = ARG_POS(
|
||||
"dsdt-path-or-keyword",
|
||||
"path to the DSDT to run or \"resource-tests\" to run the resource tests"
|
||||
);
|
||||
|
||||
static arg_spec_t EXPECT_ARG = ARG_LIST(
|
||||
"expect", 'r',
|
||||
"test mode, evaluate \\MAIN and expect <expected-type> <expected-value>"
|
||||
);
|
||||
static arg_spec_t EXTRA_TABLES_ARG = ARG_LIST(
|
||||
"extra-tables", 'x', "a list of extra SSDTs to load"
|
||||
);
|
||||
static arg_spec_t ENUMERATE_NAMESPACE_ARG = ARG_FLAG(
|
||||
"enumerate-namespace", 'd', "dump the entire namespace after loading it"
|
||||
);
|
||||
static arg_spec_t WHILE_LOOP_TIMEOUT_ARG = ARG_PARAM(
|
||||
"while-loop-timeout", 't',
|
||||
"number of seconds to use for the while loop timeout"
|
||||
);
|
||||
static arg_spec_t LOG_LEVEL_ARG = ARG_PARAM(
|
||||
"log-level", 'l',
|
||||
"log level to set, one of: debug, trace, info, warning, error"
|
||||
);
|
||||
static arg_spec_t HELP_ARG = ARG_HELP(
|
||||
"help", 'h', "Display this menu and exit"
|
||||
);
|
||||
|
||||
static arg_spec_t *const POSITIONAL_ARGS[] = {
|
||||
&DSDT_PATH_ARG,
|
||||
};
|
||||
|
||||
static arg_spec_t *const OPTION_ARGS[] = {
|
||||
&EXPECT_ARG,
|
||||
&EXTRA_TABLES_ARG,
|
||||
&ENUMERATE_NAMESPACE_ARG,
|
||||
&WHILE_LOOP_TIMEOUT_ARG,
|
||||
&LOG_LEVEL_ARG,
|
||||
&HELP_ARG,
|
||||
};
|
||||
|
||||
static const arg_parser_t PARSER = {
|
||||
.positional_args = POSITIONAL_ARGS,
|
||||
.num_positional_args = UACPI_ARRAY_SIZE(POSITIONAL_ARGS),
|
||||
.option_args = OPTION_ARGS,
|
||||
.num_option_args = UACPI_ARRAY_SIZE(OPTION_ARGS),
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *dsdt_path_or_keyword;
|
||||
const char *expected_value = NULL;
|
||||
uacpi_object_type expected_type = UACPI_OBJECT_UNINITIALIZED;
|
||||
bool dump_namespace;
|
||||
uacpi_log_level log_level;
|
||||
|
||||
parse_args(&PARSER, argc, argv);
|
||||
|
||||
uacpi_context_set_loop_timeout(get_uint_or(&WHILE_LOOP_TIMEOUT_ARG, 3));
|
||||
|
||||
dsdt_path_or_keyword = get(&DSDT_PATH_ARG);
|
||||
if (strcmp(dsdt_path_or_keyword, "resource-tests") == 0) {
|
||||
run_resource_tests();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (is_set(&EXPECT_ARG)) {
|
||||
if (EXPECT_ARG.values.count != 2)
|
||||
error("bad --expect format");
|
||||
|
||||
expected_type = string_to_object_type(EXPECT_ARG.values.blobs[0].data);
|
||||
expected_value = EXPECT_ARG.values.blobs[1].data;
|
||||
}
|
||||
|
||||
dump_namespace = is_set(&ENUMERATE_NAMESPACE_ARG);
|
||||
// Don't spam the log with traces if enumeration is enabled
|
||||
log_level = dump_namespace ? UACPI_LOG_INFO : UACPI_LOG_TRACE;
|
||||
|
||||
if (is_set(&LOG_LEVEL_ARG))
|
||||
log_level = log_level_from_string(get(&LOG_LEVEL_ARG));
|
||||
|
||||
uacpi_context_set_log_level(log_level);
|
||||
|
||||
run_test(
|
||||
dsdt_path_or_keyword, &EXTRA_TABLES_ARG.values, expected_type,
|
||||
expected_value, dump_namespace
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user