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 <[email protected]>
  • Loading branch information
LaurentiuM1234 committed Aug 31, 2023
1 parent 12138ae commit fd9719d
Show file tree
Hide file tree
Showing 5 changed files with 395 additions and 0 deletions.
11 changes: 11 additions & 0 deletions tests/kernel/interrupt/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,14 @@ 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)

if (CONFIG_SHARED_INTERRUPTS)
# note: this test is very brittle since it requires that the chosen
# interrupt lines are unused for all of the testing platforms. Failing
# to meet this requirement would lead to build failures due to the number
# of clients exceeding the limit. Still, it's important to test that the
# static shared interrupts work properly. As such, restrict it to a single
# platform so that we can overcome this problem.
target_sources_ifdef(CONFIG_BOARD_QEMU_CORTEX_A53 app PRIVATE src/static_shared_irq.c)
target_sources_ifdef(CONFIG_DYNAMIC_INTERRUPTS app PRIVATE src/dynamic_shared_irq.c)
endif()
227 changes: 227 additions & 0 deletions tests/kernel/interrupt/src/dynamic_shared_irq.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
/*
* Copyright 2023 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/

#include "test_shared_irq.h"

struct shared_irq_fixture {
unsigned int irq1;
unsigned int irq2;
unsigned int irq1_table_idx;
unsigned int irq2_table_idx;
unsigned int irq_priority;
};

static struct shared_irq_fixture fixture;

static void reset_test_vector(void)
{
int i;

for (i = 0; i < TEST_VECTOR_SIZE; i++) {
test_vector[i] = 0;
}
}

static void dynamic_shared_irq_suite_after(void *data)
{
ARG_UNUSED(data);

/* note: no need to check the state of the SW ISR tables after
* all these disconnect operations. If there's something wrong
* it should be detected by dynamic_shared_irq_suite_before().
*/
arch_irq_disconnect_dynamic(fixture.irq1, fixture.irq_priority,
test_isr_0, 0, 0);
arch_irq_disconnect_dynamic(fixture.irq1, fixture.irq_priority,
test_isr_1, (void *)1, 0);
arch_irq_disconnect_dynamic(fixture.irq2, fixture.irq_priority,
test_isr_2, (void *)2, 0);
}

static void dummy_isr(const void *data)
{
ARG_UNUSED(data);

test_vector[0] = TEST_DUMMY_ISR_VAL;
}

static unsigned int get_irq_slot(unsigned int start)
{
unsigned int i, table_idx;

for (i = start; i <= CONFIG_GEN_IRQ_START_VECTOR + CONFIG_NUM_IRQS - 1; i++) {
table_idx = i - CONFIG_GEN_IRQ_START_VECTOR;

if (_sw_isr_table[table_idx].isr == &z_irq_spurious) {
test_vector[0] = 0;

/* check to see if we can trigger this IRQ */
arch_irq_connect_dynamic(i, IRQ_PRIORITY, dummy_isr,
NULL, 0);
irq_enable(i);
trigger_irq(i);

/* wait a bit */
k_busy_wait(100);

if (test_vector[0] == TEST_DUMMY_ISR_VAL) {
/* found a valid INTID */
irq_disable(i);

arch_irq_disconnect_dynamic(i, IRQ_PRIORITY,
dummy_isr, NULL, 0);
return i;
}
}
}

return TEST_INVALID_IRQ;
}

static void *dynamic_shared_irq_suite_setup(void)
{
fixture.irq1 = get_irq_slot(CONFIG_GEN_IRQ_START_VECTOR);
zassert_true(fixture.irq1 != TEST_INVALID_IRQ,
"no suitable value found for irq1");
fixture.irq2 = get_irq_slot(fixture.irq1 + 1);
zassert_true(fixture.irq2 != TEST_INVALID_IRQ,
"no suitable value found for irq2");
fixture.irq_priority = IRQ_PRIORITY;

fixture.irq1_table_idx = fixture.irq1 - CONFIG_GEN_IRQ_START_VECTOR;
fixture.irq2_table_idx = fixture.irq2 - CONFIG_GEN_IRQ_START_VECTOR;

return NULL;
}

