1 /*
2  * Copyright (c) 2012-2014 Wind River Systems, Inc.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  * test static IDT APIs
10  *  Ensures interrupt and exception stubs are installed correctly.
11  */
12 
13 #include <zephyr/kernel.h>
14 #include <zephyr/ztest.h>
15 #include <zephyr/tc_util.h>
16 #include <zephyr/arch/x86/ia32/segmentation.h>
17 
18 #include <kernel_internal.h>
19 #if defined(__GNUC__)
20 #include "test_asm_inline_gcc.h"
21 #else
22 #include "test_asm_inline_other.h"
23 #endif
24 
25 /* These vectors are somewhat arbitrary. We try and use unused vectors */
26 #define TEST_SOFT_INT 60
27 #define TEST_SPUR_INT 61
28 
29 
30 #define MY_STACK_SIZE 2048
31 #define MY_PRIORITY 5
32 
33 K_THREAD_STACK_DEFINE(my_stack_area, MY_STACK_SIZE);
34 static struct k_thread my_thread;
35 
36 /* externs */
37 
38 /* The _idt_base_address symbol is generated via a linker script */
39 
40 extern unsigned char _idt_base_address[];
41 
42 extern void *int_stub;
43 NANO_CPU_INT_REGISTER(int_stub, -1, -1, TEST_SOFT_INT, 0);
44 
45 static volatile int exc_handler_executed;
46 static volatile int int_handler_executed;
47 /* Assume the spurious interrupt handler will execute and abort the task */
48 static volatile int spur_handler_aborted_thread = 1;
49 
k_sys_fatal_error_handler(unsigned int reason,const z_arch_esf_t * esf)50 void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *esf)
51 {
52 	if (reason != K_ERR_SPURIOUS_IRQ) {
53 		printk("wrong error reason\n");
54 		k_fatal_halt(reason);
55 	}
56 	if (k_current_get() != &my_thread) {
57 		printk("wrong thread crashed\n");
58 		k_fatal_halt(reason);
59 	}
60 }
61 
62 /**
63  * Handler to perform various actions from within an ISR context
64  *
65  * This routine is the ISR handler for _trigger_isr_handler().
66  */
67 
isr_handler(void)68 void isr_handler(void)
69 {
70 	int_handler_executed++;
71 }
72 
73 /**
74  *
75  * This is the handler for the divide by zero exception.
76  *
77  * The source of this divide-by-zero error comes from the following line in
78  * main() ...
79  *         error = error / exc_handler_executed;
80  * Where exc_handler_executed is zero.
81  * The disassembled code for it looks something like ....
82  *         f7 fb                   idiv   %ecx
83  * This handler is part of a test that is only interested in detecting the
84  * error so that we know the exception connect code is working.  Therefore,
85  * a very quick and dirty approach is taken for dealing with the exception;
86  * we skip the offending instruction by adding 2 to the EIP.  (If nothing is
87  * done, then control goes back to the offending instruction and an infinite
88  * loop of divide-by-zero errors would be created.)
89  *
90  */
91 
exc_divide_error_handler(z_arch_esf_t * p_esf)92 void exc_divide_error_handler(z_arch_esf_t *p_esf)
93 {
94 	p_esf->eip += 2;
95 	/* provide evidence that the handler executed */
96 	exc_handler_executed = 1;
97 }
98 _EXCEPTION_CONNECT_NOCODE(exc_divide_error_handler, IV_DIVIDE_ERROR, 0);
99 extern void *_EXCEPTION_STUB_NAME(exc_divide_error_handler, IV_DIVIDE_ERROR);
100 
101 /**
102  * @ingroup kernel_interrupt_tests
103  * @brief Test the position of interrupt stubs in IDT
104  *
105  * @details This test examines the IDT and verifies that the static interrupt
106  * and exception stubs are installed at the correct place.
107  *
108  */
ZTEST(static_idt,test_idt_stub)109 ZTEST(static_idt, test_idt_stub)
110 {
111 	struct segment_descriptor *p_idt_entry;
112 	uint32_t offset;
113 
114 	TC_PRINT("Testing to see if IDT has address of test stubs()\n");
115 	/* Check for the interrupt stub */
116 	p_idt_entry = (struct segment_descriptor *)
117 		      (_idt_base_address + (TEST_SOFT_INT << 3));
118 	offset = (uint32_t)(&int_stub);
119 	zassert_equal(DTE_OFFSET(p_idt_entry), offset,
120 		      "Failed to find offset of int_stub (0x%x)"
121 		      " at vector %d\n", offset, TEST_SOFT_INT);
122 
123 	/* Check for the exception stub */
124 	p_idt_entry = (struct segment_descriptor *)
125 		      (_idt_base_address + (IV_DIVIDE_ERROR << 3));
126 	offset = (uint32_t)(&_EXCEPTION_STUB_NAME(exc_divide_error_handler, 0));
127 	zassert_equal(DTE_OFFSET(p_idt_entry), offset,
128 		      "Failed to find offset of exc stub (0x%x)"
129 		      " at vector %d\n", offset, IV_DIVIDE_ERROR);
130 
131 	/*
132 	 * If the other fields are wrong, the system will crash when the
133 	 * exception and software interrupt are triggered so we don't check
134 	 * them.
135 	 */
136 }
137 
idt_spur_task(void * arg1,void * arg2,void * arg3)138 void idt_spur_task(void *arg1, void *arg2, void *arg3)
139 {
140 	TC_PRINT("- Expect to see unhandled interrupt/exception message\n");
141 
142 	_trigger_spur_handler();
143 
144 	/* Shouldn't get here */
145 	spur_handler_aborted_thread = 0;
146 
147 }
148 
149 /**
150  * @ingroup kernel_interrupt_tests
151  * @brief Test entry point to static IDT
152  * @details this test is to generate the interrupt, exception,
153  * and spurious interrupt using various method, the registered handler
154  * should get called
155  */
156 
ZTEST(static_idt,test_static_idt)157 ZTEST(static_idt, test_static_idt)
158 {
159 	volatile int error;     /* used to create a divide by zero error */
160 
161 
162 	TC_PRINT("Testing to see interrupt handler executes properly\n");
163 	_trigger_isr_handler();
164 
165 	zassert_not_equal(int_handler_executed, 0,
166 			  "Interrupt handler did not execute");
167 	zassert_equal(int_handler_executed, 1,
168 		      "Interrupt handler executed more than once! (%d)\n",
169 		      int_handler_executed);
170 
171 	TC_PRINT("Testing to see exception handler executes properly\n");
172 
173 	/*
174 	 * Use exc_handler_executed instead of 0 to prevent the compiler
175 	 * issuing a 'divide by zero' warning.
176 	 */
177 	error = 32;     /* avoid static checker uninitialized warnings */
178 
179 	__asm__ volatile ("movl $0x0, %%edx\n\t"
180 			  "movl %0, %%eax\n\t"
181 			  "movl %1, %%ebx\n\t"
182 			  "idivl %%ebx;" : : "g" (error), "g" (exc_handler_executed) :
183 			  "eax", "edx");
184 
185 	zassert_not_equal(exc_handler_executed, 0,
186 			  "Exception handler did not execute");
187 	zassert_equal(exc_handler_executed, 1,
188 		      "Exception handler executed more than once! (%d)\n",
189 		      exc_handler_executed);
190 	/*
191 	 * Start task to trigger the spurious interrupt handler
192 	 */
193 	TC_PRINT("Testing to see spurious handler executes properly\n");
194 	k_thread_create(&my_thread, my_stack_area, MY_STACK_SIZE,
195 			idt_spur_task, NULL, NULL, NULL,
196 			MY_PRIORITY, 0, K_NO_WAIT);
197 
198 	/*
199 	 * The thread should not run past where the spurious interrupt is
200 	 * generated. Therefore spur_handler_aborted_thread should remain at 1.
201 	 */
202 	zassert_not_equal(spur_handler_aborted_thread, 0,
203 			  "Spurious handler did not execute as expected");
204 }
205 
206 ZTEST_SUITE(static_idt, NULL, NULL, NULL, NULL, NULL);
207