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,255 @@
import os
import copy
import subprocess
from typing import List, Optional, Callable
from utilities.asl import ASL, ASLSource
ACPICA_BUFFER_PRINT_PREFIX = " 0000: "
def _parse_acpiexec_buffers(raw_output: str) -> List[List[int]]:
lines = raw_output.split("\n")
answers = []
for i, line in enumerate(lines):
if "Evaluating" in line:
lines = lines[i + 1:]
break
for line in lines:
if not line.startswith(ACPICA_BUFFER_PRINT_PREFIX):
continue
line = line.removeprefix(ACPICA_BUFFER_PRINT_PREFIX)
buffer_bytes = []
for x in line.split(" "):
# Buffers are printed out with ascii disassembly at the end.
# Skip as soon as we encounter empty space.
if x == "":
break
buffer_bytes.append(int(x, base=16))
answers.append(buffer_bytes)
return answers
def _generate_for_each_bit_combination(
src: ASLSource, per_combo_cb: Callable,
final_cb: Optional[Callable] = None
) -> None:
methods = []
for i in range(0, 64):
method_name = f"FT{i}"
methods.append(method_name)
src.l(ASL.method(method_name))
src.block_begin()
for j in range(0, 65):
if (i >= j):
continue
per_combo_cb(i, j, src)
src.block_end()
src.l(ASL.method("MAIN"))
src.block_begin()
for method in methods:
src.l(ASL.invoke(method))
if final_cb is not None:
final_cb(src)
src.block_end()
src.finalize()
_READS_ANSWERS_NAME = "buffer-reads-answers"
_WRITES_ANSWERS_NAME = "buffer-writes-answers"
def _generate_buffer_reads_answers(
compiler: str, bin_dir: str, src: ASLSource
) -> List[List[int]]:
output_path = os.path.join(bin_dir, _READS_ANSWERS_NAME + ".aml")
if not os.path.exists(output_path):
_do_generate_buffer_reads_answers(compiler, bin_dir, src)
raw_answers = subprocess.check_output(
["acpiexec", "-b", "execute MAIN", output_path],
universal_newlines=True
)
return _parse_acpiexec_buffers(raw_answers)
def _generate_buffer_writes_answers(
compiler: str, bin_dir: str, src: ASLSource
) -> List[List[int]]:
output_path = os.path.join(bin_dir, _WRITES_ANSWERS_NAME + ".aml")
if not os.path.exists(output_path):
_do_generate_buffer_writes_answers(compiler, bin_dir, src)
raw_answers = subprocess.check_output(
["acpiexec", "-b", "execute MAIN", output_path],
universal_newlines=True
)
return _parse_acpiexec_buffers(raw_answers)
def _do_generate_buffer_reads_answers(
compiler: str, bin_dir: str, src: ASLSource
) -> None:
def gen_buffer_dump(i, j, src):
field_size = j - i
field_name = f"FI{field_size:02X}"
src.l(ASL.create_field("BUFF", i, field_size, field_name))
src.l(ASL.assign("Debug", field_name))
_generate_for_each_bit_combination(src, gen_buffer_dump)
answers_src_path = os.path.join(bin_dir, _READS_ANSWERS_NAME + ".asl")
src.dump(answers_src_path)
ASLSource.compile(answers_src_path, compiler, bin_dir)
def _do_generate_buffer_writes_answers(
compiler: str, bin_dir: str, src: ASLSource
) -> None:
def gen_buffer_dump(i, j, src):
field_size = j - i
field_name = f"FI{field_size:02X}"
src.l(ASL.create_field("BUFX", i, field_size, field_name))
src.l(ASL.assign(field_name, "BUFF"))
src.l(ASL.assign("Debug", field_name))
_generate_for_each_bit_combination(src, gen_buffer_dump)
writes_src_path = os.path.join(bin_dir, _WRITES_ANSWERS_NAME + ".asl")
src.dump(writes_src_path)
ASLSource.compile(writes_src_path, compiler, bin_dir)
_READS_TEST_NAME = "2080-buffer-reads"
_WRITES_TEST_NAME = "2080-buffer-writes"
def generate_buffer_reads_test(compiler: str, bin_dir: str) -> str:
output_path = os.path.join(bin_dir, _READS_TEST_NAME + ".asl")
if os.path.exists(output_path):
return output_path
return _do_generate_buffer_reads_test(compiler, bin_dir)
def generate_buffer_writes_test(compiler: str, bin_dir: str) -> str:
output_path = os.path.join(bin_dir, _WRITES_TEST_NAME + ".asl")
if os.path.exists(output_path):
return output_path
return _do_generate_buffer_writes_test(compiler, bin_dir)
def _generate_buffer_test_prologue() -> ASLSource:
src = ASLSource(2)
src.l(ASL.name(
"BUFF",
ASL.buffer([0xAC, 0x12, 0x42, 0xCA, 0xDE, 0xFF, 0xCB, 0xDD])
))
src.l(ASL.name(
"BUFX",
ASL.buffer(count=8)
))
return src
def _generate_buffer_test_harness(src: ASLSource) -> None:
src.l(ASL.name("FAIL", 0))
src.l(ASL.name("PASS", 0))
src.l(ASL.method("FDBG", 3))
src.block_begin()
src.l(ASL.assign("Debug", "Arg0"))
src.l(ASL.assign("Debug", "Arg1"))
src.l(ASL.assign("Debug", "Arg2"))
src.l(ASL.increment("FAIL"))
src.block_end()
def _do_generate_buffer_reads_test(compiler: str, bin_dir: str) -> str:
src = _generate_buffer_test_prologue()
answers = _generate_buffer_reads_answers(compiler, bin_dir,
copy.deepcopy(src))
_generate_buffer_test_harness(src)
answer_idx = 0
def gen_buffer_check(i, j, src):
nonlocal answer_idx
field_size = j - i
field_name = f"FI{field_size:02X}"
src.l(ASL.create_field("BUFF", i, field_size, field_name))
src.iff(ASL.equal(field_name, ASL.buffer(answers[answer_idx])))
answer_idx += 1
src.l(ASL.increment("PASS"))
src.elsee()
src.l(ASL.invoke("FDBG", [
field_name, "__LINE__", f'"{field_name}"'
]))
src.block_end()
_generate_for_each_bit_combination(src, gen_buffer_check,
lambda src: src.l(ASL.returnn("FAIL")))
test_src_path = os.path.join(bin_dir, _READS_TEST_NAME + ".asl")
src.dump_as_test_case(test_src_path, "Reads from buffer fields",
"int", "0")
return test_src_path
def _do_generate_buffer_writes_test(compiler: str, bin_dir: str) -> str:
src = _generate_buffer_test_prologue()
answers = _generate_buffer_writes_answers(compiler, bin_dir,
copy.deepcopy(src))
_generate_buffer_test_harness(src)
answer_idx = 0
def gen_buffer_check(i, j, src):
nonlocal answer_idx
field_size = j - i
field_name = f"FI{field_size:02X}"
src.l(ASL.create_field("BUFX", i, field_size, field_name))
src.l(ASL.assign(field_name, "BUFF"))
src.iff(ASL.equal(field_name, ASL.buffer(answers[answer_idx])))
answer_idx += 1
src.l(ASL.increment("PASS"))
src.elsee()
src.l(ASL.invoke("FDBG", [
field_name, "__LINE__", f'"{field_name}"'
]))
src.block_end()
src.l(ASL.assign("BUFX", 0))
_generate_for_each_bit_combination(src, gen_buffer_check,
lambda src: src.l(ASL.returnn("FAIL")))
test_src_path = os.path.join(bin_dir, _WRITES_TEST_NAME + ".asl")
src.dump_as_test_case(test_src_path, "Writes to buffer fields",
"int", "0")
return test_src_path

View File

