Linux Audio

Check our new training course

Loading...
/*
 * Copyright (c) 2018 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

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

#if defined(CONFIG_DYNAMIC_INTERRUPTS)

#define ISR_DYN_ARG	0xab249cfd

static unsigned int handler_has_run;
static uintptr_t handler_test_result;

static void dyn_isr(const void *arg)
{
	ARG_UNUSED(arg);
	handler_test_result = (uintptr_t)arg;
	handler_has_run = 1;
}

#if defined(CONFIG_GEN_SW_ISR_TABLE)
extern struct _isr_table_entry __sw_isr_table _sw_isr_table[];
extern void z_irq_spurious(const void *unused);

/**
 * @brief Test dynamic ISR installation
 *
 * @ingroup kernel_interrupt_tests
 *
 * @details This routine locates an unused entry in the software ISR table,
 * installs a dynamic ISR to the unused entry by calling the dynamic
 * configured function, and verifies that the ISR is successfully installed
 * by checking the software ISR table entry.
 *
 * @see arch_irq_connect_dynamic()
 */
void test_isr_dynamic(void)
{
	int i;
	const void *argval;

	for (i = 0; i < (CONFIG_NUM_IRQS - CONFIG_GEN_IRQ_START_VECTOR); i++) {
		if (_sw_isr_table[i].isr == z_irq_spurious) {
			break;
		}
	}

	zassert_true(_sw_isr_table[i].isr == z_irq_spurious,
		     "could not find slot for dynamic isr");

	printk("installing dynamic ISR for IRQ %d\n",
	       CONFIG_GEN_IRQ_START_VECTOR + i);

	argval = (const void *)&i;
	arch_irq_connect_dynamic(i + CONFIG_GEN_IRQ_START_VECTOR, 0, dyn_isr,
				 argval, 0);

	zassert_true(_sw_isr_table[i].isr == dyn_isr &&
		     _sw_isr_table[i].arg == argval,
		     "dynamic isr did not install successfully");
}
#else
/*
 * For testing arch such as x86, x86_64 and posix which support dynamic
 * interrupt but without SW ISR table, we test it by applying for a
 * dynamic interrupt and then trigger it to check if happened correctly.
 */
#if defined(CONFIG_X86)
#define IV_IRQS 32	/* start of vectors available for x86 IRQs */

/* Using APIC TSC deadline timer will conflict with our testcase */
#if defined(CONFIG_APIC_TSC_DEADLINE_TIMER)
#define TEST_IRQ_DYN_LINE 17
#else
#define TEST_IRQ_DYN_LINE 16
#endif

#define TRIGGER_IRQ_DYN_LINE (TEST_IRQ_DYN_LINE + IV_IRQS)

#elif defined(CONFIG_ARCH_POSIX)
#define TEST_IRQ_DYN_LINE 5
#define TRIGGER_IRQ_DYN_LINE 5
#endif

void test_isr_dynamic(void)
{
	int vector_num;

	/**TESTPOINT: configuration of interrupts dynamically at runtime */
	vector_num = arch_irq_connect_dynamic(TEST_IRQ_DYN_LINE, 1, dyn_isr,
				 (void *)ISR_DYN_ARG, 0);

#if defined(CONFIG_X86_64)
/* The isr table for x86_64 is visiable, so check it up here */
extern void (*x86_irq_funcs[])(const void *);
extern const void *x86_irq_args[];

	zassert_true(x86_irq_funcs[TEST_IRQ_DYN_LINE] == dyn_isr &&
		     x86_irq_args[TEST_IRQ_DYN_LINE] == (void *)ISR_DYN_ARG,
		     "dynamic isr did not install successfully");
#endif

	TC_PRINT("vector(%d)\n", vector_num);
	zassert_true(vector_num > 0,
			"irq connect dynamic failed");

	/*
	 * The reason we need to hard code the trigger vector here
	 * is that the x86 only support immediate number for INT
	 * instruction. So trigger an interrupt of x86 under gcov code
	 * coverage report enabled, which means GCC optimization will
	 * be -O0. In this case, an build error happens and shows:
	 * "error: 'asm' operand 0 probably does not match constraints"
	 * and "error: impossible constraint in 'asm'"
	 *
	 * Although we hard code the trigger vecotr it here, we still
	 * do a check if the vector match getting from
	 * arch_irq_connect_dynamic().
	 */
	zassert_equal(vector_num, TRIGGER_IRQ_DYN_LINE,
			"vector %d mismatch we specified to trigger %d",
			vector_num, TRIGGER_IRQ_DYN_LINE);

	zassert_equal(handler_has_run, 0,
			"handler has run before interrupt trigger");

	irq_enable(TEST_IRQ_DYN_LINE);

	trigger_irq(TRIGGER_IRQ_DYN_LINE);

	zassert_equal(handler_has_run, 1,
			"interrupt triggered but handler has not run(%d)",
			handler_has_run);

	/**TESTPOINT: pass word-sized parameter to interrupt */
	zassert_equal(handler_test_result, ISR_DYN_ARG,
			"parameter(0x%lx) in handler is not correct",
			handler_test_result);

	irq_disable(TRIGGER_IRQ_DYN_LINE);

	/**TESTPOINT: interrupt cannot be triggered when disable it */
	zassert_equal(handler_has_run, 1,
			"interrupt handler should not be triggered again(%d)",
			handler_has_run);
}
#endif /* CONFIG_GEN_SW_ISR_TABLE */

#else
/* Skip the dynamic interrupt test for the platforms that do not support it */
void test_isr_dynamic(void)
{
	ztest_test_skip();
}
#endif /* CONFIG_DYNAMIC_INTERRUPTS */