static void dynamic_shared_irq_suite_before(void *data)
{
ARG_UNUSED(data);

arch_irq_connect_dynamic(fixture.irq1, fixture.irq_priority,
test_isr_0, 0, 0);

zassert_true(_sw_isr_table[fixture.irq1_table_idx].isr == test_isr_0,
"wrong _sw_isr_table ISR at irq1");
zassert_true(!_sw_isr_table[fixture.irq1_table_idx].arg,
"wrong _sw_isr_table argument at irq1");
zassert_true(!_shared_sw_isr_table[fixture.irq1_table_idx].client_num,
"wrong client number at irq1");

arch_irq_connect_dynamic(fixture.irq1, fixture.irq_priority,
test_isr_1, (void *)1, 0);

zassert_true(_sw_isr_table[fixture.irq1_table_idx].isr == shared_isr,
"wrong _sw_isr_table ISR at irq1");
zassert_true(_sw_isr_table[fixture.irq1_table_idx].arg ==
&_shared_sw_isr_table[fixture.irq1_table_idx],
"wrong _sw_isr_table argument at irq1");
zassert_true(_shared_sw_isr_table[fixture.irq1_table_idx].client_num == 2,
"wrong client number at irq1");

zassert_true(client_exists_at_index(test_isr_0, 0, fixture.irq1_table_idx, 0),
"unexpected client data for irq1, index 0");
zassert_true(client_exists_at_index(test_isr_1, (void *)1, fixture.irq1_table_idx, 1),
"unexpected client data for irq1, index 1");

arch_irq_connect_dynamic(fixture.irq2, fixture.irq_priority,
test_isr_2, (void *)2, 0);

zassert_true(_sw_isr_table[fixture.irq2_table_idx].isr == test_isr_2,
"wrong _sw_isr_table ISR at irq2");
zassert_true(_sw_isr_table[fixture.irq2_table_idx].arg == (void *)2,
"wrong _sw_isr_table argument at irq2");
zassert_true(!_shared_sw_isr_table[fixture.irq2_table_idx].client_num,
"wrong client number at irq2");

reset_test_vector();
}

/**
* @brief Test writing to a vector with a shared interrupt
*
* @ingroup kernel_interrupt_tests
*
* @details This tests if interrupts are dynamically shared successfully
* (i.e: multiple ISR/arg pairs are called whenever the interrupt
* they were registered for is triggered).
*/
ZTEST(shared_irq_feature, test_dynamic_shared_irq_write)
{
int i;

irq_enable(fixture.irq1);
irq_enable(fixture.irq2);

trigger_irq(fixture.irq1);
trigger_irq(fixture.irq2);

/* wait 5ms before checking the results */
k_busy_wait(5000);

for (i = 0; i < TEST_VECTOR_SIZE; i++) {
zassert_true(test_vector[i] == result_vector[i],
"wrong test_vector value at %d: 0x%x vs 0x%x",
i, test_vector[i], result_vector[i]);
}

irq_disable(fixture.irq1);
irq_disable(fixture.irq2);
}

/**
* @brief Test writing to a vector after an ISR/arg disconnect.
*
* @ingroup kernel_interrupt_tests
*
* @details This tests if ISR/arg pairs are disconnected successfully
* and the interrupts are "unshared" whenever a single ISR/arg pair is
* left.
*/
ZTEST(shared_irq_feature, test_dynamic_shared_irq_disconnect_write)
{
int i;

/* remove test_isr_0/NULL pair. After this statement we expect
* irq1 to be unshared.
*/
arch_irq_disconnect_dynamic(fixture.irq1, fixture.irq_priority,
test_isr_0, 0, 0);

zassert_true(_sw_isr_table[fixture.irq1_table_idx].isr == test_isr_1,
"wrong _sw_isr_table ISR at irq1");
zassert_true(_sw_isr_table[fixture.irq1_table_idx].arg == (void *)1,
"wrong _sw_isr_table arg at irq1");
zassert_true(!_shared_sw_isr_table[fixture.irq1_table_idx].client_num,
"wrong client number at irq1");

irq_enable(fixture.irq1);
trigger_irq(fixture.irq1);

/* wait 5ms before checking the results */
k_busy_wait(5000);

for (i = 0; i < TEST_VECTOR_SIZE; i++) {
if (i == 1) {
zassert_true(test_vector[i] == result_vector[i],
"wrong test_vector at %d: 0x%x vs 0x%x",
i, test_vector[i], result_vector[i]);
continue;
}

zassert_true(!test_vector[i],
"wrong test_vector value at %d: 0x%x vs 0x%x",
i, test_vector[i], result_vector[i]);
}

irq_disable(fixture.irq1);
}

ZTEST_SUITE(shared_irq_feature, NULL,
dynamic_shared_irq_suite_setup,
dynamic_shared_irq_suite_before,
dynamic_shared_irq_suite_after,
NULL);
75 changes: 75 additions & 0 deletions tests/kernel/interrupt/src/static_shared_irq.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright 2023 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/

#include "test_shared_irq.h"

#define GIC_IRQ1 10
#define GIC_IRQ2 11