@ -0,0 +1,476 @@
#!/usr/bin/python3
import subprocess
import argparse
import os
import sys
import time
import platform
from multiprocessing import Manager, Pool, Queue
from typing import List, Tuple, Optional
from types import TracebackType
from abc import ABC, abstractmethod
from utilities.asl import ASLSource
import generated_test_cases.buffer_field as bf
def abs_path_to_current_dir() -> str:
return os.path.dirname(os.path.abspath(__file__))
def generate_test_cases(compiler: str, bin_dir: str) -> List[str]:
return [
bf.generate_buffer_reads_test(compiler, bin_dir),
bf.generate_buffer_writes_test(compiler, bin_dir),
]
ACPI_DUMPS_URL = "https://github.com/UltraOS/ACPIDumps.git"
class TestCase(ABC):
def __init__(self, path: str, name: str):
self.path = path
self.name = name
@abstractmethod
def extra_runner_args(self) -> List[str]:
pass
class BarebonesTestCase(TestCase):
def __init__(
self, name: str
) -> None:
super().__init__(name, name)
def extra_runner_args(self) -> List[str]:
return []
class TestCaseWithMain(TestCase):
def __init__(
self, path: str, name: str, rtype: str, value: str
) -> None:
super().__init__(path, f"{os.path.basename(path)}:{name}")
self.rtype = rtype
self.value = value
def extra_runner_args(self) -> List[str]:
return ["--expect", self.rtype, self.value]
class TestCaseHardwareBlob(TestCase):
def __init__(self, path: str) -> None:
dsdt_path = os.path.join(path, "dsdt.dat")
super().__init__(dsdt_path, os.path.basename(path))
self.ssdt_paths = [
path for path in os.listdir(path)
if path.startswith("ssdt") and path.endswith(".dat")
]
def extract_ssdt_number(path: str) -> int:
number = ""
assert path.startswith("ssdt")
for c in path[4:]:
if not c.isdigit():
break
number += c
# some blobs apparently come with just "ssdt.dat" and not
# "ssdtX.dat", take that into account here.
return 0 if not number else int(number)
if self.ssdt_paths:
self.ssdt_paths.sort(key=extract_ssdt_number)
self.ssdt_paths = [
os.path.join(path, ssdt_path) for ssdt_path in self.ssdt_paths
]
def extra_runner_args(self) -> List[str]:
args = ["--enumerate-namespace"]
if self.ssdt_paths:
args.append("--extra-tables")
args.extend(self.ssdt_paths)
return args
def generate_large_test_cases(extractor: str, bin_dir: str) -> List[TestCase]:
acpi_dumps_dir = os.path.join(abs_path_to_current_dir(), "acpi-dumps")
large_tests_dir = os.path.join(bin_dir, "large-tests")
if not os.path.exists(acpi_dumps_dir):
subprocess.check_call(["git", "clone", ACPI_DUMPS_URL, acpi_dumps_dir])
os.makedirs(large_tests_dir, exist_ok=True)
test_cases = []
def recurse_one(path, depth=1):
for obj in os.listdir(path):
if obj.startswith("."):
continue
obj_path = os.path.join(path, obj)
if os.path.isdir(obj_path):
recurse_one(obj_path, depth + 1)
continue
if depth == 1 or not obj.endswith(".bin"):
continue
print(f"Preparing HW blob {obj_path}...")
split_path = obj_path.split(os.path.sep)[-depth:]
fixed_up_path = [
seg.replace(" ", "_").lower() for seg in split_path
]
test_case_name = "_".join(fixed_up_path).replace(".bin", "")
this_test_dir = os.path.join(large_tests_dir, test_case_name)
if (not os.path.exists(this_test_dir) or not
os.path.exists(os.path.join(this_test_dir, "dsdt.dat"))):
os.makedirs(this_test_dir, exist_ok=True)
# These are two separate invocations because of a bug in
# acpixtract where it exits with -1 when there isn't an SSDT
# inside a blob, even though it's specified as optional in
# code. Merge once https://github.com/acpica/acpica/pull/959
# is shipped everywhere.
subprocess.check_call(
[extractor, "-sDSDT", obj_path], cwd=this_test_dir,
stdout=subprocess.DEVNULL
)
subprocess.run(
[extractor, "-sSSDT", obj_path], cwd=this_test_dir,
stdout=subprocess.DEVNULL
)
test_cases.append(TestCaseHardwareBlob(this_test_dir))
recurse_one(acpi_dumps_dir)
return test_cases
def get_case_name_and_expected_result(case: str) -> Tuple[str, str, str]:
with open(case) as tc:
name = tc.readline()
name = name[name.find(":") + 1:].strip()
expected_line = tc.readline()
expected_line = expected_line[expected_line.find(":") + 1:].strip()
expected = [val.strip() for val in expected_line.split("=>")]
return name, expected[0], expected[1]
class TestHeaderFooter:
def __init__(self, text: str) -> None:
self.hdr = "{:=^80}".format(" " + text + " ")
def __enter__(self) -> None:
print(self.hdr, flush=True)
def __exit__(
self, exc_type: Optional[type[BaseException]],
ex: Optional[BaseException], traceback: Optional[TracebackType]
) -> Optional[bool]:
print("=" * len(self.hdr), flush=True)
return None
def run_resource_tests(runner: str) -> int:
with TestHeaderFooter("Resource Conversion Tests"):
return subprocess.run([runner, "resource-tests"]).returncode
def compile_test_cases(
test_cases: List[str], compiler: str, bin_dir: str
) -> List[TestCase]:
compiled_cases: List[TestCase] = []
for case in test_cases:
print(f"Compiling {case}...", end="")
# Skip the table loading test for old iASL, it prints bogus error
# messages and refuses to compile the test case no matter what I try:
#
# If (!Load(TABL)) {
# Error 6126 - syntax error ^
#
if os.path.basename(case) == "table-loading-0.asl":
out = subprocess.check_output([compiler, "-v"],
universal_newlines=True)
# I don't know which versions it's broken for specifically, this
# one comes with Ubuntu 22.04, so hardcode it.
if "20200925" in out:
print("SKIPPED (bugged iASL)", flush=True)
continue
compiled_cases.append(
TestCaseWithMain(
ASLSource.compile(case, compiler, bin_dir),
*get_case_name_and_expected_result(case)
)
)
print("")
return compiled_cases
def run_single_test(case: TestCase, results: Queue, runner: str) -> bool:
timeout = False
start_time = time.time()
proc = subprocess.Popen(
[runner, case.path, *case.extra_runner_args()],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
universal_newlines=True
)
try:
stdout, stderr = proc.communicate(timeout=60)
elapsed_time = time.time() - start_time
except subprocess.TimeoutExpired:
proc.kill()
stdout, stderr = proc.communicate()
timeout = True
elapsed_time = time.time() - start_time
if proc.returncode == 0:
results.put((True, case, elapsed_time))
return True
else:
results.put((False, case, elapsed_time, stdout, stderr, timeout))
return False
def run_tests(cases: List[TestCase], runner: str, parallelism: int) -> bool:
pass_count = 0
fail_count = 0
start_time = time.time()
def print_test_header(case: TestCase, success: bool, timeout: bool,
elapsed: float) -> None:
status_str = "OK" if success else "TIMEOUT" if timeout else "FAILED"
print(f"[{pass_count}/{len(cases)}] {case.name} "
f"{status_str} in {elapsed:.2f}s", flush=True)
def format_output(data: str) -> str:
return "\n".join(["\t" + line for line in data.split("\n")])
with Pool(processes=parallelism) as pool:
manager = Manager()
result_queue = manager.Queue()
pool.starmap_async(run_single_test,
[(case, result_queue, runner) for case in cases])
while pass_count + fail_count < len(cases):
success, case, elapsed_time, *args = result_queue.get()
if success:
pass_count += 1
print_test_header(case, True, False, elapsed_time)
else:
fail_count += 1
stdout, stderr, timeout = args
print_test_header(case, False, timeout, elapsed_time)
stdout_output = format_output(stdout)
stderr_output = format_output(stderr)
if stdout_output:
print(f"STDOUT FOR {case.name}:", flush=True)
print(stdout_output, flush=True)
else:
print(f"NO STDOUT FROM TEST {case.name}", flush=True)
if stderr_output:
print(f"STDERR FOR {case.name}:", flush=True)
print(stderr_output, flush=True)
else:
print(f"NO STDERR FROM TEST {case.name}", flush=True)
pool.close()
pool.join()
elapsed_time = time.time() - start_time
print(f"SUMMARY: {pass_count}/{len(cases)} in {elapsed_time:.2f}s", end="")
if not fail_count:
print(" (ALL PASS!)")
else:
print(f" ({fail_count} FAILED)")
return not fail_count
def test_relpath(*args: str) -> str:
return os.path.join(abs_path_to_current_dir(), *args)
def platform_name_for_binary(binary: str) -> str:
if platform.system() == "Windows":
binary += ".exe"
return binary
def test_runner_binary() -> str:
return platform_name_for_binary("test-runner")
def barebones_test_runner_binary() -> str:
return platform_name_for_binary("barebones-test-runner")
def build_test_runner(bitness: int, watcom: bool) -> Tuple[str, str]:
build_dir = f"build-{platform.system().lower()}-{bitness}bits"
if watcom:
build_dir = f"{build_dir}-watcom"
runner_build_dir = test_relpath("runner", build_dir)
runner_exe = os.path.join(runner_build_dir, test_runner_binary())
barebones_runner_exe = os.path.join(
runner_build_dir, barebones_test_runner_binary()
)
use_ninja = False
if platform.system() != "Windows":
try:
subprocess.run(["ninja", "--version"], check=True,
stdout=subprocess.DEVNULL)
use_ninja = True
except FileNotFoundError:
pass
cmake_args: List[str] = ["cmake"]
if watcom:
cmake_args.extend(["-G", "Watcom WMake"])
elif use_ninja:
cmake_args.extend(["-G", "Ninja"])
cmake_args.append("..")
if not watcom and bitness == 32:
if platform.system() == "Windows":
cmake_args.extend(["-A", "Win32"])
else:
cmake_args.extend([
"-DCMAKE_CXX_FLAGS=-m32",
"-DCMAKE_C_FLAGS=-m32"
])
if not os.path.isdir(runner_build_dir):
os.makedirs(runner_build_dir, exist_ok=True)
subprocess.run(cmake_args, cwd=runner_build_dir, check=True)
subprocess.run(["cmake", "--build", "."], cwd=runner_build_dir, check=True)
return barebones_runner_exe, runner_exe
def main() -> int:
parser = argparse.ArgumentParser(description="Run uACPI tests")
parser.add_argument("--asl-compiler",
help="Compiler to use to build test cases",
default="iasl")
parser.add_argument("--acpi-extractor",
help="ACPI extractor utility to use for ACPI dumps",
default="acpixtract")
parser.add_argument("--test-dir",
default=test_relpath("test-cases"),
help="The directory to run tests from, defaults to "
"'test-cases' in the same directory")
parser.add_argument("--test-runner",
help="The test runner binary to invoke")
parser.add_argument("--barebones-test-runner",
help="The barebones test runner binary to invoke")
parser.add_argument("--binary-directory",
default=test_relpath("bin"),
help="The directory to store intermediate files in, "
"created & deleted automatically. Defaults to "
"'bin' in the same directory")
parser.add_argument("--bitness", default=64, choices=[32, 64], type=int,
help="uACPI build bitness")
parser.add_argument("--large", action="store_true",
help="Run the large test suite as well")
parser.add_argument("--barebones", action="store_true",
help="Run the barebones test suite as well")
parser.add_argument("--parallelism", type=int,
default=os.cpu_count() or 1,
help="Number of test runners to run in parallel")
parser.add_argument("--watcom", action="store_true",
help="Use OpenWatcom to build test runners")
args = parser.parse_args()
if args.watcom:
args.bitness = 32
test_compiler = args.asl_compiler
test_dir = args.test_dir
test_runner = args.test_runner
bare_test_runner = args.barebones_test_runner
if test_runner is None or (args.barebones and bare_test_runner is None):
bare_runner_default, runner_default = build_test_runner(args.bitness,
args.watcom)
if bare_test_runner is None:
bare_test_runner = bare_runner_default
if test_runner is None:
test_runner = runner_default
ret = run_resource_tests(test_runner)
if ret != 0:
sys.exit(ret)
bin_dir = args.binary_directory
os.makedirs(bin_dir, exist_ok=True)
test_cases = [
os.path.join(test_dir, f)
for f in os.listdir(test_dir)
if os.path.splitext(f)[1] == ".asl"
]
test_cases.extend(generate_test_cases(test_compiler, bin_dir))
base_test_cases = compile_test_cases(
test_cases, test_compiler, bin_dir
)
with TestHeaderFooter("AML Tests"):
ret = run_tests(base_test_cases, test_runner, args.parallelism)
if ret and args.large:
large_test_cases = generate_large_test_cases(
args.acpi_extractor, bin_dir
)
with TestHeaderFooter("Large AML Tests"):
ret = run_tests(large_test_cases, test_runner, args.parallelism)
if ret and args.barebones:
bare_cases: List[TestCase] = [
BarebonesTestCase("basic-operation"),
BarebonesTestCase("table-installation"),
]
with TestHeaderFooter("Barebones Mode Tests"):
ret = run_tests(bare_cases, bare_test_runner, args.parallelism)
sys.exit(not ret)
if __name__ == "__main__":
main()

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

View File

@ -0,0 +1,415 @@
// Name: Support for various address spaces works
// Expect: str => check-address-spaces-work
DefinitionBlock ("x.aml", "SSDT", 1, "uTEST", "ASPTESTS", 0xF0F0F0F0)
{
Method (MAIN) {
// Skip for non-uacpi test runners
Return ("check-address-spaces-work")
}
Method (DOIP, 1, Serialized) {
If (Arg0 == 0) {
Local0 = "DEADBEE0"
Local1 = 0xDEADBEE0
} Else {
Local0 = "DEADBEEF"
Local1 = 0xDEADBEEF
}
OperationRegion (IPMR, IPMI, 0xDEADBEE0, 10)
Field (IPMR, BufferAcc, NoLock, Preserve) {
CMD0, 120,
// Offset = base + 0xF
CMD1, 1234,
}
Name (REQ, Buffer (32) { })
Name (RET, 0)
REQ = Concatenate("IPMICommand", Local0)
If (Arg0 == 0) {
Local0 = CMD0 = REQ
} Else {
Local0 = CMD1 = REQ
}
If (SizeOf(Local0) != 66) {
Printf("Unexpected IPMI response size %o", SizeOf(Local0))
Return (Zero)
}
RET = Local0
If (RET != Local1) {
Printf("Unexpected IMPI response %o, expected %o", RET, Local1)
Return (Zero)
}
Return (Ones)
}
Device (GPO0)
{
Name (_HID, "INT33FC" /* Intel Baytrail GPIO Controller */)
Name (_DDN, "ValleyView General Purpose Input/Output (GPIO) controller")
Name (_UID, 0)
}
Device (GPO1)
{
Name (_HID, "INT33FC" /* Intel Baytrail GPIO Controller */)
Name (_DDN, "ValleyView GPNCORE controller")
Name (_UID, 1)
}
Method (DGIO, 0, Serialized) {
OperationRegion (GPOP, GeneralPurposeIo, Zero, 0x06)
Field (GPOP, ByteAcc, NoLock, Preserve)
{
Connection (
GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
"\\GPO0", 0x00, ResourceConsumer, ,
)
{ // Pin list
0x0002, 0x0003, 0x0004, 0x0005, 0x0006
}
),
CCU0, 1,
CCU1, 3,
CCU2, 1,
Connection (
GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
"\\GPO1", 0x00, ResourceConsumer, ,
)
{ // Pin list
0x005F
}
),
CCU3, 1
}
CCU0 = 1
CCU1 = 2
CCU2 = 0
Local0 = CCU0
If (Local0 != 1) {
Printf("Bad CCU0 return %o", Local0)
Return (Zero)
}
Local0 = CCU1
If (Local0 != 2) {
Printf ("Bad CCU1 return %o", Local0)
Return (Zero)
}
Local0 = CCU2
If (Local0 != 0) {
Printf ("Bad CCU2 return %o", Local0)
Return (Zero)
}
Local0 = CCU3
if (Local0 != 0) {
Printf ("Bad CCU3 value %o", Local0)
Return (Zero)
}
Return (Ones)
}
Method (DPCC, 0, Serialized) {
OperationRegion (GPOP, PCC, 0xCA, 0xFF)
Field (GPOP, DWordAcc, NoLock, Preserve)
{
H, 8,
E, 8,
L0, 8,
L1, 8,
O, 8,
Offset(12),
CMD, 32,
}
Field (GPOP, DWordAcc, NoLock, Preserve)
{
HELL, 48,
}
H = "H"
E = "E"
L0 = "L"
L1 = "L"
O = "O"
If (ToString(HELL) != "HELLO") {
Printf ("Unexpected HELL value %o", ToString(HELL))
Return (Zero)
}
// Invoke the test runner handler
CMD = 0xDEADBEEF
// We expect it to modify the CMD field as a response
If (CMD != 0xBEEFDEAD) {
Printf ("Unexpected CMD value %o", CMD)
Return (Zero)
}
Return (Ones)
}
Method (DPRM, 0, Serialized) {
OperationRegion (GPOP, PlatformRtMechanism, 0x00, 0xFF)
Field (GPOP, BufferAcc, NoLock, Preserve)
{
DEAD, 80,
}
Local0 = DEAD = "helloworld"
Printf("Got a PRM response: %o", Local0)
If (SizeOf(Local0) != 26) {
Printf ("Unexpected Local0 size %o", SizeOf(Local0))
Return (Zero)
}
If (ToString(Local0) != "goodbyeworld") {
Printf ("Unexpected Local0 value %o", ToString(Local0))
Return (Zero)
}
Return (Ones)
}
Method (DFHW, 0, Serialized) {
OperationRegion (GPOP, FFixedHW, 0xCAFEBABE, 0xFEFECACA)
Field (GPOP, BufferAcc, NoLock, Preserve)
{
X, 1,
}
Local0 = X = "someguidandstuff"
Printf("Got a FFixedHW response: %o", Local0)
If (SizeOf(Local0) != 256) {
Printf ("Unexpected Local0 size %o", SizeOf(Local0))
Return (Zero)
}
If (ToString(Local0) != "ok") {
Printf ("Unexpected Local0 value %o", ToString(Local0))
Return (Zero)
}
Return (Ones)
}
Scope (_SB) {
Device (I2C0)
{
Name (_HID, "INT34B2")
Name (_UID, 0)
}
Device (I2C1)
{
Name (_HID, "80860F41" /* Intel Baytrail I2C Host Controller */)
Name (_CID, "80860F41" /* Intel Baytrail I2C Host Controller */)
Name (_DDN, "Intel(R) I2C Controller #5 - 80860F45")
Name (_UID, 1)
}
}
Name (RES1, ResourceTemplate ()
{
I2cSerialBusV2 (0x0008, ControllerInitiated, 0x00061A80,
AddressingMode7Bit, "\\_SB.I2C0",
0x00, ResourceConsumer, , Exclusive,
)
})
Name (RES2, ResourceTemplate ()
{
I2cSerialBusV2 (0x0040, ControllerInitiated, 0x00061A80,
AddressingMode7Bit, "\\_SB.I2C1",
0x00, ResourceConsumer, , Exclusive,
)
})
Method (DGSB, 0, Serialized) {
Method (CHEK, 3) {
If (SizeOf(Arg0) != Arg1) {
Printf(
"Bad resulting buffer length %o, expected %o",
SizeOf(Arg0), Arg1
)
Return (Zero)
}
Name (INT, 0)
INT = Arg0
If (INT != Arg2) {
Printf("Unexpected response %o, expected %o", INT, Arg2)
Return (Zero)
}
Return (Ones)
}
OperationRegion (RCH1, GenericSerialBus, 0x100, 0x0100)
Field (RCH1, BufferAcc, NoLock, Preserve)
{
Connection (RES1),
Offset (0x11),
// Command == 0x111
AccessAs (BufferAcc, AttribQuick),
CMD0, 128,
// Command == 0x121
AccessAs (BufferAcc, AttribSendReceive),
CMD1, 8,
// Command == 0x122
AccessAs (BufferAcc, AttribByte),
CMD2, 16,
// Command == 0x124
AccessAs (BufferAcc, AttribWord),
CMD3, 32,
// Command == 0x128
AccessAs (BufferAcc, AttribBlock),
CMD4, 2048,
// Command == 0x228
AccessAs (BufferAcc, AttribProcessCall),
CMD5, 8,
// Command == 0x229
AccessAs (BufferAcc, AttribBlockProcessCall),
CMD6, 144,
Connection (RES2),
// Command == 0x23B
AccessAs (BufferAcc, AttribBytes(15)),
CMD7, 8,
// Command == 0x23C
AccessAs (BufferAcc, AttribRawBytes(255)),
CMD8, 8,
// Command == 0x23D
AccessAs (BufferAcc, AttribRawProcessBytes(123)),
CMD9, 8,
}
Local0 = CMD0 = 0x111
If (CHEK(Local0, 2, 0x112) != Ones) {
Return (Zero)
}
Local0 = 0x121
Local0 = CMD1 = Local0
If (CHEK(Local0, 3, 0x122) != Ones) {
Return (Zero)
}
Local0 = CMD2 = 0x122
If (CHEK(Local0, 3, 0x123) != Ones) {
Return (Zero)
}
Local0 = CMD3
If (CHEK(Local0, 4, 0x125) != Ones) {
Return (Zero)
}
Local0 = CMD4
If (CHEK(Local0, 257, 0x129) != Ones) {
Return (Zero)
}
Local0 = CMD5 = 0x228
If (CHEK(Local0, 4, 0x229) != Ones) {
Return (Zero)
}
Local0 = CMD6
If (CHEK(Local0, 257, 0x22A) != Ones) {
Return (Zero)
}
Local0 = CMD7 = 0x23B
If (CHEK(Local0, 15 + 2, 0x23C) != Ones) {
Return (Zero)
}
Local0 = CMD8
If (CHEK(Local0, 255 + 2, 0x23D) != Ones) {
Return (Zero)
}
Local0 = CMD9
If (CHEK(Local0, 255 + 2, 0x23E) != Ones) {
Return (Zero)
}
Return (Ones)
}
/*
* Arg0 -> The address space type
* Return -> Ones on succeess, Zero on failure
*/
Method (CHEK, 1, Serialized) {
Switch (Arg0) {
Case (7) { // IPMI
Local0 = DOIP(0)
If (Local0 != Ones) {
Break
}
Local0 = DOIP(1)
Break
}
Case (8) { // General Purpose IO
Local0 = DGIO()
Break
}
Case (9) { // Generic Serial Bus
Local0 = DGSB()
Break
}
Case (0x0A) { // PCC
Local0 = DPCC()
Break
}
Case (0x0B) { // PRM
Local0 = DPRM()
Break
}
Case (0x7F) { // FFixedHW
Local0 = DFHW()
Break
}
}
If (Local0 != Ones) {
Printf("Address space %o failed: expected '%o', got '%o'!",
Arg0, Ones, Local0)
Return (Zero)
}
Printf("Address space %o OK", ToHexString(Arg0))
Return (Ones)
}
}

