Skip to content

Commit

Permalink
tests: kernel: interrupt: Add testcase for shared interrupts
Browse files Browse the repository at this point in the history
This commit introduces a new testcase for shared interrupts.

Signed-off-by: Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>
  • Loading branch information
Laurentiu Mihalcea committed Aug 14, 2023
1 parent ea2d675 commit d941bf9
Show file tree
Hide file tree
Showing 3 changed files with 347 additions and 0 deletions.
1 change: 1 addition & 0 deletions tests/kernel/interrupt/CMakeLists.txt
Expand Up @@ -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)
337 changes: 337 additions & 0 deletions tests/kernel/interrupt/src/shared_irq.c
@@ -0,0 +1,337 @@
/*
* Copyright 2023 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/ztest.h>
#include <zephyr/interrupt_util.h>

#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) {
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) {
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) {
return false;
}

if (_shared_irq_table[GIC_IRQ_LINE_1].client_num) {
return false;
}
if (_shared_irq_table[GIC_IRQ_LINE_2].client_num) {
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) {
return false;
}
if (_shared_irq_table[irq_line_1].client_num) {
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) {
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) {
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]) {
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 /* CONFIG_DYNAMIC_INTERRUPTS */
}
9 changes: 9 additions & 0 deletions tests/kernel/interrupt/testcase.yaml
Expand Up @@ -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

0 comments on commit d941bf9

Please sign in to comment.