Integrate uACPI

This commit is contained in:
2025-08-17 18:37:57 +02:00
parent 069870cd0d
commit 92ccd189e7
166 changed files with 42104 additions and 33 deletions

View File

@ -0,0 +1 @@
build-*

View 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 ()

View 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);
}

View 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, &current_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;
}

View 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;
}

View 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;
}

View 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;
}
}

View 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;
}

View 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
}

File diff suppressed because it is too large Load Diff

View 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;
}