From 315465ed70a302978f3cf740dbd1c0880ab6ec7b Mon Sep 17 00:00:00 2001 From: Laurentiu Mihalcea Date: Fri, 11 Aug 2023 15:41:51 +0300 Subject: [PATCH] tests: kernel: interrupt: Add testcase for shared interrupts This commit introduces a new testcase for shared interrupts. Signed-off-by: Laurentiu Mihalcea --- tests/kernel/interrupt/CMakeLists.txt | 1 + tests/kernel/interrupt/src/shared_irq.c | 307 ++++++++++++++++++++++++ tests/kernel/interrupt/testcase.yaml | 9 + 3 files changed, 317 insertions(+) create mode 100644 tests/kernel/interrupt/src/shared_irq.c diff --git a/tests/kernel/interrupt/CMakeLists.txt b/tests/kernel/interrupt/CMakeLists.txt index 20851bb5ff41912..998bfe52926ad26 100644 --- a/tests/kernel/interrupt/CMakeLists.txt +++ b/tests/kernel/interrupt/CMakeLists.txt @@ -17,3 +17,4 @@ target_sources(app PRIVATE target_sources_ifdef(CONFIG_DYNAMIC_INTERRUPTS app PRIVATE src/dynamic_isr.c) target_sources_ifdef(CONFIG_X86 app PRIVATE src/regular_isr.c) +target_sources_ifdef(CONFIG_SHARED_INTERRUPTS app PRIVATE src/shared_irq.c) diff --git a/tests/kernel/interrupt/src/shared_irq.c b/tests/kernel/interrupt/src/shared_irq.c new file mode 100644 index 000000000000000..78846e0f36e6db2 --- /dev/null +++ b/tests/kernel/interrupt/src/shared_irq.c @@ -0,0 +1,307 @@ +/* + * Copyright 2023 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#define GIC_IRQ_LINE_1 10 +#define GIC_IRQ_LINE_2 11 +#define GIC_IRQ_PRIORITY 0 +#define TEST_VECTOR_SIZE 10 +#define TEST_INVALID_INDEX -1 + +#define ISR_DEFINE(name) \ +static void name(const void *data) \ +{ \ + int idx = POINTER_TO_INT(data); \ + test_vector[idx] = result_vector[idx]; \ +} \ + +static uint32_t test_vector[TEST_VECTOR_SIZE] = { +}; + +static uint32_t result_vector[TEST_VECTOR_SIZE] = { + 0xdeadbeef, + 0xcafebabe, + 0x1234cafe, +}; + +ISR_DEFINE(test_isr_0); +ISR_DEFINE(test_isr_1); +ISR_DEFINE(test_isr_2); + +/* utility function - checks if client ISR/arg found at index idx matches given + * pair. If passed idx is TEST_INVALID_IDX then this function looks through the + * list of all clients for given ISR/arg pair. + */ +static bool client_exists_at_index(void (*routine)(const void *arg), void *arg, int irq, int idx) +{ + int i; + struct shared_irq_data *irq_data; + + if (idx == TEST_INVALID_INDEX) { + irq_data = &_shared_irq_table[irq]; + + for (i = 0; i < irq_data->client_num; i++) { + if (irq_data->clients[i].routine == routine && irq_data->clients[i].arg == arg) + return true; + } + } else { + if (_shared_irq_table[irq].client_num <= idx) + return false; + + return _shared_irq_table[irq].clients[idx].routine == routine && + _shared_irq_table[irq].clients[idx].arg == arg; + } + + return false; +} + +static void reset_test_vector(void) +{ + int i; + + for (i = 0; i < TEST_VECTOR_SIZE; i++) + test_vector[i] = 0; +} + +#ifdef CONFIG_GIC +static bool static_shared_irq_test(void) +{ + int i; + + IRQ_CONNECT(GIC_IRQ_LINE_1, GIC_IRQ_PRIORITY, test_isr_0, 0, 0); + IRQ_CONNECT(GIC_IRQ_LINE_1, GIC_IRQ_PRIORITY, test_isr_1, (void *)1, 0); + IRQ_CONNECT(GIC_IRQ_LINE_2, GIC_IRQ_PRIORITY, test_isr_2, (void *)2, 0); + + /* note: the order in which the ISR/arg pairs appear in the list of clients + * depends on gen_isr_tables.py. As such, we don't care about the order ATM. + * + * Instead, the order will be verified in the case of dynamic interrupts since + * the algorithm from shared_irq.c will register clients in the order in which + * arch_irq_connect_dynamic() was called. + */ + if (!client_exists_at_index(test_isr_0, 0, GIC_IRQ_LINE_1, TEST_INVALID_INDEX)) + return false; + + if (!client_exists_at_index(test_isr_1, (void *)1, GIC_IRQ_LINE_1, TEST_INVALID_INDEX)) + return false; + + /* GIC_IRQ_LINE_2 is not shared so we expect the ISR field from + * _sw_isr_table to remain unmodified. + */ + if (_sw_isr_table[GIC_IRQ_LINE_2].isr != test_isr_2) + return false; + + /* since GIC_IRQ_LINE_1 is being shared, we expect the ISR field + * from _sw_isr_table to be shared_isr. + */ + if (_sw_isr_table[GIC_IRQ_LINE_1].isr != shared_isr) + return false; + + /* the number of clients for GIC_IRQ_LINE_1 should be 2, while + * the number of clients for GIC_IRQ_LINE_2 should be 0 (since it's + * not being shared). + */ + if (_shared_irq_table[GIC_IRQ_LINE_1].client_num != 2) + return false; + if (_shared_irq_table[GIC_IRQ_LINE_2].client_num != 0) + return false; + + irq_enable(GIC_IRQ_LINE_1); + irq_enable(GIC_IRQ_LINE_2); + + trigger_irq(GIC_IRQ_LINE_1); + trigger_irq(GIC_IRQ_LINE_2); + + /* wait 5ms before checking the results */ + k_busy_wait(5000); + + for (i = 0; i < TEST_VECTOR_SIZE; i++) { + if (test_vector[i] != result_vector[i]) + return false; + } + + irq_disable(GIC_IRQ_LINE_1); + irq_disable(GIC_IRQ_LINE_2); + + reset_test_vector(); + + /* pass */ + return true; +} +#endif /* CONFIG_GIC */ + +/* note: this function is used to put _sw_isr_table and _shared_irq_table + * into a known state. + */ +static bool reset_irq_table_state_test(void) +{ + /* at the end of these statements, _sw_isr_table and _shared_irq_table should + * have the following states: + * + * _sw_isr_table[GIC_IRQ_LINE_1].isr = z_irq_spurious + * _sw_isr_table[GIC_IRQ_LINE_1].arg = NULL + * _sw_isr_table[GIC_IRQ_LINE_2].isr = z_irq_spurious + * _sw_isr_table[GIC_IRQ_LINE_2].arg = NULL + * + * _shared_irq_table[GIC_IRQ_LINE_1].client_num = 0 + * _shared_irq_table[GIC_IRQ_LINE_2].client_num = 0 + */ + arch_irq_disconnect_dynamic(GIC_IRQ_LINE_1, GIC_IRQ_PRIORITY, test_isr_0, 0, 0); + arch_irq_disconnect_dynamic(GIC_IRQ_LINE_1, GIC_IRQ_PRIORITY, test_isr_1, (void *)1, 0); + arch_irq_disconnect_dynamic(GIC_IRQ_LINE_2, GIC_IRQ_PRIORITY, test_isr_2, (void *)2, 0); + + if (_sw_isr_table[GIC_IRQ_LINE_1].isr != z_irq_spurious) + return false; + if (_sw_isr_table[GIC_IRQ_LINE_1].arg != NULL) + return false; + + if (_sw_isr_table[GIC_IRQ_LINE_2].isr != z_irq_spurious) + return false; + if (_sw_isr_table[GIC_IRQ_LINE_2].arg != NULL) + return false; + + if (_shared_irq_table[GIC_IRQ_LINE_1].client_num != 0) + return false; + if (_shared_irq_table[GIC_IRQ_LINE_2].client_num != 0) + return false; + + /* pass */ + return true; +} + +static bool dynamic_shared_irq_test(void) +{ + int irq_line_1, irq_line_2, irq_priority, i; + +#if defined(CONFIG_GIC) + irq_line_1 = GIC_IRQ_LINE_1; + irq_line_2 = GIC_IRQ_LINE_2; + irq_priority = GIC_IRQ_PRIORITY; +#elif defined(CONFIG_CPU_CORTEX_M) + irq_line_1 = get_available_nvic_line(CONFIG_NUM_IRQS); + irq_line_2 = get_available_nvic_line(irq_line_1); + irq_priority = 1; +#else +#error "Unsupported architecture." +#endif + + arch_irq_connect_dynamic(irq_line_1, irq_priority, test_isr_0, 0, 0); + + if (_sw_isr_table[irq_line_1].isr != test_isr_0) + return false; + if (_sw_isr_table[irq_line_1].arg != NULL) + return false; + if (_shared_irq_table[irq_line_1].client_num != 0) + return false; + + arch_irq_connect_dynamic(irq_line_1, irq_priority, test_isr_1, (void *)1, 0); + + if (_sw_isr_table[irq_line_1].isr != shared_isr) + return false; + if (_sw_isr_table[irq_line_1].arg != &_shared_irq_table[irq_line_1]) + return false; + if (_shared_irq_table[irq_line_1].client_num != 2) + return false; + + /* here it's mandatory that test_isr_0/NULL is the first client, while + * test_isr_1/1 is the second client. + */ + if (!client_exists_at_index(test_isr_0, 0, irq_line_1, 0)) + return 0; + if (!client_exists_at_index(test_isr_1, (void *)1, irq_line_1, 1)) + return 0; + + arch_irq_connect_dynamic(irq_line_2, irq_priority, test_isr_2, (void *)2, 0); + + if (_sw_isr_table[irq_line_2].isr != test_isr_2) + return false; + if (_sw_isr_table[irq_line_2].arg != (void *)2) + return false; + if (_shared_irq_table[irq_line_2].client_num != 0) + return false; + + irq_enable(irq_line_1); + irq_enable(irq_line_2); + + trigger_irq(irq_line_1); + trigger_irq(irq_line_2); + + /* wait 5ms before checking the results */ + k_busy_wait(5000); + + for (i = 0; i < TEST_VECTOR_SIZE; i++) { + if (test_vector[i] != result_vector[i]) + return false; + } + + irq_disable(irq_line_1); + irq_disable(irq_line_2); + + reset_test_vector(); + + /* remove test_isr_0/NULL pair. After this statement we expect + * irq_line_1 to be unshared. + */ + arch_irq_disconnect_dynamic(irq_line_1, irq_priority, test_isr_0, 0, 0); + + if (_sw_isr_table[irq_line_1].isr != test_isr_1) + return false; + if (_sw_isr_table[irq_line_1].arg != (void *)1) + return false; + if (_shared_irq_table[irq_line_1].client_num != 0) + return false; + + irq_enable(irq_line_1); + trigger_irq(irq_line_1); + + /* wait 5ms before checking the results */ + k_busy_wait(5000); + + for (i = 0; i < TEST_VECTOR_SIZE; i++) { + if (i == 1) { + if (test_vector[i] != result_vector[i]) + return false; + continue; + } + + if (test_vector[i] != 0) + return false; + } + + irq_disable(irq_line_1); + + reset_test_vector(); + + /* pass */ + return true; +} + +ZTEST(interrupt_feature, test_shared_irq) +{ + /* TODO: make this test work on other architectures as well. + * What are the equivalents of the SGIs on the other architectures? + */ +#ifdef CONFIG_GIC + zassert_true(static_shared_irq_test(), + "failed static shared irq test."); +#endif /* CONFIG_GIC */ + +#ifdef CONFIG_DYNAMIC_INTERRUPTS + +#ifdef CONFIG_GIC + zassert_true(reset_irq_table_state_test(), + "failed reset table state test"); +#endif /* CONFIG_GIC */ + + zassert_true(dynamic_shared_irq_test(), + "failed dynamic irq test."); + + zassert_true(reset_irq_table_state_test(), + "failed reset table state test"); +#endif +} diff --git a/tests/kernel/interrupt/testcase.yaml b/tests/kernel/interrupt/testcase.yaml index ba94c20b8b449c1..87d18432a1b50ce 100644 --- a/tests/kernel/interrupt/testcase.yaml +++ b/tests/kernel/interrupt/testcase.yaml @@ -6,3 +6,12 @@ tests: - kernel - interrupt filter: not CONFIG_TRUSTED_EXECUTION_NONSECURE + arch.shared_interrupt: + arch_allow: + - arm64 + - arm + tags: + - kernel + - interrupt + extra_configs: + - CONFIG_SHARED_INTERRUPTS=y