/* SPDX-License-Identifier: GPL-2.0-only */ #include <stdlib.h> #include <types.h> #include <tests/test.h> #include <acpi/acpigen.h> #define ACPIGEN_TEST_BUFFER_SZ (16 * KiB) /* Returns AML package length. Works with normal and extended packages. This implementation is independent from acpigen.c implementation of package length. */ static u32 decode_package_length(const char *ptr) { const u8 *aml = (u8 *)ptr; const u32 offset = (aml[0] == EXT_OP_PREFIX ? 2 : 1); u32 byte_zero_mask = 0x3F; /* Bits [0:5] */ u32 byte_count = aml[offset] >> 6; u32 package_length = 0; while (byte_count) { package_length |= aml[offset + byte_count] << ((byte_count << 3) - 4); byte_zero_mask = 0x0F; /* Use bits [0:3] of byte 0 */ byte_count--; } package_length |= (aml[offset] & byte_zero_mask); return package_length; } static u32 get_current_block_length(const char *base) { const u32 offset = (base[0] == EXT_OP_PREFIX ? 2 : 1); return ((uintptr_t)acpigen_get_current() - ((uintptr_t)base + offset)); } static int setup_acpigen(void **state) { void *buffer = malloc(ACPIGEN_TEST_BUFFER_SZ); if (buffer == NULL) return -1; memset(buffer, 0, ACPIGEN_TEST_BUFFER_SZ); *state = buffer; return 0; } static int teardown_acpigen(void **state) { free(*state); return 0; } static void test_acpigen_single_if(void **state) { char *acpigen_buf = *state; u32 if_package_length = 0; u32 block_length = 0; acpigen_set_current(acpigen_buf); /* Create dummy AML */ acpigen_write_if_lequal_op_int(LOCAL0_OP, 64); for (int i = 0; i < 20; ++i) acpigen_write_store_ops(ZERO_OP, LOCAL1_OP); /* Close if */ acpigen_pop_len(); if_package_length = decode_package_length(acpigen_buf); block_length = get_current_block_length(acpigen_buf); assert_int_equal(if_package_length, block_length); } static void create_nested_ifs_recursive(size_t stack_len[], u32 i, u32 n) { if (i >= n) return; char *const start = acpigen_get_current(); acpigen_write_if_and(LOCAL0_OP, ZERO_OP); for (int k = 0; k < 3; ++k) acpigen_write_store_ops(ZERO_OP, LOCAL1_OP); create_nested_ifs_recursive(stack_len, i + 1, n); acpigen_pop_len(); stack_len[i] = acpigen_get_current() - start; } static void test_acpigen_nested_ifs(void **state) { char *acpigen_buf = *state; const size_t nesting_level = 8; size_t block_len[8] = {0}; acpigen_set_current(acpigen_buf); create_nested_ifs_recursive(block_len, 0, nesting_level); for (int i = 0, j = 0; i < nesting_level; ++i, ++j) { /* Find next if op */ for (; j < ACPIGEN_TEST_BUFFER_SZ; ++j) { if ((u8)acpigen_buf[j] == IF_OP) break; } assert_int_equal(decode_package_length(acpigen_buf + j), block_len[i] - 1); } } static void test_acpigen_write_package(void **state) { char *acpigen_buf = *state; u32 package_length; u32 block_length; acpigen_set_current(acpigen_buf); acpigen_write_package(3); acpigen_write_return_singleton_buffer(0xA); acpigen_write_return_singleton_buffer(0x7); acpigen_write_return_singleton_buffer(0xF); acpigen_pop_len(); package_length = decode_package_length(acpigen_buf); block_length = get_current_block_length(acpigen_buf); assert_int_equal(package_length, block_length); } static void test_acpigen_scope_with_contents(void **state) { char *acpigen_buf = *state; char *block_start[8] = {0}; u32 block_counter = 0; u32 package_length; u32 block_length; acpigen_set_current(acpigen_buf); /* Scope("\_SB") { */ block_start[block_counter++] = acpigen_get_current(); acpigen_write_scope("\\_SB"); /* Device("PCI0") { */ block_start[block_counter++] = acpigen_get_current(); acpigen_write_device("PCI0"); /* Name(INT1, 0x1234) */ acpigen_write_name_integer("INT1", 0x1234); /* Name (_HID, EisaId ("PNP0A08")) // PCI Express Bus */ acpigen_write_name("_HID"); acpigen_emit_eisaid("PNP0A08"); /* Method(^BN00, 0, NotSerialized) { */ block_start[block_counter++] = acpigen_get_current(); acpigen_write_method("^BN00", 0); /* Return( 0x12 + ^PCI0.INT1 ) */ acpigen_write_return_op(AND_OP); acpigen_write_byte(0x12); acpigen_emit_namestring("^PCI0.INT1"); /* } */ acpigen_pop_len(); block_counter--; package_length = decode_package_length(block_start[block_counter]); block_length = get_current_block_length(block_start[block_counter]); assert_int_equal(package_length, block_length); /* Method (_BBN, 0, NotSerialized) { */ block_start[block_counter++] = acpigen_get_current(); acpigen_write_method("_BBN", 0); /* Return (BN00 ()) */ acpigen_write_return_namestr("BN00"); acpigen_emit_byte(0x0A); /* } */ acpigen_pop_len(); block_counter--; package_length = decode_package_length(block_start[block_counter]); block_length = get_current_block_length(block_start[block_counter]); assert_int_equal(package_length, block_length); /* } */ acpigen_pop_len(); block_counter--; package_length = decode_package_length(block_start[block_counter]); block_length = get_current_block_length(block_start[block_counter]); assert_int_equal(package_length, block_length); /* } */ acpigen_pop_len(); block_counter--; package_length = decode_package_length(block_start[block_counter]); block_length = get_current_block_length(block_start[block_counter]); assert_int_equal(package_length, block_length); } int main(void) { const struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown(test_acpigen_single_if, setup_acpigen, teardown_acpigen), cmocka_unit_test_setup_teardown(test_acpigen_nested_ifs, setup_acpigen, teardown_acpigen), cmocka_unit_test_setup_teardown(test_acpigen_write_package, setup_acpigen, teardown_acpigen), cmocka_unit_test_setup_teardown(test_acpigen_scope_with_contents, setup_acpigen, teardown_acpigen), }; return cb_run_group_tests(tests, NULL, NULL); }