blob: 7486e9f11df0134dff99b1cb554af2526be70705 [file] [log] [blame]
/* static_idt.c - test static IDT APIs */
/*
* Copyright (c) 2012-2014 Wind River Systems, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
DESCRIPTION
Ensures interrupt and exception stubs are installed correctly.
*/
#include <zephyr.h>
#include <tc_util.h>
#include <nano_private.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 62
#define TEST_SPUR_INT 63
/* externs */
/* the _idt_base_address symbol is generated via a linker script */
extern unsigned char _idt_base_address[];
extern void *nanoIntStub;
extern void *exc_divide_error_handlerStub;
NANO_CPU_INT_REGISTER(nanoIntStub, -1, -1, TEST_SOFT_INT, 0);
static volatile int excHandlerExecuted;
static volatile int intHandlerExecuted;
/* Assume the spurious interrupt handler will execute and abort the task/fiber */
static volatile int spurHandlerAbortedThread = 1;
#ifdef CONFIG_NANOKERNEL
static char __stack fiberStack[512];
#endif
/**
*
* isr_handler - handler to perform various actions from within an ISR context
*
* This routine is the ISR handler for _trigger_isrHandler().
*
* @return N/A
*/
void isr_handler(void)
{
intHandlerExecuted++;
}
/**
*
* exc_divide_error_handler -
*
* This is the handler for the divde by zero exception. The source of this
* divide-by-zero error comes from the following line in main() ...
* error = error / excHandlerExecuted;
* Where excHandlerExecuted 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 *pEsf)
{
pEsf->eip += 2;
excHandlerExecuted = 1; /* provide evidence that the handler executed */
}
_EXCEPTION_CONNECT_NOCODE(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 nanoIdtStubTest(void)
{
IDT_ENTRY *pIdtEntry;
uint16_t offset;
/* Check for the interrupt stub */
pIdtEntry = (IDT_ENTRY *) (_idt_base_address + (TEST_SOFT_INT << 3));
offset = (uint16_t)((uint32_t)(&nanoIntStub) & 0xFFFF);
if (pIdtEntry->offset_low != offset) {
TC_ERROR("Failed to find low offset of nanoIntStub "
"(0x%x) at vector %d\n", (uint32_t)offset, TEST_SOFT_INT);
return TC_FAIL;
}
offset = (uint16_t)((uint32_t)(&nanoIntStub) >> 16);
if (pIdtEntry->offset_high != offset) {
TC_ERROR("Failed to find high offset of nanoIntStub "
"(0x%x) at vector %d\n", (uint32_t)offset, TEST_SOFT_INT);
return TC_FAIL;
}
/* Check for the exception stub */
pIdtEntry = (IDT_ENTRY *) (_idt_base_address + (IV_DIVIDE_ERROR << 3));
offset = (uint16_t)((uint32_t)(&exc_divide_error_handlerStub) & 0xFFFF);
if (pIdtEntry->offset_low != offset) {
TC_ERROR("Failed to find low offset of exc stub "
"(0x%x) at vector %d\n", (uint32_t)offset, IV_DIVIDE_ERROR);
return TC_FAIL;
}
offset = (uint16_t)((uint32_t)(&exc_divide_error_handlerStub) >> 16);
if (pIdtEntry->offset_high != offset) {
TC_ERROR("Failed to find high offset of exc stub "
"(0x%x) at vector %d\n", (uint32_t)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/fiber to test spurious handlers
*
* @return 0
*/
#ifdef CONFIG_MICROKERNEL
void idtSpurTask(void)
#else
static void idtSpurFiber(int a1, int a2)
#endif
{
#ifndef CONFIG_MICROKERNEL
ARG_UNUSED(a1);
ARG_UNUSED(a2);
#endif /* !CONFIG_MICROKERNEL */
TC_PRINT("- Expect to see unhandled interrupt/exception message\n");
_trigger_spurHandler();
/* Shouldn't get here */
spurHandlerAbortedThread = 0;
}
/**
*
* @brief Entry point to static IDT tests
*
* This is the entry point to the static IDT tests.
*
* @return N/A
*/
#ifdef CONFIG_MICROKERNEL
void idtTestTask(void)
#else
void main(void)
#endif
{
int rv; /* return value from tests */
volatile int error; /* used to create a divide by zero error */
TC_START("Test Nanokernel static IDT tests");
TC_PRINT("Testing to see if IDT has address of test stubs()\n");
rv = nanoIdtStubTest();
if (rv != TC_PASS) {
goto doneTests;
}
TC_PRINT("Testing to see interrupt handler executes properly\n");
_trigger_isrHandler();
if (intHandlerExecuted == 0) {
TC_ERROR("Interrupt handler did not execute\n");
rv = TC_FAIL;
goto doneTests;
} else if (intHandlerExecuted != 1) {
TC_ERROR("Interrupt handler executed more than once! (%d)\n",
intHandlerExecuted);
rv = TC_FAIL;
goto doneTests;
}
TC_PRINT("Testing to see exception handler executes properly\n");
/*
* Use excHandlerExecuted instead of 0 to prevent the compiler issuing a
* 'divide by zero' warning.
*/
error = error / excHandlerExecuted;
if (excHandlerExecuted == 0) {
TC_ERROR("Exception handler did not execute\n");
rv = TC_FAIL;
goto doneTests;
} else if (excHandlerExecuted != 1) {
TC_ERROR("Exception handler executed more than once! (%d)\n",
excHandlerExecuted);
rv = TC_FAIL;
goto doneTests;
}
/*
* Start fiber/task to trigger the spurious interrupt handler
*/
TC_PRINT("Testing to see spurious handler executes properly\n");
#ifdef CONFIG_MICROKERNEL
task_start(tSpurTask);
#else
task_fiber_start(fiberStack, sizeof(fiberStack), idtSpurFiber, 0, 0, 5, 0);
#endif
/*
* The fiber/task should not run past where the spurious interrupt is
* generated. Therefore spurHandlerAbortedThread should remain at 1.
*/
if (spurHandlerAbortedThread == 0) {
TC_ERROR("Spurious handler did not execute as expected\n");
rv = TC_FAIL;
goto doneTests;
}
doneTests:
TC_END(rv, "%s - %s.\n", rv == TC_PASS ? PASS : FAIL, __func__);
TC_END_REPORT(rv);
}