View File

@ -0,0 +1,71 @@
// Name: Nested Package Doesn't Leak Memory
// Expect: int => 1
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method(GPKG) {
Local1 = 10
Local0 = Package (Local1) {
0x123,
0x321,
Package {
0x321,
"123",
Package {
0x321,
Package {
0x321,
"123",
Package {
0x321,
"123",
Package {
0x321,
Package {
0x321,
"123",
Package (Local1) {
0x321,
"123",
999,
},
999,
},
"123",
999,
},
999,
},
999,
},
"123",
999,
},
999,
},
"Hello world",
Package {
0x321,
"Hello",
},
Package {
0x321,
"World",
},
Package {
Buffer (Local1) { 0xFF },
0xDEADBEEF,
},
Buffer { 1, 2, 3 }
}
Return (Local0)
}
Method (MAIN) {
Local0 = GPKG()
Debug = Local0
Local0 = 1
Return (Local0)
}
}

View File

@ -0,0 +1,127 @@
// Name: Concatenate Resources
// Expect: int => 1
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Name (BUF0, ResourceTemplate ()
{
WordBusNumber (ResourceProducer, MinFixed, MaxFixed, PosDecode,
0x0000,
0x0000,
0x00FF,
0x0000,
0x0100,
,, _Y00)
DWordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange,
0x00000000,
0x00000000,
0x00000CF7,
0x00000000,
0x00000CF8,
1, "\\SOME.PATH",, TypeStatic, DenseTranslation)
IO (Decode16,
0x0CF8,
0x0CF8,
0x01,
0x08,
)
DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite,
0x00000000,
0x000A0000,
0x000BFFFF,
0x00000000,
0x00020000,
123, "^^^^^^^^^ANOT.ER.PATH", , AddressRangeMemory, TypeStatic)
})
Name (IIC0, ResourceTemplate ()
{
I2cSerialBusV2 (0x0000, ControllerInitiated, 0x00061A80,
AddressingMode7Bit, "\\_SB.PCI0.I2C0",
0x00, ResourceConsumer, _Y10, Exclusive,
)
})
Name (RBUF, ResourceTemplate ()
{
I2cSerialBusV2 (0x0029, ControllerInitiated, 0x00061A80,
AddressingMode7Bit, "\\_SB.PCI0.I2C0",
0x00, ResourceConsumer, , Exclusive,
)
GpioInt (Level, ActiveHigh, Exclusive, PullNone, 0x0000,
"\\_SB.PCI0.GPI0", 0x00, ResourceConsumer, ,
)
{
0x012A
}
GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
"\\_SB.PCI0.GPI0", 0x00, ResourceConsumer, ,
)
{
0x002F
}
GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
"\\_SB.PCI0.GPI0", 0x00, ResourceConsumer, ,
)
{
0x0124
}
})
// src0, src1, dst
Method (CHEK, 3)
{
Local0 = (SizeOf(Arg0) + SizeOf(Arg1)) - 2
If (Local0 != SizeOf(Arg2)) {
Printf("Invalid final buffer size: %o, expected %o",
Local0, SizeOf(Arg2))
Return (0)
}
Local0 = 0
Local1 = 0
While (Local0 < (SizeOf(Arg0) - 2)) {
Local2 = DerefOf(Arg0[Local0])
Local3 = DerefOf(Arg2[Local1])
If (Local2 != Local3) {
Printf("Byte src=%o (dst=%o) mismatch, expected %o got %o",
Local0, Local1, ToHexString(Local2), ToHexString(Local3))
Return (0)
}
Local0 += 1
Local1 += 1
}
Local0 = 0
While (Local0 < SizeOf(Arg1)) {
Local2 = DerefOf(Arg1[Local0])
Local3 = DerefOf(Arg2[Local1])
If (Local2 != Local3) {
Printf("Byte src=%o (dst=%o) mismatch, expected %o got %o",
Local0, Local1, ToHexString(Local2), ToHexString(Local3))
Return (0)
}
Local0 += 1
Local1 += 1
}
Return (1)
}
Method (MAIN, 0, NotSerialized)
{
Local0 = ConcatenateResTemplate(BUF0, IIC0)
If (CHEK(BUF0, IIC0, Local0) != 1) {
Return (0)
}
Local1 = ConcatenateResTemplate(Local0, RBUF)
Return(CHEK(Local0, RBUF, Local1))
}
}

View File

@ -0,0 +1,34 @@
// Name: Copy a local method and execute it
// Expect: int => 3735928559
DefinitionBlock ("x.aml", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Name (TEST, "Hello world!")
Method (GETX) {
Return (1)
}
Method (COPY) {
Method (GETX, 1, Serialized) {
Name (Y, 0xDEAD0000)
Y += Arg0
Return (Y)
}
Return (RefOf(GETX))
}
Method (COP1) {
Local0 = COPY()
Return (DerefOf(Local0))
}
Method (MAIN, 0, NotSerialized)
{
Local0 = COP1()
CopyObject(Local0, TEST)
Return (TEST(0xBEEF))
}
}

View File

@ -0,0 +1,70 @@
// Name: CopyObject with Operation Region works
// Expect: int => 0
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (REPL, 1) {
Arg0 = 123
}
Method (CAS0) {
OperationRegion(MYRE, SystemMemory, 0, 128)
Field (MYRE, AnyAcc, NoLock) {
FILD, 32
}
FILD = 1
CopyObject(123, MYRE)
}
CAS0()
Method (CAS1) {
OperationRegion(MYRE, SystemMemory, 0, 128)
Field (MYRE, AnyAcc, NoLock) {
FILD, 32
}
FILD = 1
REPL(RefOf(MYRE))
}
CAS1()
Method (CAS2) {
OperationRegion(MYRE, SystemMemory, 0, 128)
Field (MYRE, AnyAcc, NoLock) {
FILD, 32
}
FILD = 1
Name (FAKE, 123)
CopyObject(MYRE, FAKE)
Field (FAKE, AnyAcc, NoLock) {
FAKF, 32
}
REPL(RefOf(MYRE))
FAKF = 1
}
CAS2()
Method (CAS3) {
OperationRegion(MYR0, SystemMemory, 0, 128)
OperationRegion(MYR1, SystemMemory, 0, 128)
CopyObject(123, MYR1)
}
CAS3()
Method (CAS4) {
OperationRegion(MYRE, SystemMemory, 0, 128)
Field (MYRE, AnyAcc, NoLock) {
FILD, 32
}
FILD = 1
CopyObject(FILD, MYRE)
FILD = 1
}
CAS4()
Name (MAIN, 0)
}

View File

@ -0,0 +1,27 @@
// Name: CopyObject on yourself works
// Expect: str => Hello World
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (BAR, 1, Serialized) {
Debug = "Enter BAR"
CopyObject (Arg0, BAR)
Debug = "Leave BAR"
}
Method (FOO) {
Debug = "Enter FOO"
CopyObject("Hello", FOO)
BAR(" World")
Debug = "Leave FOO"
Return (0x123)
}
Method (MAIN) {
Local0 = FOO()
Printf("First invocation of FOO returned %o", Local0)
Return (Concatenate(FOO, BAR))
}
}

View File

@ -0,0 +1,31 @@
// Name: CopyObject to predefined works
// Expect: str => HelloWorld
DefinitionBlock ("x.aml", "SSDT", 1, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (HELO) {
Return ("Hello")
}
Method (WRLD) {
Return ("World")
}
Method (MAIN) {
OperationRegion(NVSM, SystemMemory, 0x100000, 128)
Field (NVSM, ByteAcc, Lock, WriteAsZeros) {
FILD, 8,
}
FILD = 0xFF
CopyObject(HELO, \)
CopyObject(WRLD, _GL)
If (FILD != 0xFF) {
Return ("Locked field read-back failed")
}
Return (Concatenate(\(), _GL()))
}
}

View File

@ -0,0 +1,84 @@
// Name: Duplicate named objects are skipped correctly
// Expect: int => 11
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Name (MAIN, 0)
Name (TEST, "Hello World")
Device (FOO) {
ThermalZone (BAR) {
Name (TEST, "Hello World")
}
}
// These all attempt to create duplicate objects
Name (FOO.BAR.TEST, "duplicated test")
MAIN += 1
Method (TEST, 0, Serialized) {
Debug = "Why is this executed?"
Name (TEST, 123)
CopyObject("Method didn't get skipped", MAIN)
Return (333)
}
Debug = "Ok, still here"
MAIN += 1
ThermalZone (TEST) {
Local0 = 123
Debug = Local0
CopyObject("???", MAIN)
}
ThermalZone (TEST) { }
MAIN += 1
Processor (FOO.BAR.TEST, 0x02, 0x00000410, 0x06) { }
MAIN += 1
Processor (\TEST, 0x01, 0x00000410, 0x06) {
Local2 = Package { 1, 2, 3 }
Debug = Local2
}
Device (\FOO.BAR.TEST)
{
Name (_HID, EisaId ("PNP0C0D"))
}
MAIN += 1
Device (\TEST) { }
// Alias to an object that doesn't exist, but new name is valid
Alias(ZOO, BAR)
Alias(PATH.THAT.DOES.NOT.EXIS.T, \BAZ)
MAIN += 1
// Alias to an object that does exist, but new name alrady exists
Alias(\TEST, \MAIN)
// Alias to a non existant object and name also already exists
Alias(ZOO, \TEST)
Alias(PATH.THAT.DOES.NOT.EXIS.T, \FOO.BAR.TEST)
MAIN += 1
Mutex(TEST, 15)
Debug = "Just a bit left"
Event(TEST)
MAIN += 1
OperationRegion(TEST, SystemMemory, 0x100000, 128)
DataTableRegion(FOO.BAR.TEST, "DSDT", "", "")
Local0 = Buffer (256) { }
CreateBitField(Local0, 111, TEST)
CreateByteField(Local0, 111, TEST)
MAIN += 1
CreateDWordField(Local0, 111, TEST)
CreateQWordField(Local0, 111, TEST)
MAIN += 1
CreateField(Local0, 111, 11, FOO.BAR.TEST)
MAIN += 1
Debug = "Made it to the end!"
}

View File

