228 lines
5.8 KiB
C
228 lines
5.8 KiB
C
|
|
/*
|
|
* Copyright (c) 2012-2014 Wind River Systems, Inc.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* test static IDT APIs
|
|
* Ensures interrupt and exception stubs are installed correctly.
|
|
*/
|
|
|
|
#include <zephyr.h>
|
|
#include <tc_util.h>
|
|
#include <arch/x86/segmentation.h>
|
|
|
|
#include <kernel_structs.h>
|
|
#if defined(__GNUC__)
|
|
#include <test_asm_inline_gcc.h>
|
|
#else
|
|
#include <test_asm_inline_other.h>
|
|
#endif
|
|
|
|
/* These vectors are somewhat arbitrary. We try and use unused vectors */
|
|
#define TEST_SOFT_INT 60
|
|
#define TEST_SPUR_INT 61
|
|
|
|
|
|
#define MY_STACK_SIZE 2048
|
|
#define MY_PRIORITY 5
|
|
|
|
K_THREAD_STACK_DEFINE(my_stack_area, MY_STACK_SIZE);
|
|
static struct k_thread my_thread;
|
|
|
|
/* externs */
|
|
|
|
/* The _idt_base_address symbol is generated via a linker script */
|
|
|
|
extern unsigned char _idt_base_address[];
|
|
|
|
extern void *int_stub;
|
|
NANO_CPU_INT_REGISTER(int_stub, -1, -1, TEST_SOFT_INT, 0);
|
|
|
|
static volatile int exc_handler_executed;
|
|
static volatile int int_handler_executed;
|
|
/* Assume the spurious interrupt handler will execute and abort the task */
|
|
static volatile int spur_handler_aborted_thread = 1;
|
|
|
|
|
|
/**
|
|
* Handler to perform various actions from within an ISR context
|
|
*
|
|
* This routine is the ISR handler for _trigger_isr_handler().
|
|
*
|
|
* @return N/A
|
|
*/
|
|
|
|
void isr_handler(void)
|
|
{
|
|
int_handler_executed++;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* This is the handler for the divide by zero exception.
|
|
*
|
|
* The source of this divide-by-zero error comes from the following line in
|
|
* main() ...
|
|
* error = error / exc_handler_executed;
|
|
* Where exc_handler_executed is zero.
|
|
* The disassembled code for it looks something like ....
|
|
* f7 fb idiv %ecx
|
|
* This handler is part of a test that is only interested in detecting the
|
|
* error so that we know the exception connect code is working. Therefore,
|
|
* a very quick and dirty approach is taken for dealing with the exception;
|
|
* we skip the offending instruction by adding 2 to the EIP. (If nothing is
|
|
* done, then control goes back to the offending instruction and an infinite
|
|
* loop of divide-by-zero errors would be created.)
|
|
*
|
|
* @return N/A
|
|
*/
|
|
|
|
void exc_divide_error_handler(NANO_ESF *p_esf)
|
|
{
|
|
p_esf->eip += 2;
|
|
/* provide evidence that the handler executed */
|
|
exc_handler_executed = 1;
|
|
}
|
|
_EXCEPTION_CONNECT_NOCODE(exc_divide_error_handler, IV_DIVIDE_ERROR);
|
|
extern void *_EXCEPTION_STUB_NAME(exc_divide_error_handler, IV_DIVIDE_ERROR);
|
|
|
|
/**
|
|
* @brief Check the IDT.
|
|
*
|
|
* This test examines the IDT and verifies that the static interrupt and
|
|
* exception stubs are installed at the correct place.
|
|
*
|
|
* @return TC_PASS on success, TC_FAIL on failure
|
|
*/
|
|
|
|
int idt_stub_test(void)
|
|
{
|
|
struct segment_descriptor *p_idt_entry;
|
|
u32_t offset;
|
|
|
|
/* Check for the interrupt stub */
|
|
p_idt_entry = (struct segment_descriptor *)
|
|
(_idt_base_address + (TEST_SOFT_INT << 3));
|
|
offset = (u32_t)(&int_stub);
|
|
if (DTE_OFFSET(p_idt_entry) != offset) {
|
|
TC_ERROR("Failed to find offset of int_stub (0x%x) at vector %d\n",
|
|
offset, TEST_SOFT_INT);
|
|
return TC_FAIL;
|
|
}
|
|
|
|
/* Check for the exception stub */
|
|
p_idt_entry = (struct segment_descriptor *)
|
|
(_idt_base_address + (IV_DIVIDE_ERROR << 3));
|
|
offset = (u32_t)(&_EXCEPTION_STUB_NAME(exc_divide_error_handler, 0));
|
|
if (DTE_OFFSET(p_idt_entry) != offset) {
|
|
TC_ERROR("Failed to find offset of exc stub (0x%x) at vector %d\n",
|
|
offset, IV_DIVIDE_ERROR);
|
|
return TC_FAIL;
|
|
}
|
|
|
|
/*
|
|
* If the other fields are wrong, the system will crash when the exception
|
|
* and software interrupt are triggered so we don't check them.
|
|
*/
|
|
return TC_PASS;
|
|
}
|
|
|
|
/**
|
|
* @brief Task to test spurious handlers
|
|
*
|
|
* @return 0
|
|
*/
|
|
|
|
void idt_spur_task(void *arg1, void *arg2, void *arg3)
|
|
{
|
|
TC_PRINT("- Expect to see unhandled interrupt/exception message\n");
|
|
|
|
_trigger_spur_handler();
|
|
|
|
/* Shouldn't get here */
|
|
spur_handler_aborted_thread = 0;
|
|
|
|
}
|
|
|
|
/**
|
|
* @brief Entry point to static IDT tests
|
|
*
|
|
* This is the entry point to the static IDT tests.
|
|
*
|
|
* @return N/A
|
|
*/
|
|
|
|
void main(void)
|
|
{
|
|
int rv; /* return value from tests */
|
|
volatile int error; /* used to create a divide by zero error */
|
|
|
|
TC_START("Starting static IDT tests");
|
|
|
|
TC_PRINT("Testing to see if IDT has address of test stubs()\n");
|
|
rv = idt_stub_test();
|
|
if (rv != TC_PASS) {
|
|
goto done_tests;
|
|
}
|
|
|
|
TC_PRINT("Testing to see interrupt handler executes properly\n");
|
|
_trigger_isr_handler();
|
|
|
|
if (int_handler_executed == 0) {
|
|
TC_ERROR("Interrupt handler did not execute\n");
|
|
rv = TC_FAIL;
|
|
goto done_tests;
|
|
} else if (int_handler_executed != 1) {
|
|
TC_ERROR("Interrupt handler executed more than once! (%d)\n",
|
|
int_handler_executed);
|
|
rv = TC_FAIL;
|
|
goto done_tests;
|
|
}
|
|
|
|
TC_PRINT("Testing to see exception handler executes properly\n");
|
|
|
|
/*
|
|
* Use exc_handler_executed instead of 0 to prevent the compiler issuing a
|
|
* 'divide by zero' warning.
|
|
*/
|
|
error = 32; /* avoid static checker uninitialized warnings */
|
|
error = error / exc_handler_executed;
|
|
|
|
if (exc_handler_executed == 0) {
|
|
TC_ERROR("Exception handler did not execute\n");
|
|
rv = TC_FAIL;
|
|
goto done_tests;
|
|
} else if (exc_handler_executed != 1) {
|
|
TC_ERROR("Exception handler executed more than once! (%d)\n",
|
|
exc_handler_executed);
|
|
rv = TC_FAIL;
|
|
goto done_tests;
|
|
}
|
|
|
|
/*
|
|
* Start task to trigger the spurious interrupt handler
|
|
*/
|
|
TC_PRINT("Testing to see spurious handler executes properly\n");
|
|
k_thread_create(&my_thread, my_stack_area, MY_STACK_SIZE,
|
|
idt_spur_task, NULL, NULL, NULL,
|
|
MY_PRIORITY, 0, K_NO_WAIT);
|
|
|
|
/*
|
|
* The fiber/task should not run past where the spurious interrupt is
|
|
* generated. Therefore spur_handler_aborted_thread should remain at 1.
|
|
*/
|
|
if (spur_handler_aborted_thread == 0) {
|
|
TC_ERROR("Spurious handler did not execute as expected\n");
|
|
rv = TC_FAIL;
|
|
goto done_tests;
|
|
}
|
|
|
|
done_tests:
|
|
TC_END(rv, "%s - %s.\n", rv == TC_PASS ? PASS : FAIL, __func__);
|
|
TC_END_REPORT(rv);
|
|
}
|