| /* 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); |
| } |