@ -0,0 +1,21 @@
// Name: Empty objects behave correctly
// Expect: int => 0
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (EMPT) { }
Method (MAIN) {
Local0 = EMPT()
Debug = Local0
Local0 = Package (0) { }
Debug = Local0
Local0 = 0
Local1 = Package(Local0) { }
Debug = Local1
Return (0)
}
}

View File

@ -0,0 +1,7 @@
// Name: Eval supports plain objects
// Expect: str => This is a plain string, not a method
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Name (MAIN, "This is a plain string, not a method")
}

View File

@ -0,0 +1,31 @@
// Name: Event signal & wait
// Expect: int => 0
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (MAIN, 0, Serialized)
{
Event (EVET)
Local0 = 5
While (Local0--) {
Signal(EVET)
}
Local0 = 5
While (Local0--) {
Local1 = Wait(EVET, 0xFFFD + Local0)
If (Local1 != Zero) {
Return (Local1)
}
}
// This should fail
Local1 = Wait(EVET, Zero)
If (Local1 == Zero) {
Return (One)
}
Return (Zero)
}
}

View File

@ -0,0 +1,48 @@
// Name: Global lock works
// Expect: int => 0
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (CHEK, 1, Serialized, 15)
{
If (Arg0 != 0) {
Debug = "Failed to acquire the global lock!"
Return (1)
}
Return (0)
}
Method (MAIN, 0, Serialized)
{
Local0 = 0
Debug = "Acquiring the lock manually!"
Local0 += CHEK(Acquire (_GL, 0xFFFF))
Local0 += CHEK(Acquire (_GL, 0xFFFF))
Local0 += CHEK(Acquire (_GL, 0xFFFF))
Local0 += CHEK(Acquire (_GL, 0xFFFF))
Debug = "Doing a field write..."
OperationRegion(NVSM, SystemMemory, 0x100000, 128)
Field (NVSM, AnyAcc, Lock, WriteAsZeros) {
FILD, 1,
}
FILD = 1
Debug = "Write done, we should still be holding the lock!"
Release(_GL)
Release(_GL)
Release(_GL)
Debug = "Should release NOW!"
Release(_GL)
// TODO? Would be nice to have some way to actually verify that a lock is held...
Return (Local0)
}
}

View File

@ -0,0 +1,24 @@
// Name: Infinite While loops eventually ends
// Expect: int => 1
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Name (LOOP, 0)
Method (HANG) {
LOOP = 1
While (LOOP++) { }
}
HANG()
Method (MAIN) {
Printf("Looped %o times before getting aborted", ToDecimalString(LOOP))
If (LOOP < 100) {
Return (0)
}
Return (1)
}
}

View File

@ -0,0 +1,60 @@
// Name: Increment And Decrement Fields & Indices
// Expect: int => 150
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (BROK, 2) {
Printf ("%o increment is broken", Arg0)
Return (Arg1)
}
Method (MAIN, 0, Serialized)
{
Local0 = Buffer { 1 }
Local1 = Package {
123, 22
}
Debug = Increment(Local0[0])
Debug = Decrement(Local1[0])
Local2 = Local1[1]
Debug = Increment(Local2)
OperationRegion(NVSM, SystemMemory, 0x100000, 128)
Field (NVSM, AnyAcc, NoLock, WriteAsZeros) {
FILD, 1,
}
FILD = 0
Debug = Increment(FILD)
// We get a 2 here but write back 0 because field is one bit
Local3 = Increment(FILD)
If (FILD != 0) {
Return (BROK("Field unit", 0xDEADBEEF))
}
Local4 = Increment(FILD)
If (FILD != 1) {
Return (BROK("Field unit", 0xBEEFDEAD))
}
If (DerefOf(Local2) != DerefOf(Local1[1])) {
Return (BROK("Buffer index", 0xCAFEBABE))
}
Return (
// 2
DerefOf(Local0[0]) +
// 122
DerefOf(Local1[0]) +
// 23
DerefOf(Local2) +
// 2
Local3 +
// 1
Local4
)
}
}

View File

@ -0,0 +1,39 @@
// Name: Buffer Indices
// Expect: str => HfVXoWorld
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (FAIL, 2)
{
Printf("Invalid string %o, expected %o", Arg0, Arg1)
Return(1)
}
Method (MAIN, 0, NotSerialized)
{
Local0 = "HelloWorld"
Local0[3] = "X"
Local1 = "HelXoWorld"
If (Local0 != Local1) {
Return(FAIL(Local0, Local1))
}
Local2 = RefOf(Index(Local0, 2))
Local2 = "V"
Local1 = "HeVXoWorld"
If (Local0 != Local1) {
Return(FAIL(Local0, Local1))
}
CopyObject(Index(Local0, 1), Local2)
Local0[1] = 0x66
If (DerefOf(Local2) != 0x66) {
Return(1)
}
Return (Local0)
}
}

View File

@ -0,0 +1,52 @@
// Name: Dump Package Contents
// Expect: str => { HelloWorld, 123, deadbeef, { some string, { ffffffffeeeeeeee, middle package }, cafebabe } }
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (DUMP, 1)
{
Local0 = 0
Local1 = "{ "
While (Local0 < SizeOf(Arg0)) {
// If package, invoke DUMP recursively
If (ObjectType(DerefOf(Arg0[Local0])) == 4) {
Fprintf(Local1, "%o, %o", Local1, DUMP(DerefOf(Arg0[Local0])))
Local0 += 1
Continue;
}
If (Local0 == 0) {
Local3 = ""
} Else {
Local3 = ", "
}
Fprintf(Local1, "%o%o%o", Local1, Local3, DerefOf(Arg0[Local0]))
Local0 += 1
}
Fprintf(Local1, "%o }", Local1)
Return(Local1)
}
Method (MAIN, 0, NotSerialized)
{
Local0 = Package {
"HelloWorld",
0x123,
0xDEADBEEF,
Package {
"some string",
Package {
0xFFFFFFFFEEEEEEEE,
"middle package",
},
0xCAFEBABE,
},
}
Local1 = DUMP(Local0)
Return(Local1)
}
}

View File

@ -0,0 +1,50 @@
// Name: Package Indices w/ References
// Expect: int => 1062815831
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (TEST, 1)
{
Local0 = Package {
1, 2, 3, "String"
}
Local1 = 0
Arg0[1] = RefOf(Local0)
Arg0[2] = RefOf(Local1)
}
Method (MAIN, 0, NotSerialized)
{
Local0 = Package {
"HelloWorld",
0x123,
0xDEADBEEF,
Package {
"some string",
0xCAFEBABE
}
}
TEST(Local0)
Local1 = DerefOf(DerefOf(Local0[1])[3])
If (Local1 != "String") {
Printf("Invalid value at nested package %o", Local1)
Return (1)
}
Local0[2] = "WHY?"
Local2 = DerefOf(Local0[2])
// Why in little-endian ascii
Local3 = 1062815831
If (Local2 != Local3) {
Printf("Failed to implicit cast, expected %o, got %o", Local3, Local2)
Return (1)
}
Return (Local2)
}
}

View File

@ -0,0 +1,22 @@
// Name: Infinite recursion eventually ends
// Expect: int => 0
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Name (MAIN, 0xDEADBEEF)
Name (ITER, 0)
Method (HANG) {
ITER++
HANG()
}
HANG()
Printf("Recursed %o times before stopping", ToDecimalString(ITER))
If (ITER > 64) {
MAIN = 0
} Else {
Debug = "Recursion depth was too small"
}
}

View File

@ -0,0 +1,12 @@
// Name: Return Integer Using Local0
// Expect: int => 0x123
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (MAIN, 0, NotSerialized)
{
Local0 = 0x123
Debug = Local0
Return (Local0)
}
}

View File

@ -0,0 +1,11 @@
// Name: Return String Using Local0
// Expect: str => hello world
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (MAIN, 0, NotSerialized)
{
Debug = (Local0 = "hello world")
Return (Local0)
}
}

View File

@ -0,0 +1,61 @@
// Name: Call methods with every ArgX
// Expect: int => 8
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method(TES7, 7) {
Local0 = Arg0 + Arg1 + Arg2 + Arg3 + Arg4 + Arg5 + Arg6
If (Local0 != (1 + 2 + 3 + 4 + 5 + 6 + 7)) { Return (Local0) }
Return (1)
}
Method(TES6, 6) {
Local0 = Arg0 + Arg1 + Arg2 + Arg3 + Arg4 + Arg5
If (Local0 != (1 + 2 + 3 + 4 + 5 + 6)) { Return (Local0) }
Return (1)
}
Method(TES5, 5) {
Local0 = Arg0 + Arg1 + Arg2 + Arg3 + Arg4
If (Local0 != (1 + 2 + 3 + 4 + 5)) { Return (Local0) }
Return (1)
}
Method(TES4, 4) {
Local0 = Arg0 + Arg1 + Arg2 + Arg3
If (Local0 != (1 + 2 + 3 + 4)) { Return (Local0) }
Return (1)
}
Method(TES3, 3) {
Local0 = Arg0 + Arg1 + Arg2
If (Local0 != (1 + 2 + 3)) { Return (Local0) }
Return (1)
}
Method(TES2, 2) {
Local0 = Arg0 + Arg1
If (Local0 != (1 + 2)) { Return (Local0) }
Return (1)
}
Method(TES1, 1) {
Local0 = Arg0
If (Local0 != (1 + 2)) { Return (Local0) }
Return (1)
}
Method(TES0, 0) { Return (1) }
Method (MAIN)
{
Return(TES7(1, 2, 3, 4, 5, 6, 7) +
TES6(1, 2, 3, 4, 5, 6) +
TES5(1, 2, 3, 4, 5) +
TES4(1, 2, 3, 4) +
TES3(1, 2, 3) +
TES2(1, 2) +
TES1(1) +
TES0())
}
}

View File

@ -0,0 +1,34 @@
// Name: Multilevel reference w/ Increment
// Expect: int => 0xDEAD0005
DefinitionBlock ("", "DSDT", 1, "uTTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (MAIN, 0, NotSerialized)
{
Local0 = 0xDEAD0000
Local1 = RefOf(Local0)
Debug = Local1
Local2 = RefOf(Local1)
Debug = Local2
Local3 = RefOf(Local2)
Debug = Local3
Local4 = RefOf(Local3)
Debug = Local4
Local5 = RefOf(Local4)
Debug = Local5
Debug = Increment(Local1)
Debug = Increment(Local2)
Debug = Increment(Local3)
Debug = Increment(Local4)
Debug = Increment(Local5)
Debug = Local0
Return (DerefOf(Local5))
}
}

View File

@ -0,0 +1,55 @@
// Name: Automatic mutex release
// Expect: int => 0
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (CHEK, 1, Serialized, 15)
{
If (Arg0 != 0) {
Debug = "Failed to acquire mutex!"
Return (1)
}
Return (0)
}
Method (MAIN, 0, Serialized)
{
Mutex(MUT0, 0)
Mutex(MUT1, 1)
Mutex(MUT2, 2)
Mutex(MUT3, 3)
Mutex(MUT4, 4)
Mutex(MUT5, 5)
Mutex(MUT6, 6)
Mutex(MUT7, 7)
Mutex(MUT8, 8)
Mutex(MUT9, 9)
Mutex(MUTA, 10)
Mutex(MUTB, 11)
Mutex(MUTC, 12)
Mutex(MUTD, 13)
Mutex(MUTE, 14)
Mutex(MUTF, 15)
Local0 = 0
Local0 += CHEK(Acquire(MUT0, 0))
Local0 += CHEK(Acquire(MUT1, 0))
Local0 += CHEK(Acquire(MUT2, 0))
Local0 += CHEK(Acquire(MUT3, 0))
Local0 += CHEK(Acquire(MUT4, 0))
Local0 += CHEK(Acquire(MUT5, 0))
Local0 += CHEK(Acquire(MUT6, 0))
Local0 += CHEK(Acquire(MUT7, 0))
Local0 += CHEK(Acquire(MUT8, 0))
Local0 += CHEK(Acquire(MUT9, 0))
Local0 += CHEK(Acquire(MUTA, 0))
Local0 += CHEK(Acquire(MUTB, 0))
Local0 += CHEK(Acquire(MUTC, 0))
Local0 += CHEK(Acquire(MUTD, 0xFFFE))
Local0 += CHEK(Acquire(MUTE, 0xFFFD))
Local0 += CHEK(Acquire(MUTF, 0xFFFF))
Return (Local0)
}
}

View File

@ -0,0 +1,64 @@
// Name: Out of order mutex release
// Expect: int => 0
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (CHEK, 1, Serialized, 15)
{
If (Arg0 != 0) {
Debug = "Failed to acquire mutex!"
Return (1)
}
Return (0)
}
Method (MAIN, 0, Serialized)
{
Mutex(MUT0)
Mutex(MUT1)
Mutex(MUT2)
Mutex(MUT3)
Mutex(MUT4)
Mutex(MUT5)
Mutex(MUT6)
Mutex(MUT7)
Mutex(MUT8)
Mutex(MUT9)
Mutex(MUTA)
Mutex(MUTB)
Mutex(MUTC)
Mutex(MUTD)
Mutex(MUTE)
Mutex(MUTF)
Local0 = 0
Local0 += CHEK(Acquire(MUT0, 0))
Local0 += CHEK(Acquire(MUT1, 0))
Local0 += CHEK(Acquire(MUT2, 0))
Local0 += CHEK(Acquire(MUT3, 0))
Local0 += CHEK(Acquire(MUT4, 0))
Local0 += CHEK(Acquire(MUT5, 0))
Local0 += CHEK(Acquire(MUT6, 0))
Local0 += CHEK(Acquire(MUT7, 0))
Local0 += CHEK(Acquire(MUT8, 0))
Local0 += CHEK(Acquire(MUT9, 0xF))
Local0 += CHEK(Acquire(MUTA, 0))
Local0 += CHEK(Acquire(MUTB, 3))
Local0 += CHEK(Acquire(MUTC, 123))
Local0 += CHEK(Acquire(MUTD, 0xFFFE))
Local0 += CHEK(Acquire(MUTE, 0xFFFD))
Local0 += CHEK(Acquire(MUTF, 0xFFFF))
Release(MUTA)
Release(MUT9)
Release(MUTC)
Release(MUT1)
Release(MUT7)
Release(MUTE)
Release(MUTD)
// The rest are released automatically when we exit the outermost method
Return (Local0)
}
}