/**
* @brief Test writing to a vector using static shared interrupts.
*
* @ingroup kernel_interrupt_tests
*
* @details This tests if interrupts are statically shared successfully
* (i.e: multiple ISR/arg pairs are called whenever the interrupt they
* were registered for is triggered).
*/
ZTEST(interrupt_feature, test_static_shared_irq_write)
{
int i;

IRQ_CONNECT(GIC_IRQ1, IRQ_PRIORITY, test_isr_0, 0, 0);
IRQ_CONNECT(GIC_IRQ1, IRQ_PRIORITY, test_isr_1, (void *)1, 0);
IRQ_CONNECT(GIC_IRQ2, IRQ_PRIORITY, test_isr_2, (void *)2, 0);

zassert_true(_sw_isr_table[GIC_IRQ1].isr == shared_isr,
"wrong _sw_isr_table ISR at GIC_IRQ1");
zassert_true(_sw_isr_table[GIC_IRQ2].isr == test_isr_2,
"wrong _sw_isr_table ISR at GIC_IRQ1");

zassert_true(_sw_isr_table[GIC_IRQ1].arg ==
&_shared_sw_isr_table[GIC_IRQ1],
"wrong _sw_isr_table arg at GIC_IRQ1");
zassert_true(_sw_isr_table[GIC_IRQ2].arg == (void *)2,
"wrong _sw_isr_table arg at GIC_IRQ2");

zassert_true(_shared_sw_isr_table[GIC_IRQ1].client_num == 2,
"wrong client number for GIC_IRQ1");
zassert_true(!_shared_sw_isr_table[GIC_IRQ2].client_num,
"wrong client number for GIC_IRQ2");

zassert_true(client_exists_at_index(test_isr_0, 0, GIC_IRQ1,
TEST_INVALID_IDX),
"test_isr_0 not a client for GIC_IRQ1");
zassert_true(client_exists_at_index(test_isr_1, (void *)1, GIC_IRQ1,
TEST_INVALID_IDX),
"test_isr_1 not a client for GIC_IRQ1");

irq_enable(GIC_IRQ1);
irq_enable(GIC_IRQ2);

trigger_irq(GIC_IRQ1);
trigger_irq(GIC_IRQ2);

/* wait 5ms before checking the results */
k_busy_wait(5000);

for (i = 0; i < TEST_VECTOR_SIZE; i++) {
zassert_true(test_vector[i] == result_vector[i],
"wrong test_vector value at %d: 0x%x vs 0x%x",
i, test_vector[i], result_vector[i]);
}

irq_disable(GIC_IRQ1);
irq_disable(GIC_IRQ2);

#ifdef CONFIG_DYNAMIC_INTERRUPTS
arch_irq_disconnect_dynamic(GIC_IRQ1, IRQ_PRIORITY, test_isr_0, 0, 0);
arch_irq_disconnect_dynamic(GIC_IRQ1, IRQ_PRIORITY, test_isr_1, (void *)1, 0);
arch_irq_disconnect_dynamic(GIC_IRQ2, IRQ_PRIORITY, test_isr_2, (void *)2, 0);
#endif /* CONFIG_DYNAMIC_INTERRUPTS */
}
66 changes: 66 additions & 0 deletions tests/kernel/interrupt/src/test_shared_irq.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright 2023 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef __TEST_SHARED_IRQ_H__
#define __TEST_SHARED_IRQ_H__

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

#define IRQ_PRIORITY 1
#define TEST_VECTOR_SIZE 10
#define TEST_INVALID_IDX 0xcafebabe
#define TEST_DUMMY_ISR_VAL 0xdeadbeef
#define TEST_INVALID_IRQ 0xcafebabe

#define ISR_DEFINE(name) \
static inline 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);

static inline bool client_exists_at_index(void (*routine)(const void *arg),
void *arg, int irq, size_t idx)
{
size_t i;
struct shared_irq_data *irq_data;

if (idx == TEST_INVALID_IDX) {
irq_data = &_shared_sw_isr_table[irq];

for (i = 0; i < irq_data->client_num; i++) {
if (irq_data->clients[i].isr == routine &&
irq_data->clients[i].arg == arg) {
return true;
}
}
} else {
if (_shared_sw_isr_table[irq].client_num <= idx) {
return false;
}

return _shared_sw_isr_table[irq].clients[idx].isr == routine &&
_shared_sw_isr_table[irq].clients[idx].arg == arg;
}

return false;
}

#endif /* __TEST_SHARED_IRQ_H__ */
Loading

0 comments on commit fd9719d

Please sign in to comment.