View File

@ -0,0 +1,37 @@
// Name: Recursive mutex
// Expect: int => 253
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (CHEK, 1, Serialized, 15)
{
If (Arg0 != 0) {
Debug = "Failed to acquire mutex!"
Return (1)
}
Return (0)
}
Mutex (MUTX)
Method (ACQ, 1, Serialized) {
CHEK(Acquire(MUTX, 0xFFFF))
Local0 = 0
If (Arg0 < 22) {
Local0 += Arg0 + ACQ(Arg0 + 1)
} Else {
Local0 += Arg0
}
Release(MUTX)
Return (Local0)
}
Method (MAIN, 0, Serialized)
{
Return (ACQ(0))
}
}

View File

@ -0,0 +1,25 @@
// Name: Notifications & Requests
// Expect: int => 0
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (FATL, 1)
{
Fatal(0xFF, 0xDEADBEEF, ToInteger(Arg0))
}
Device (PSP) {
Name (_HID, "ACPI0000")
}
Method (MAIN, 0, NotSerialized)
{
Breakpoint
FATL("0xCAFEBABEC0DEDEAD")
Breakpoint
Notify(PSP, 0x10)
Return (0)
}
}

View File

@ -0,0 +1,80 @@
// Name: Public API for object mutation works
// Expect: str => check-object-api-works
DefinitionBlock ("x.aml", "SSDT", 1, "uTEST", "APITESTS", 0xF0F0F0F0)
{
Method (MAIN) {
// Skip for non-uacpi test runners
Return ("check-object-api-works")
}
/*
* Arg0 -> Expected case
* Arg1 -> The actual value
* Return -> Ones on succeess, Zero on failure
*/
Method (CHEK, 2) {
Switch (Arg0) {
Case (1) {
Local0 = 0xDEADBEEF
Break
}
Case (2) {
Local0 = "Hello World"
Break
}
Case (3) {
Local0 = "TEST"
/*
* Arg1 is expected to be a reference to a string XXXX, store
* into it here to invoke implicit case semantics.
*/
Arg1 = Local0
Break
}
Case (4) {
Local0 = Buffer { 0xDE, 0xAD, 0xBE, 0xEF }
Break
}
Case (5) {
If (ObjectType(Arg1) != 4) {
Printf("Expected a Package, got %o", Arg1)
Return (Zero)
}
If (SizeOf(Arg1) != 3) {
Printf("Expected a Package of 3 elements, got %o", SizeOf(Arg1))
Return (Zero)
}
Local0 = Package {
"First Element",
2,
Buffer { 1, 2, 3 },
}
Local1 = 0
While (Local1 < 3) {
If (DerefOf(Local0[Local1]) != DerefOf(Arg1[Local1])) {
Printf("Expected %o, got %o!", DerefOf(Local0[Local1]), DerefOf(Arg1[Local1]))
Return (Zero)
}
Printf("Object %o OK", Local1)
Local1++
}
Return (Ones)
}
}
If (Local0 != Arg1) {
Printf("Expected '%o', got '%o'!", Local0, Arg1)
Return (Zero)
}
Printf("Comparison %o OK", Arg0)
Return (Ones)
}
}

View File

@ -0,0 +1,81 @@
// Name: _OSI works correctly
// Expect: int => 0
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (CHEK, 2) {
Local0 = _OSI(Arg0)
If (Local0 != Arg1) {
Printf("_OSI(%o) failed (expected %o, got %o)", Arg0, Arg1, Local0)
Return (1)
}
Return (0)
}
Method (MAIN, 0, NotSerialized)
{
Local0 = 0
If (!CondRefOf(_OSI)) {
Debug = "No _OSI method!"
Return (1111111)
}
Local0 += CHEK("Windows 2000", Ones)
Local0 += CHEK("Windows 2001", Ones)
Local0 += CHEK("Windows 2001 SP1", Ones)
Local0 += CHEK("Windows 2001.1", Ones)
Local0 += CHEK("Windows 2001 SP2", Ones)
Local0 += CHEK("Windows 2001.1 SP1", Ones)
Local0 += CHEK("Windows 2006.1", Ones)
Local0 += CHEK("Windows 2006 SP1", Ones)
Local0 += CHEK("Windows 2006 SP2", Ones)
Local0 += CHEK("Windows 2009", Ones)
Local0 += CHEK("Windows 2012", Ones)
Local0 += CHEK("Windows 2013", Ones)
Local0 += CHEK("Windows 2015", Ones)
Local0 += CHEK("Windows 2016", Ones)
Local0 += CHEK("Windows 2017", Ones)
Local0 += CHEK("Windows 2017.2", Ones)
Local0 += CHEK("Windows 2018", Ones)
Local0 += CHEK("Windows 2018.2", Ones)
Local0 += CHEK("Windows 2019", Ones)
// ACPICA acpiexec
If (_OSI("AnotherTestString")) {
// do nothing
Debug = "ACPICA acpiexec detected"
} ElseIf (_OSI("TestRunner")) {
Debug = "uACPI test runner detected"
// These are only enabled in uACPI test runner
Local0 += CHEK("3.0 Thermal Model", Ones)
Local0 += CHEK("Module Device", Ones)
// Don't check these in ACPICA, it might be too old to have these
Local0 += CHEK("Windows 2020", Ones)
Local0 += CHEK("Windows 2021", Ones)
Local0 += CHEK("Windows 2022", Ones)
} Else {
Debug = "Neither uACPI nor ACPICA were detected, aborting test"
Return (123321)
}
// This is removed in both uACPI and ACPICA test runners
Local0 += CHEK("Windows 2006", Zero)
Local0 += CHEK("Extended Address Space Descriptor", Ones)
Local0 += CHEK("Processor Aggregator Device", Zero)
Local0 += CHEK("3.0 _SCP Extensions", Zero)
Local0 += CHEK("Processor Device", Zero)
Local0 += CHEK("", Zero)
Local0 += CHEK("Windows 99999", Zero)
Local0 += CHEK("Windows 2014", Zero)
Local0 += CHEK("Linux", Zero)
Return (Local0)
}
}

View File

@ -0,0 +1,40 @@
// Name: IO from deleted fields doesn't crash
// Expect: int => 0
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
External(MAIN)
If (!CondRefOf(MAIN)) {
Name (MAIN, Ones)
/*
* Load ourselves again, we won't enter this branch a second time.
* We expect this LoadTable call to fail because of the invalid
* field store.
*/
MAIN = LoadTable("DSDT", "uTEST", "TESTTABL", "", "", 0)
Return (0)
}
Method (TEST) {
OperationRegion(MYRE, SystemMemory, 0, 128)
Field (MYRE, AnyAcc, NoLock) {
FILD, 32
}
FILD = 1
Return (RefOf(FILD))
}
Name (X, "")
/*
* Get a dangling field object and make X be this field.
* Then attempt to perform a read from it.
*/
Local0 = TEST()
CopyObject(DerefOf(Local0), X)
Debug = X
}

View File

@ -0,0 +1,14 @@
// Name: Modify Local0 Using RefOf Local1
// Expect: int => 0x124
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (MAIN, 0, NotSerialized)
{
Local0 = RefOf(Local1)
Local0 = 0x123
Debug = Local0
Debug = Local1
Return(Local1++)
}
}

View File

@ -0,0 +1,19 @@
// Name: Modify Local0 Using Arg0
// Expect: int => 124
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (TEST, 1, NotSerialized)
{
Local0 = RefOf(Arg0)
Local0 = 123
Arg0++
}
Method (MAIN, 0, NotSerialized)
{
Local0 = 200
TEST(RefOf(Local0))
Return (Local0)
}
}

View File

@ -0,0 +1,17 @@
// Name: Call-by-value w/ Store() doesn't modify Local
// Expect: str => MyString
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (TEST, 1, NotSerialized)
{
Arg0 = 0xDEADBEEF
}
Method (MAIN, 0, NotSerialized)
{
Local0 = "MyString"
TEST(Local0)
Return (Local0)
}
}

View File

@ -0,0 +1,17 @@
// Name: Call-by-value w/ CopyObject() doesn't modify Local
// Expect: str => MyString
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (TEST, 1, NotSerialized)
{
CopyObject(0xDEADBEEF, Arg0)
}
Method (MAIN, 0, NotSerialized)
{
Local0 = "MyString"
TEST(Local0)
Return (Local0)
}
}

View File

@ -0,0 +1,24 @@
// Name: Recursively modify ArgX
// Expect: int => 0x100A
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (TEST, 2, NotSerialized)
{
Local0 = RefOf(Arg0)
Local0++
Debug = Arg1
Debug = DerefOf(Local0)
If (Arg1--) {
TEST(RefOf(Arg0), Arg1)
}
}
Method (MAIN, 0, NotSerialized)
{
Local0 = 0x1000
TEST(RefOf(Local0), 10)
Return (Local0)
}
}

View File

@ -0,0 +1,23 @@
// Name: Call-by-value w/ Store() modifies strings through a reference
// Expect: str => WHY?
// NOTE:
// This test seems bogus but it's actually corrrect, it produces
// the same output on NT.
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (TEST, 1, NotSerialized)
{
Local0 = RefOf(Arg0)
// WHY? in little-endian ASCII
Local0 = 0x3F594857
}
Method (MAIN, 0, NotSerialized)
{
Local0 = "MyST"
TEST(Local0)
Return (Local0)
}
}

View File

@ -0,0 +1,18 @@
// Name: Call-by-value w/ Store() does NOT modify integers even through a reference
// Expect: int => 0x123
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (TEST, 1, NotSerialized)
{
Local0 = RefOf(Arg0)
Local0 = 0xDEADBEEF
}
Method (MAIN, 0, NotSerialized)
{
Local0 = 0x123
TEST(Local0)
Return (Local0)
}
}

View File

@ -0,0 +1,21 @@
// Name: LocalX reference is rebindable via CopyObject
// Expect: str => Modified
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (TEST, 1, NotSerialized)
{
Local0 = RefOf(Arg0)
Local0 = "Modified String"
CopyObject("Wrong String", Local0)
Local0 = 0xDEADBEEF
}
Method (MAIN, 0, NotSerialized)
{
Local0 = "MyString"
TEST(Local0)
Return (Local0)
}
}

View File

@ -0,0 +1,18 @@
// Name: ArgX reference is not rebindable via CopyObject
// Expect: int => 0xDEADBEEF
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (TEST, 1, NotSerialized)
{
CopyObject(0xDEADC0DE, Arg0)
CopyObject(0xDEADBEEF, Arg0)
}
Method (MAIN, 0, NotSerialized)
{
Local0 = "MyString"
TEST(RefOf(Local0))
Return (Local0)
}
}

View File

@ -0,0 +1,25 @@
// Name: ArgX non-reference is rebindable
// Expect: str => MyString
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (TES0, 1, NotSerialized)
{
CopyObject("Hello World", Arg0)
Debug = Arg0
}
Method (TES1, 1, NotSerialized)
{
Arg0 = 0x123
Debug = Arg0
}
Method (MAIN, 0, NotSerialized)
{
Local0 = "MyString"
TES0(Local0)
TES1(Local0)
Return(Local0)
}
}

View File

@ -0,0 +1,18 @@
// Name: ArgX reference is not rebindable via Store
// Expect: int => 0xDEADBEEF
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (TEST, 1, NotSerialized)
{
Store(0xDEADC0DE, Arg0)
Store(0xDEADBEEF, Arg0)
}
Method (MAIN, 0, NotSerialized)
{
Local0 = "MyString"
TEST(RefOf(Local0))
Return (Local0)
}
}

View File

@ -0,0 +1,18 @@
// Name: Modification via LocalX reference implict-casts (string->int64)
// Expect: int => 0x676E6F6C79726576
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (TEST, 1, NotSerialized)
{
Local0 = RefOf(Arg0)
Local0 = "verylongstringbiggerthanint"
}
Method (MAIN, 0, NotSerialized)
{
Local0 = 0xDEADC0DEDEADBEEF
TEST(RefOf(Local0))
Return (Local0)
}
}

View File

@ -0,0 +1,147 @@
// Name: _REG gets called correctly
// Expect: int => 0
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Name (RET, 0)
Device (PCI0) {
Name (_HID, "PNP0000")
Method (_CID) {
Local0 = Package {
"idk",
0x000A2E4F,
"badid",
0x02002E4F,
0x030FD041,
0x130FD041,
0x120FD041,
// PCI Express root bridge
"PNP0A08"
}
Return (Local0)
}
Name (_SEG, 0x1)
Name (_BBN, 0x10)
Device (HPET) {
Name (_HID, "PNP0103")
OperationRegion(LOLX, SystemMemory, 0xDEADBEEF, 123)
Method (_REG, 2) {
Debug = "HPET._REG shouldn't have been called"
RET += 1
}
}
Name (STAT, 0)
Device (UHCI) {
Name (_ADR, 0x0001000F)
Method (TEST, 1, Serialized) {
Method (_REG, 2, Serialized) {
Printf("PCIR _REG(%o, %o) called", Arg0, Arg1)
If (Arg0 != 2) {
Printf("Invalid space value %o", Arg0)
RET += 1
}
Switch (Arg1) {
Case (0) {
If (STAT != 1) {
Debug = "Trying to _REG(disconnect) before _REG(connect)"
RET += 1
Break
}
STAT += 1
Break
}
Case (1) {
If (STAT != 0) {
Debug = "Trying to run _REG(connect) twice"
RET += 1
Break
}
STAT += 1
Break
}
}
}
OperationRegion(PCIR, PCI_Config, 0x00, Arg0)
Field (PCIR, AnyAcc, NoLock) {
REG0, 8
}
If (STAT != 1) {
Debug = "No one ever called _REG on PCIR, giving up"
RET += 1
Return ()
}
REG0 = 123
}
}
}
Device (PCI1) {
Name (STAT, 0)
// PCI root bus
Name (_HID, "PNP0A03")
Device (XHCI) {
Name (_ADR, 0x00030002)
OperationRegion(HREG, PCI_Config, 0x04, 0xF0)
Field (HREG, AnyAcc, NoLock) {
REG2, 8
}
// This can only be called after loading the namespace
Method (_REG, 2) {
Printf("HREG _REG(%o, %o) called", Arg0, Arg1)
If (Arg0 != 2) {
Printf("Invalid space value %o", Arg0)
RET += 1
}
If (Arg1 != 1 || STAT != 0) {
Printf("Invalid Arg1 (%o) for state %o", Arg1, STAT)
RET += 1
}
STAT += 1
}
Method (WRIT, 1) {
REG2 = Arg0
Return (Arg0)
}
}
}
Method (MAIN) {
\PCI0.UHCI.TEST(0xFF)
Local0 = 1
If (\PCI1.STAT == 1) {
Local0 -= \PCI1.XHCI.WRIT(1)
} Else {
Debug = "PCI1.XHCI._REG was never called!"
}
Return (RET + Local0)
}
}

View File

@ -0,0 +1,15 @@
// Name: Return Integer 0 (Indirect)
// Expect: int => 0
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (HELP, 0, NotSerialized)
{
Return (0)
}
Method (MAIN, 0, NotSerialized)
{
Return (HELP())
}
}

View File

@ -0,0 +1,32 @@
// Name: Return Integer 1 Using Ifs
// Expect: int => 1
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (GET0, 1, NotSerialized)
{
Debug = "GET0 called"
Return(Arg0)
}
Method (MAIN, 0, NotSerialized)
{
If (GET0(0)) {
Debug = "We shouldn't be here..."
} Else {
Local0 = 0
If (GET0(1)) {
Debug = "Branch worked"
Local0 = 1
} Else {
Debug = "Shouldn't see this either"
Local0 = 2
}
Return(Local0)
}
Return (3)
}
}

View File

@ -0,0 +1,20 @@
// Name: Return Integer 0xDEAD (Double Indirect)
// Expect: int => 0xDEAD
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (HELP, 0, NotSerialized)
{
Return (0xDEAD)
}
Method (INDI, 0, NotSerialized)
{
Return (HELP())
}
Method (MAIN, 0, NotSerialized)
{
Return (INDI())
}
}

View File

@ -0,0 +1,10 @@
// Name: Return Integer (Byte 0xCA)
// Expect: int => 0xCA
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (MAIN, 0, NotSerialized)
{
Return (0xCA)
}
}

View File

@ -0,0 +1,10 @@
// Name: Return Integer (DWord 0xCAFEBABE)
// Expect: int => 0xCAFEBABE
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (MAIN, 0, NotSerialized)
{
Return (0xCAFEBABE)
}
}

View File

@ -0,0 +1,10 @@
// Name: Return Integer (QWord 0xCAFEBABEDEADC0DE)
// Expect: int => 0xCAFEBABEDEADC0DE
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (MAIN, 0, NotSerialized)
{
Return (0xCAFEBABEDEADC0DE)
}
}

View File

@ -0,0 +1,10 @@
// Name: Return Integer (Word 0xCAFE)
// Expect: int => 0xCAFE
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (MAIN, 0, NotSerialized)
{
Return (0xCAFE)
}
}

View File

@ -0,0 +1,32 @@
// Name: Scopes and undefined references
// Expect: int => 13
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Name (MAIN, 0)
Scope (\PATH.THAT.DOES.NOT.EXIS.T) {
Debug = "Why are we here"
MAIN += 1
}
Scope (\_SB) {
MAIN += 3
Scope (^ANOT.HER) {
MAIN += 4
}
Scope (\_GPE) {
MAIN += 10
Scope (PATH) {
MAIN += 200
}
}
Scope (FAIL.TOO) {
MAIN += 300
}
}
}

View File

@ -0,0 +1,50 @@
// Name: Sleep & Stall
// Expect: int => 0
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (CHEK, 4)
{
Local0 = Arg2 - Arg1
If (Local0 < Arg3) {
Printf("%o finished too soon, elapsed %o, expected at least %o",
Arg0, ToDecimalString(Local0), ToDecimalString(Arg3))
Return (1)
}
Return (0)
}
Method (STAL, 1) {
Local1 = 0
While (Local1 < Arg0) {
Stall(100)
Local1 += 1
}
}
Method (MAIN, 0, Serialized)
{
Local0 = Timer
// Stall for 10 * 100 microseconds (aka 1ms)
STAL(10)
If (CHEK("Stall", Local0, Timer, 10000) != 0) {
Return (1)
}
Local0 = Timer
// Sleep for 2ms
Sleep(2)
If (CHEK("Sleep", Local0, Timer, 20000) != 0) {
Return (1)
}
Return (0)
}
}

View File

@ -0,0 +1,26 @@
// Name: Store Copies The Buffer
// Expect: str => Hello
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (MODF, 0, NotSerialized)
{
Local1 = Store("Hello", Local0)
Return (Local1)
}
Method (MAIN, 0, NotSerialized)
{
Local1 = Store(MODF(), Local0)
// Modify the string at Local1
Local2 = RefOf(Local1)
Local2 = "Goodbye"
Debug = Local0
Debug = Local1
// Local0 should still have the same value
Return(Local0)
}
}

View File

@ -0,0 +1,61 @@
// Name: Load tables from buffers
// Expect: str => Hello World 123
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (MAIN, 0, Serialized)
{
Name (WRLD, "World")
/*
* Method (PRT0, 0, NotSerialized)
* {
* Return(Concatenate("Hello ", \MAIN.WRLD))
* }
*/
External(\PRT0, MethodObj)
Name (TABL, Buffer {
0x53,0x53,0x44,0x54,0x40,0x00,0x00,0x00, /* 00000000 "SSDT@..." */
0x02,0x86,0x75,0x54,0x45,0x53,0x54,0x00, /* 00000008 "..uTEST." */
0x54,0x45,0x53,0x54,0x54,0x41,0x42,0x4C, /* 00000010 "TESTTAB0" */
0xF0,0xF0,0xF0,0xF0,0x49,0x4E,0x54,0x4C, /* 00000018 "....INTL" */
0x31,0x03,0x22,0x20,0x14,0x1B,0x50,0x52, /* 00000020 "1." ..PR" */
0x54,0x30,0x00,0xA4,0x73,0x0D,0x48,0x65, /* 00000028 "T0..s.He" */
0x6C,0x6C,0x6F,0x20,0x00,0x5C,0x2E,0x4D, /* 00000030 "llo .\.M" */
0x41,0x49,0x4E,0x57,0x52,0x4C,0x44,0x00 /* 00000038 "AINWRLD." */
})
If (!Load(TABL)) {
Return ("Table 0 load failed")
}
/*
* Name (O123, "123")
*
* Method (PRT1, 2, NotSerialized)
* {
* Return(Concatenate(Concatenate(Arg0, " "), Arg1))
* }
*/
External(\PRT1, MethodObj)
External(\O123, StrObj)
Name (TAB1, Buffer {
0x53,0x53,0x44,0x54,0x3F,0x00,0x00,0x00, /* 00000000 "SSDT?..." */
0x02,0x97,0x75,0x54,0x45,0x53,0x54,0x00, /* 00000008 "..uTEST." */
0x54,0x45,0x53,0x54,0x54,0x41,0x42,0x4C, /* 00000010 "TESTTAB1" */
0xF0,0xF0,0xF0,0xF0,0x49,0x4E,0x54,0x4C, /* 00000018 "....INTL" */
0x31,0x03,0x22,0x20,0x08,0x4F,0x31,0x32, /* 00000020 "1." .O12" */
0x33,0x0D,0x31,0x32,0x33,0x00,0x14,0x10, /* 00000028 "3.123..." */
0x50,0x52,0x54,0x31,0x02,0xA4,0x73,0x73, /* 00000030 "PRT1..ss" */
0x68,0x0D,0x20,0x00,0x00,0x69,0x00 /* 00000038 "h. ..i." */
})
Load(TAB1, Local1)
If (!Local1) {
Return ("Table 1 load failed")
}
Local0 = PRT0()
Return (PRT1(Local0, O123))
}
}

View File

@ -0,0 +1,76 @@
// Name: Load tables from opregions & fields
// Expect: str => Hello World 123
DefinitionBlock ("", "SSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (MAIN, 0, Serialized)
{
Name (WRLD, "World")
/*
* Method (PRT0, 0, NotSerialized)
* {
* Return(Concatenate("Hello ", \MAIN.WRLD))
* }
*/
External(\PRT0, MethodObj)
Name (TAB0, Buffer {
0x53,0x53,0x44,0x54,0x40,0x00,0x00,0x00, /* 00000000 "SSDT@..." */
0x02,0x86,0x75,0x54,0x45,0x53,0x54,0x00, /* 00000008 "..uTEST." */
0x54,0x45,0x53,0x54,0x54,0x41,0x42,0x4C, /* 00000010 "TESTTAB0" */
0xF0,0xF0,0xF0,0xF0,0x49,0x4E,0x54,0x4C, /* 00000018 "....INTL" */
0x31,0x03,0x22,0x20,0x14,0x1B,0x50,0x52, /* 00000020 "1." ..PR" */
0x54,0x30,0x00,0xA4,0x73,0x0D,0x48,0x65, /* 00000028 "T0..s.He" */
0x6C,0x6C,0x6F,0x20,0x00,0x5C,0x2E,0x4D, /* 00000030 "llo .\.M" */
0x41,0x49,0x4E,0x57,0x52,0x4C,0x44,0x00 /* 00000038 "AINWRLD." */
})
OperationRegion(TABR, SystemMemory, 0xDEADBEE0, SizeOf(TAB0))
Field (TABR, WordAcc, NoLock, WriteAsOnes) {
COPY, 512,
}
COPY = TAB0
Load(TABR, Local0)
If (!Local0) {
Return ("Table 0 load failed")
}
/*
* Name (O123, "123")
*
* Method (PRT1, 2, NotSerialized)
* {
* Return(Concatenate(Concatenate(Arg0, " "), Arg1))
* }
*/
External(\PRT1, MethodObj)
External(\O123, StrObj)
Name (TAB1, Buffer {
0x53,0x53,0x44,0x54,0x3F,0x00,0x00,0x00, /* 00000000 "SSDT?..." */
0x02,0x97,0x75,0x54,0x45,0x53,0x54,0x00, /* 00000008 "..uTEST." */
0x54,0x45,0x53,0x54,0x54,0x41,0x42,0x4C, /* 00000010 "TESTTAB1" */
0xF0,0xF0,0xF0,0xF0,0x49,0x4E,0x54,0x4C, /* 00000018 "....INTL" */
0x31,0x03,0x22,0x20,0x08,0x4F,0x31,0x32, /* 00000020 "1." .O12" */
0x33,0x0D,0x31,0x32,0x33,0x00,0x14,0x10, /* 00000028 "3.123..." */
0x50,0x52,0x54,0x31,0x02,0xA4,0x73,0x73, /* 00000030 "PRT1..ss" */
0x68,0x0D,0x20,0x00,0x00,0x69,0x00,0x00 /* 00000038 "h. ..i." */
})
OperationRegion(TABX, SystemMemory, 0xCAFEBAB0, SizeOf(TAB1))
Field (TABX, DWordAcc, NoLock, Preserve) {
BLOB, 512,
}
// Copy the table into the operation region
BLOB = TAB1
Load(BLOB, Local1)
If (!Local1) {
Return ("Table 1 load failed")
}
Local0 = PRT0()
Return (PRT1(Local0, O123))
}
Debug = MAIN()
}

View File

@ -0,0 +1,113 @@
// Name: Recursive table loads
// Expect: int => 0
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
// Number of started table loads
Name (NUMB, 0)
// Number of finished table loads
Name (NUMA, 0)
/*
* DefinitionBlock ("", "SSDT", 1, "uTEST", "TESTTABL", 0xF0F0F0F0)
* {
* External(NUMA, IntObj)
* External(NUMB, IntObj)
* External(ITEM, IntObj)
* External(TABL, IntObj)
*
* // Recursively start 10 table loads
* If (NUMB < 10) {
* // Create an ITEM here to prove to the caller that we got invoked
* If (!CondRefOf(ITEM)) {
* Name (ITEM, 123)
* }
*
* NUMB += 1
* Local0 = Load(TABL)
*
* // The last load is expected to fail, everything before should succeed
* If (!Local0) {
* If (NUMB != 10) {
* NUMA = 0xDEADBEEF
* Printf("Table load %o failed", NUMB)
* }
* } Else {
* NUMA += 1
* }
*
*
* // Return something bogus here to make sure the return value isn't
* // propagated to the caller of Load.
* Return (Package { 1, 2 ,3})
* }
*
* // We're the last table load, do something naughty to cause an error
* Local0 = Package { 1 }
* Local1 = RefOf(Local0)
*
* // This code specifically attempts to perform a bogus implicit cast
* Local1 = "Hello World"
* }
*/
Name (TABL, Buffer {
0x53, 0x53, 0x44, 0x54, 0xdc, 0x00, 0x00, 0x00,
0x01, 0x33, 0x75, 0x54, 0x45, 0x53, 0x54, 0x00,
0x54, 0x45, 0x53, 0x54, 0x54, 0x41, 0x42, 0x4c,
0xf0, 0xf0, 0xf0, 0xf0, 0x49, 0x4e, 0x54, 0x4c,
0x28, 0x06, 0x23, 0x20, 0xa0, 0x22, 0x00, 0x15,
0x5c, 0x4e, 0x55, 0x4d, 0x41, 0x01, 0x00, 0x15,
0x5c, 0x4e, 0x55, 0x4d, 0x42, 0x01, 0x00, 0x15,
0x5c, 0x49, 0x54, 0x45, 0x4d, 0x01, 0x00, 0x15,
0x5c, 0x54, 0x41, 0x42, 0x4c, 0x01, 0x00, 0xa0,
0x4b, 0x07, 0x95, 0x4e, 0x55, 0x4d, 0x42, 0x0a,
0x0a, 0xa0, 0x10, 0x92, 0x5b, 0x12, 0x49, 0x54,
0x45, 0x4d, 0x00, 0x08, 0x49, 0x54, 0x45, 0x4d,
0x0a, 0x7b, 0x72, 0x4e, 0x55, 0x4d, 0x42, 0x01,
0x4e, 0x55, 0x4d, 0x42, 0x70, 0x5b, 0x20, 0x54,
0x41, 0x42, 0x4c, 0x00, 0x60, 0xa0, 0x38, 0x92,
0x60, 0xa0, 0x34, 0x92, 0x93, 0x4e, 0x55, 0x4d,
0x42, 0x0a, 0x0a, 0x70, 0x0c, 0xef, 0xbe, 0xad,
0xde, 0x4e, 0x55, 0x4d, 0x41, 0x70, 0x73, 0x73,
0x0d, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x6c,
0x6f, 0x61, 0x64, 0x20, 0x00, 0x4e, 0x55, 0x4d,
0x42, 0x00, 0x0d, 0x20, 0x66, 0x61, 0x69, 0x6c,
0x65, 0x64, 0x00, 0x00, 0x5b, 0x31, 0xa1, 0x0b,
0x72, 0x4e, 0x55, 0x4d, 0x41, 0x01, 0x4e, 0x55,
0x4d, 0x41, 0xa4, 0x12, 0x07, 0x03, 0x01, 0x0a,
0x02, 0x0a, 0x03, 0x70, 0x12, 0x03, 0x01, 0x01,
0x60, 0x70, 0x71, 0x60, 0x61, 0x70, 0x0d, 0x48,
0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72,
0x6c, 0x64, 0x00, 0x61
})
Method (MAIN, 0, Serialized)
{
Load(TABL, Local0)
Printf("Recursive loads finished!")
If (!Local0) {
Printf("Table load failed!")
Return (0xCAFEBABE)
}
External(ITEM, IntObj)
If (ITEM != 123) {
Printf("ITEM has incorrect value %o", ITEM)
Return (0xDEADBEEF)
}
If (NUMB != 10) {
Printf("Invalid NUMB value %o", ToDecimalString(NUMB))
Return (0xEEFFAABB)
}
If (NUMA != 9) {
Printf("Invalid NUMA value %o", ToDecimalString(NUMA))
Return (0x11223344)
}
Return (0)
}
}

View File

@ -0,0 +1,106 @@
// Name: Recursive table loads with LoadTable
// Expect: int => 0
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
External(ITEM, IntObj)
External(NUMB, IntObj)
External(NUMA, IntObj)
External(VISI, IntObj)
// All dynamic loads branch into here
If (CondRefOf(ITEM)) {
// Recursively start 10 table loads
If (NUMB < 10) {
NUMB += 1
Local0 = NUMB
If (NUMB == 3) {
Local0 = LoadTable("DSDT", "uTEST", "", "", "PARA", Local0)
VISI += 1
} ElseIf (NUMB == 5) {
Local0 = LoadTable("DSDT", "", "", "", "PARA", Local0)
VISI += 10
} ElseIf (NUMB == 7) {
Local0 = LoadTable("DSDT", "", "TESTTABL", "", "", Local0)
VISI += 100
} Else {
Local0 = LoadTable("DSDT", "uTEST", "TESTTABL", "", "PARA", Local0)
VISI += 1000
}
// The last load is expected to fail, everything before should succeed
If (!Local0) {
If (NUMB != 10) {
NUMA = 0xDEADBEEF
Printf("Table load %o failed", NUMB)
}
} Else {
NUMA += 1
}
// Return something bogus here to make sure the return value isn't
// propagated to the caller of Load.
Return (Package { 1, 2 ,3})
}
// We're the last table load, do something naughty to cause an error
Local0 = Package { 1 }
Local1 = RefOf(Local0)
// This code specifically attempts to perform a bogus implicit cast
Local1 = "Hello World"
}
Name (ITEM, 123)
// Number of started table loads
Name (NUMB, 0)
// Number of finished table loads
Name (NUMA, 0)
// Visited branches
Name (VISI, 0)
Name (PARA, 0)
Name (PASS, "FAIL")
Method (MAIN, 0, Serialized)
{
Local0 = LoadTable("DSDT", "uTEST", "TESTTABL", "", "PASS", 0x53534150)
Printf("Recursive loads finished!")
If (!Local0) {
Printf("Table load failed!")
Return (0xCAFEBABE)
}
If (NUMB != 10) {
Printf("Invalid NUMB value %o", ToDecimalString(NUMB))
Return (0xEEFFAABB)
}
If (VISI != 7111) {
Printf("Invalid VISI value %o", ToDecimalString(VISI))
Return (0xAFFAAFFA)
}
If (NUMA != 9) {
Printf("Invalid NUMA value %o", ToDecimalString(NUMA))
Return (0x11223344)
}
If (PARA != 1) {
Printf("Invalid PARA value %o", ToDecimalString(PARA))
Return (0xDDFFBBCC)
}
If (PASS != "PASS") {
Printf("Invalid PASS value %o", PASS)
Return (0xECECECEC)
}
Return (0)
}
}

View File

@ -0,0 +1,124 @@
// Name: LoadTable scoping rules are correct
// Expect: int => 0
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
External(SSDT, IntObj)
External(VAL, IntObj)
// All dynamic loads branch into here
If (CondRefOf(SSDT)) {
Name (TEST, 0)
TEST = VAL
Return (Package { 1 })
}
Name (SSDT, 123)
Name (VAL, 1)
Device (DEV0) {
Name (LRES, 0)
}
Device (DEV1) { }
Scope (_SB) {
Device (DEV1) {
Name (LRES, "XX")
Device (DEV1) { }
}
}
Name (LRES, 99)
Device (DEV2) {
Name (LRES, 123)
}
Method (LDTB, 3) {
Local0 = LoadTable("DSDT", "uTEST", "TESTTABL", Arg0, Arg1, Arg2)
VAL += 1
If (!Local0) {
Printf("Table load failed!")
Return (0)
}
Return (1)
}
Method (MAIN)
{
/*
* DEV0 is the scope, LRES should be evaluated relative to it.
* TEST is expected to be loaded there as well.
*/
If (!LDTB("DEV0", "LRES", 0xFEBEFEBE)) {
Return (1)
}
If (!CondRefOf(\DEV0.TEST, Local0)) {
Printf("No TEST under \\DEV0")
Return (1)
}
If (DerefOf(Local0) != 1) {
Printf("Incorrect \\DEV0.TEST value %o", DerefOf(Local0))
Return (1)
}
If (\DEV0.LRES != 0xFEBEFEBE) {
Printf("\\DEV0.LRES has an incorrect value %o", \DEV0.LRES)
Return (1)
}
CopyObject(0, Local0)
Scope (\_SB.DEV1) {
/*
* We're already inside \_SB.DEV1, so this DEV1 should match
* \_SB.DEV1.DEV1, note that there's also \DEV1, that shouldn't
* get matched here.
*
* There's, however, no \_SB.DEV1.DEV1.LRES, so this should resolve
* into \_SB.DEV1.LRES instead.
*/
Local0 = LoadTable("DSDT", "uTEST", "TESTTABL", "DEV1", "LRES", 0x4B4F)
If (!Local0) {
Printf("Table load failed!")
Return (0)
}
VAL += 1
}
If (!CondRefOf(\_SB.DEV1.DEV1.TEST, Local0)) {
Printf("No TEST under _SB.DEV1.DEV1")
Return (1)
}
If (DerefOf(Local0) != 2) {
Printf("Incorrect \\_SB.DEV1.DEV1.TEST value %o", DerefOf(Local0))
}
If (\_SB.DEV1.LRES != "OK") {
Printf("DEV1.LRES has an incorrect value %o", \_SB.DEV1.LRES)
Return (1)
}
CopyObject(0, Local0)
/*
* DEV2 relative load, however, LRES is specified as an absolute path
* so it shouldn't get resolved to DEV2.LRES.
*/
If (!LDTB("DEV2", "\\LRES", 0xCAFEBABE)) {
Return (1)
}
If (!CondRefOf(\DEV2.TEST, Local0)) {
Printf("No TEST under \\DEV2")
Return (1)
}
If (DerefOf(Local0) != 3) {
Printf("Incorrect \\DEV2.TEST value %o", DerefOf(Local0))
Return (1)
}
If (\LRES != 0xCAFEBABE) {
Printf("\\LRES has an incorrect value %o", \LRES)
Return (1)
}
Return (0)
}
}

View File

@ -0,0 +1,64 @@
// Name: Load/LoadTable handle bogus-sized tables correctly
// Expect: int => 0
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
External(\SSDT, IntObj)
External(\OK, IntObj)
External(\TYPE, IntObj)
// All dynamic loads branch into here
If (CondRefOf(\OK)) {
if (TYPE == 0) {
Local0 = Load(\SSDT)
} Else {
DataTableRegion (DSDT, "DSDT", "uTEST", "TESTTABL")
Field (DSDT, DwordAcc, NoLock, Preserve) {
SIGN, 32,
LENG, 32,
}
// Make our own length bogus, then try to load ourselves, this should fail
LENG = 3
LoadTable("DSDT", "uTEST", "TESTTABL", "", "", 0)
}
// Should be unreachable, we expect the Load above to abort us
OK += 1
Return (0)
}
Name (SSDT, Buffer {
0x53, 0x53, 0x44, 0x54, 0x2a, 0x00, 0x00, 0x00,
0x01, 0x89, 0x75, 0x54, 0x45, 0x53, 0x54, 0x00,
0x42, 0x41, 0x44, 0x54, 0x42, 0x4c, 0x00, 0x00,
0xf0, 0xf0, 0xf0, 0xf0, 0x49, 0x4e, 0x54, 0x4c,
0x28, 0x06, 0x23, 0x20, 0x70, 0x0d, 0x3f, 0x00,
0x5b, 0x31
})
Name (OK, 0)
Name (TYPE, 0)
Method (MAIN) {
// Make the size something bogus
SSDT[4] = 0x11
SSDT[5] = 0
SSDT[6] = 0
SSDT[7] = 0
// Try Load'ing a bogus length SSDT
TYPE = 0
Local0 = LoadTable("DSDT", "uTEST", "TESTTABL", "", "", 0)
// Now try LoadTable ourselves, after corrupting our own length
TYPE = 1
Local0 = LoadTable("DSDT", "uTEST", "TESTTABL", "", "", 0)
// Expect the above to fail
if (Local0 || OK) {
Return (1)
}
Return (0)
}
}

View File

@ -0,0 +1,70 @@
// Name: Table overrides work
// Expect: int => 0
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
/*
* We expect this table to be denied by the test-runner because it denies
* anything with "DENYTABL" in OEM table id.
*
* DefinitionBlock ("x.aml", "SSDT", 1, "uTEST", "DENYTABL", 0xF0F0F0F0)
* {
* Name (BUG, 1)
* }
*/
Name (TAB0, Buffer {
0x53, 0x53, 0x44, 0x54, 0x2a, 0x00, 0x00, 0x00,
0x01, 0xe1, 0x75, 0x54, 0x45, 0x53, 0x54, 0x00,
0x44, 0x45, 0x4e, 0x59, 0x54, 0x41, 0x42, 0x4c,
0xf0, 0xf0, 0xf0, 0xf0, 0x49, 0x4e, 0x54, 0x4c,
0x28, 0x06, 0x23, 0x20, 0x08, 0x42, 0x55, 0x47,
0x5f, 0x01
})
/*
* We expect this table to be overriden by the test-runner because it
* overrides anything with "OVERTABL" in OEM table id. The override it
* provides has a Name(VAL, "TestRunner")
*
* DefinitionBlock ("x.aml", "SSDT", 1, "uTEST", "OVERTABL", 0xF0F0F0F0)
* {
* Name (VAL, "Hello")
* }
*/
Name (TAB1, Buffer {
0x53, 0x53, 0x44, 0x54, 0x30, 0x00, 0x00, 0x00,
0x01, 0xcd, 0x75, 0x54, 0x45, 0x53, 0x54, 0x00,
0x4f, 0x56, 0x45, 0x52, 0x54, 0x41, 0x42, 0x4c,
0xf0, 0xf0, 0xf0, 0xf0, 0x49, 0x4e, 0x54, 0x4c,
0x25, 0x09, 0x20, 0x20, 0x08, 0x56, 0x41, 0x4c,
0x5f, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x00
})
Method (MAIN, 0, NotSerialized)
{
Load(TAB0, Local0)
If (Local0) {
Debug = "Table was not denied load"
Return (0xDEAD)
}
If (CondRefOf(BUG)) {
Debug = "Table was not loaded but the BUG object exists"
Return (0xDEAD)
}
Load(TAB1, Local0)
If (!Local0) {
Debug = "Failed to load table"
Return (0xDEAD)
}
If (VAL != "TestRunner") {
Printf("Table didn't get overriden correctly: expected %o got %o",
"TestRunner", VAL)
Return (0xDEAD)
}
Return (0)
}
}

View File

@ -0,0 +1,113 @@
// Name: ToInteger produces correct results
// Expect: int => 0
DefinitionBlock ("", "SSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Name(FCNT, 0)
Method (CHEK, 3)
{
If (Arg0 != Arg1) {
FCNT++
Printf("On line %o: invalid number %o, expected %o",
ToDecimalString(Arg2), ToHexString(Arg0), ToHexString(Arg1))
}
}
Method (MAIN, 0, NotSerialized)
{
Local0 = ToInteger(123)
Local1 = 123
CHEK(Local0, Local1, __LINE__)
Local0 = ToInteger("123")
Local1 = 123
CHEK(Local0, Local1, __LINE__)
Local0 = ToInteger(" \t\t\t\v 123")
Local1 = 123
CHEK(Local0, Local1, __LINE__)
Local0 = ToInteger("123abcd")
Local1 = 123
CHEK(Local0, Local1, __LINE__)
Local0 = ToInteger("0x123abcd")
Local1 = 0x123abcd
CHEK(Local0, Local1, __LINE__)
Local0 = ToInteger("")
Local1 = 0
CHEK(Local0, Local1, __LINE__)
Local0 = ToInteger("0X")
Local1 = 0
CHEK(Local0, Local1, __LINE__)
Local0 = ToInteger("0x")
Local1 = 0
CHEK(Local0, Local1, __LINE__)
Local0 = ToInteger("0")
Local1 = 0
CHEK(Local0, Local1, __LINE__)
Local0 = ToInteger("0xDeAdBeeF")
Local1 = 0xDEADBEEF
CHEK(Local0, Local1, __LINE__)
Local0 = ToInteger("0XDeAdBeeFCafeBabeHelloWorld")
Local1 = 0xDEADBEEFCAFEBABE
CHEK(Local0, Local1, __LINE__)
Local0 = ToInteger(Buffer { 0xDE, 0xAD, 0xBE, 0xEF })
Local1 = 0xEFBEADDE
CHEK(Local0, Local1, __LINE__)
Local0 = ToInteger(Buffer { 1 })
Local1 = 1
CHEK(Local0, Local1, __LINE__)
Local0 = ToInteger(Buffer { 0 })
Local1 = 0
CHEK(Local0, Local1, __LINE__)
Local0 = ToInteger(Buffer { 0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE })
Local1 = 0xBEBAFECAEFBEADDE
CHEK(Local0, Local1, __LINE__)
// These are incompatible with ACPICA, skip if it's detected
If (ToHexString(0xF) == "0xF") {
Local0 = ToInteger("99999999999999999999999999999999999999999999999")
Local1 = 0xFFFFFFFFFFFFFFFF
CHEK(Local0, Local1, __LINE__)
Local0 = ToInteger("0xDEADBEEFCAFEBABE1")
Local1 = 0xFFFFFFFFFFFFFFFF
CHEK(Local0, Local1, __LINE__)
Local0 = ToInteger("+123")
Local1 = 123
CHEK(Local0, Local1, __LINE__)
Local0 = ToInteger("-123")
Local1 = 0xFFFFFFFFFFFFFF85
CHEK(Local0, Local1, __LINE__)
Local0 = ToInteger("-0xDEADBEF HELLOWORLD")
Local1 = 0xFFFFFFFFF2152411
CHEK(Local0, Local1, __LINE__)
Local0 = ToInteger("+0XC0D\t123")
Local1 = 0xC0D
CHEK(Local0, Local1, __LINE__)
Local0 = ToInteger("0123")
Local1 = 83
CHEK(Local0, Local1, __LINE__)
}
Return (FCNT)
}
}

View File

@ -0,0 +1,99 @@
// Name: ToX produces correct results
// Expect: int => 0
DefinitionBlock ("", "SSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Name(FCNT, 0)
Method (CHEK, 2)
{
If (ObjectType(Arg0) == 3) {
Arg0 = ToHexString(Arg0)
}
If (ObjectType(Arg1) == 3) {
Arg1 = ToHexString(Arg1)
}
If (Arg0 != Arg1) {
FCNT++
Printf("Invalid string %o, expected %o", Arg0, Arg1)
}
}
Method (MAIN, 0, NotSerialized)
{
// Dec string
Local0 = ToDecimalString(123)
Local1 = "123"
CHEK(Local0, Local1)
Local0 = ToDecimalString(Buffer { 1, 2, 222, 33, 45, 192, 3, 255 })
Local1 = "1,2,222,33,45,192,3,255"
CHEK(Local0, Local1)
Local0 = ToDecimalString("")
Local1 = ""
CHEK(Local0, Local1)
Local0 = ToDecimalString("123")
Local1 = "123"
CHEK(Local0, Local1)
Local0 = ToDecimalString(0xFFFFFFFFFFFFFFFF)
Local1 = "18446744073709551615"
CHEK(Local0, Local1)
// Hex string
Local0 = ToHexString(123)
Local1 = "0x7B"
CHEK(Local0, Local1)
Local0 = ToHexString(Buffer { 1, 2, 222, 33, 45, 192, 3, 255 })
Local1 = "0x01,0x02,0xDE,0x21,0x2D,0xC0,0x03,0xFF"
CHEK(Local0, Local1)
Local0 = ToHexString("")
Local1 = ""
CHEK(Local0, Local1)
Local0 = ToHexString("123")
Local1 = "123"
CHEK(Local0, Local1)
Local0 = ToHexString(0xF)
Local1 = "0xF"
CHEK(Local0, Local1)
Local0 = ToHexString(0xFF)
Local1 = "0xFF"
CHEK(Local0, Local1)
Local0 = ToHexString(0xFFF)
Local1 = "0xFFF"
CHEK(Local0, Local1)
Local0 = ToHexString(0xFFFFF)
Local1 = "0xFFFFF"
CHEK(Local0, Local1)
Local0 = ToHexString(0xFFFFFFFFFFFFFFFF)
Local1 = "0xFFFFFFFFFFFFFFFF"
CHEK(Local0, Local1)
// Buffer
Local0 = ToBuffer(Buffer { 1, 2, 3 })
Local1 = Buffer { 1, 2, 3 }
CHEK(Local0, Local1)
Local0 = ToBuffer("Hello")
Local1 = Buffer { 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x00 }
CHEK(Local0, Local1)
Local0 = ToBuffer(0xDEADBEEFCAFEBABE)
Local1 = Buffer { 0xBE, 0xBA, 0xFE, 0xCA, 0xEF, 0xBE, 0xAD, 0xDE }
CHEK(Local0, Local1)
Return (FCNT)
}
}

View File

@ -0,0 +1,30 @@
// Name: Unresolved paths don't error out when needed
// Expect: int => 1
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (MAIN, 0, NotSerialized)
{
Local0 = Package {
321,
PATH.THAT.DOES.NOT_.EXIS.T,
123,
\OK,
\MAIN,
}
Debug = Local0
If (CondRefOf(ANOT.HER_.PATH.THAT.DOES.NOT.EXIS.T, Local0) ||
CondRefOf(\TEST) || CondRefOf(^TEST) || CondRefOf(^TEST.JEST) ||
CondRefOf(\TEST.JEST)) {
Return (0)
}
If (CondRefOf(^MAIN, Local1)) {
Debug = Local1
Return (1)
}
Return (0)
}
}

View File

@ -0,0 +1,24 @@
// Name: While With Break
// Expect: int => 10
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (MAIN, 0, NotSerialized)
{
Local0 = 1
Local1 = 10
While (Local0) {
If (Local1--) {
Debug = "Incrementing Local0"
Local0++
Debug = Local0
} Else {
Debug = "Local1 is 0, breaking"
Break
}
}
Return(Local0)
}
}

View File

@ -0,0 +1,25 @@
// Name: While With Continue
// Expect: int => 10
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (MAIN, 0, NotSerialized)
{
Local0 = 1
Local1 = 10
While (Local0) {
If (Local1--) {
Debug = "Incrementing Local0 & continuing"
Local0++
Debug = Local0
Continue
}
Debug = "Local1 is 0, breaking"
Break
}
Return(Local0)
}
}

View File

@ -0,0 +1,20 @@
// Name: Increment/Decrement With While
// Expect: int => 10
DefinitionBlock ("", "DSDT", 2, "uTEST", "TESTTABL", 0xF0F0F0F0)
{
Method (MAIN, 0, NotSerialized)
{
Local0 = 1
Local1 = 10
While (Local1--) {
Debug = "Iteration"
Debug = Local0
Local0++
}
Return(Local0)
}
}

View File

@ -0,0 +1,217 @@
import os
import subprocess
from typing import List, Optional, Union
class ASL:
@staticmethod
def definition_block(kind: str, revision: int) -> str:
block = "DefinitionBlock "
block += f'("", "{kind}", {revision}, '
block += '"uTEST", "TESTTABL", 0xF0F0F0F0)'
return block
@staticmethod
def name(name: str, term_arg: Union[str, int]) -> str:
return f"Name({name}, {term_arg})"
@staticmethod
def buffer(
initializers: Optional[List[int]] = None,
count: Optional[int] = None
) -> str:
if count is not None:
buf_len = count
elif initializers is not None:
buf_len = len(initializers)
else:
buf_len = 0
buf = f"Buffer({buf_len})"
buf += " { "
if initializers is not None:
buf += ", ".join([str(x) for x in initializers])
buf += " }"
return buf
@staticmethod
def method(name: str, num_args: Optional[int] = None) -> str:
method = f"Method ({name}"
if num_args is not None:
method += f", {num_args}"
method += ")"
return method
@staticmethod
def invoke(name: str, args: List[str] = []) -> str:
invocation = f"{name}("
invocation += ", ".join(args)
invocation += ")"
return invocation
@staticmethod
def end_block() -> str:
return "}"
@staticmethod
def assign(dst: str, src: str) -> str:
return f"{dst} = {src}"
@staticmethod
def increment(name: str) -> str:
return f"{name}++"
@staticmethod
def create_field(buf: str, idx: int, field_size: int,
field_name: str) -> str:
return f"CreateField({buf}, {idx}, {field_size}, {field_name})"
@staticmethod
def iff(arg: str) -> str:
return f"If ({arg})"
@staticmethod
def elsee() -> str:
return "Else"
@staticmethod
def equal(arg0: str, arg1: str) -> str:
return f"{arg0} == {arg1}"
@staticmethod
def returnn(arg: str) -> str:
return f"Return ({arg})"
class ASLSource:
def __init__(self, revision: int = 0):
self.lines: List[str] = []
self.revision = revision
self.indentation = 0
self.l(ASL.definition_block("DSDT", 2))
self.block_begin()
def l(self, line: str) -> None:
line = " " * (self.indentation * 4) + line
self.lines.append(line)
def block_begin(self) -> None:
self.l("{")
self.indentation += 1
def block_end(self) -> None:
self.indentation -= 1
self.l("}\n")
def iff(self, arg: str) -> None:
self.l(ASL.iff(arg) + " {")
self.indentation += 1
def elsee(self) -> None:
self.indentation -= 1
self.l("} Else {")
self.indentation += 1
def finalize(self) -> None:
self.block_end()
def dump(self, path: str) -> None:
with open(path, "w") as f:
f.write("\n".join(self.lines))
def dump_as_test_case(
self, path: str, case_name: str,
expected_type: str, expected_value: Union[str, int]
) -> None:
with open(path, "w") as f:
f.write(f"// Name: (generated) {case_name}\n")
f.write(f"// Expect: {expected_type} => {expected_value}\n")
f.write("\n".join(self.lines))
@staticmethod
def compile(path: str, compiler: str, bin_dir: str) -> str:
case_aml_name = os.path.basename(path).rsplit(".", 1)[0] + ".aml"
out_case = os.path.join(bin_dir, case_aml_name)
ignored_warnings = [
# Warning 3144 Method Local is set but never used
"-vw", "3144",
# Remark 2098 Recursive method call
"-vw", "2098",
# Invalid type [Reference] found, RefOf operator requires
# [Integer|String|Buffer|Package|FieldUnit|Device|Event|
# Method|Mutex|Region|Power|Processor|Thermal|BufferField|
# DdbHandle|DebugObject]
"-vw", "6058",
# One or more objects within the Pathname do not exist
"-vw", "6161",
# Too few arguments
"-vw", "6005",
# Called method returns no value
"-vw", "6080",
# Object does not exist
"-vw", "6084",
# Not a control method, cannot invoke
"-vw", "6086",
# Object is not referenced
"-vw", "2089",
# Creation of named objects within a method is highly inefficient
"-vw", "2173",
# Existing object has invalid type for Scope operator
"-vw", "6117",
# Switch expression is not a static Integer/Buffer/String data
"-vw", "3124",
# Static OperationRegion should be declared outside control method
"-vw", "3175",
# Missing dependency
# (for some reason it has two different error codes)
"-vw", "3141",
"-vw", "6141",
# Field Unit extends beyond region limit
"-vw", "6031",
# Name already exists in scope
"-vw", "6074",
# Object not found or not accessible from current scope
"-vw", "6085",
# Legacy Processor() keyword detected. Use Device() keyword instead
"-vw", "3168",
# Local or Arg used outside a control method
"-vw", "2067",
]
args = [compiler, *ignored_warnings, "-oa", "-p", out_case, path]
proc = subprocess.Popen(args, stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True)
proc.wait(10)
stdout = proc.stdout
assert stdout
stderr = proc.stderr
assert stderr
if proc.returncode != 0:
raise RuntimeError(f"Compiler error:\nstdout:\n{stdout.read()}"
f"\nstderr:\n{stderr.read()}")
return out_case