Line data Source code
1 1 : /*
2 : * Copyright (c) 2019 Intel Corp.
3 : * SPDX-License-Identifier: Apache-2.0
4 : */
5 :
6 : /**
7 : * @file
8 : * @brief X86 specific kernel interface header
9 : *
10 : * This header contains the X86 specific kernel interfaces for both
11 : * IA-32 and Intel-64. It is included by the kernel interface
12 : * architecture-abstraction header (include/zephyr/arch/cpu.h).
13 : */
14 :
15 : #ifndef ZEPHYR_INCLUDE_ARCH_X86_ARCH_H_
16 : #define ZEPHYR_INCLUDE_ARCH_X86_ARCH_H_
17 :
18 : #include <zephyr/devicetree.h>
19 :
20 : /* Changing this value will require manual changes to exception and IDT setup
21 : * in locore.S for intel64
22 : */
23 : #define Z_X86_OOPS_VECTOR 32
24 :
25 : #if !defined(_ASMLANGUAGE)
26 :
27 : #include <zephyr/sys/sys_io.h>
28 : #include <zephyr/types.h>
29 : #include <stddef.h>
30 : #include <stdbool.h>
31 : #include <zephyr/irq.h>
32 : #include <zephyr/arch/x86/mmustructs.h>
33 : #include <zephyr/arch/x86/thread_stack.h>
34 : #include <zephyr/arch/x86/cet.h>
35 : #include <zephyr/linker/sections.h>
36 :
37 : #ifdef __cplusplus
38 : extern "C" {
39 : #endif
40 :
41 : #ifdef CONFIG_PCIE_MSI
42 :
43 : struct x86_msi_vector {
44 : unsigned int irq;
45 : uint8_t vector;
46 : #ifdef CONFIG_INTEL_VTD_ICTL
47 : bool remap;
48 : uint8_t irte;
49 : #endif
50 : };
51 :
52 : typedef struct x86_msi_vector arch_msi_vector_t;
53 :
54 : #endif /* CONFIG_PCIE_MSI */
55 :
56 0 : static ALWAYS_INLINE void arch_irq_unlock(unsigned int key)
57 : {
58 : if ((key & 0x00000200U) != 0U) { /* 'IF' bit */
59 : __asm__ volatile ("sti" ::: "memory");
60 : }
61 : }
62 :
63 0 : static ALWAYS_INLINE void sys_out8(uint8_t data, io_port_t port)
64 : {
65 : __asm__ volatile("outb %b0, %w1" :: "a"(data), "Nd"(port));
66 : }
67 :
68 0 : static ALWAYS_INLINE uint8_t sys_in8(io_port_t port)
69 : {
70 : uint8_t ret;
71 :
72 : __asm__ volatile("inb %w1, %b0" : "=a"(ret) : "Nd"(port));
73 :
74 : return ret;
75 : }
76 :
77 0 : static ALWAYS_INLINE void sys_out16(uint16_t data, io_port_t port)
78 : {
79 : __asm__ volatile("outw %w0, %w1" :: "a"(data), "Nd"(port));
80 : }
81 :
82 0 : static ALWAYS_INLINE uint16_t sys_in16(io_port_t port)
83 : {
84 : uint16_t ret;
85 :
86 : __asm__ volatile("inw %w1, %w0" : "=a"(ret) : "Nd"(port));
87 :
88 : return ret;
89 : }
90 :
91 0 : static ALWAYS_INLINE void sys_out32(uint32_t data, io_port_t port)
92 : {
93 : __asm__ volatile("outl %0, %w1" :: "a"(data), "Nd"(port));
94 : }
95 :
96 0 : static ALWAYS_INLINE uint32_t sys_in32(io_port_t port)
97 : {
98 : uint32_t ret;
99 :
100 : __asm__ volatile("inl %w1, %0" : "=a"(ret) : "Nd"(port));
101 :
102 : return ret;
103 : }
104 :
105 0 : static ALWAYS_INLINE void sys_write8(uint8_t data, mm_reg_t addr)
106 : {
107 : __asm__ volatile("movb %0, %1"
108 : :
109 : : "q"(data), "m" (*(volatile uint8_t *)(uintptr_t) addr)
110 : : "memory");
111 : }
112 :
113 0 : static ALWAYS_INLINE uint8_t sys_read8(mm_reg_t addr)
114 : {
115 : uint8_t ret;
116 :
117 : __asm__ volatile("movb %1, %0"
118 : : "=q"(ret)
119 : : "m" (*(volatile uint8_t *)(uintptr_t) addr)
120 : : "memory");
121 :
122 : return ret;
123 : }
124 :
125 0 : static ALWAYS_INLINE void sys_write16(uint16_t data, mm_reg_t addr)
126 : {
127 : __asm__ volatile("movw %0, %1"
128 : :
129 : : "r"(data), "m" (*(volatile uint16_t *)(uintptr_t) addr)
130 : : "memory");
131 : }
132 :
133 0 : static ALWAYS_INLINE uint16_t sys_read16(mm_reg_t addr)
134 : {
135 : uint16_t ret;
136 :
137 : __asm__ volatile("movw %1, %0"
138 : : "=r"(ret)
139 : : "m" (*(volatile uint16_t *)(uintptr_t) addr)
140 : : "memory");
141 :
142 : return ret;
143 : }
144 :
145 0 : static ALWAYS_INLINE void sys_write32(uint32_t data, mm_reg_t addr)
146 : {
147 : __asm__ volatile("movl %0, %1"
148 : :
149 : : "r"(data), "m" (*(volatile uint32_t *)(uintptr_t) addr)
150 : : "memory");
151 : }
152 :
153 0 : static ALWAYS_INLINE uint32_t sys_read32(mm_reg_t addr)
154 : {
155 : uint32_t ret;
156 :
157 : __asm__ volatile("movl %1, %0"
158 : : "=r"(ret)
159 : : "m" (*(volatile uint32_t *)(uintptr_t) addr)
160 : : "memory");
161 :
162 : return ret;
163 : }
164 :
165 0 : static ALWAYS_INLINE void sys_set_bit(mem_addr_t addr, unsigned int bit)
166 : {
167 : __asm__ volatile("btsl %1, %0"
168 : : "+m" (*(volatile uint8_t *) (addr))
169 : : "Ir" (bit)
170 : : "memory");
171 : }
172 :
173 0 : static ALWAYS_INLINE void sys_clear_bit(mem_addr_t addr, unsigned int bit)
174 : {
175 : __asm__ volatile("btrl %1, %0"
176 : : "+m" (*(volatile uint8_t *) (addr))
177 : : "Ir" (bit));
178 : }
179 :
180 0 : static ALWAYS_INLINE int sys_test_bit(mem_addr_t addr, unsigned int bit)
181 : {
182 : int ret;
183 :
184 : __asm__ volatile("btl %2, %1;"
185 : "sbb %0, %0"
186 : : "=r" (ret), "+m" (*(volatile uint8_t *) (addr))
187 : : "Ir" (bit));
188 :
189 : return ret;
190 : }
191 :
192 0 : static ALWAYS_INLINE int sys_test_and_set_bit(mem_addr_t addr,
193 : unsigned int bit)
194 : {
195 : int ret;
196 :
197 : __asm__ volatile("btsl %2, %1;"
198 : "sbb %0, %0"
199 : : "=r" (ret), "+m" (*(volatile uint8_t *) (addr))
200 : : "Ir" (bit));
201 :
202 : return ret;
203 : }
204 :
205 0 : static ALWAYS_INLINE int sys_test_and_clear_bit(mem_addr_t addr,
206 : unsigned int bit)
207 : {
208 : int ret;
209 :
210 : __asm__ volatile("btrl %2, %1;"
211 : "sbb %0, %0"
212 : : "=r" (ret), "+m" (*(volatile uint8_t *) (addr))
213 : : "Ir" (bit));
214 :
215 : return ret;
216 : }
217 :
218 0 : #define sys_bitfield_set_bit sys_set_bit
219 0 : #define sys_bitfield_clear_bit sys_clear_bit
220 0 : #define sys_bitfield_test_bit sys_test_bit
221 0 : #define sys_bitfield_test_and_set_bit sys_test_and_set_bit
222 0 : #define sys_bitfield_test_and_clear_bit sys_test_and_clear_bit
223 :
224 : /*
225 : * Map of IRQ numbers to their assigned vectors. On IA32, this is generated
226 : * at build time and defined via the linker script. On Intel64, it's an array.
227 : */
228 :
229 : extern unsigned char _irq_to_interrupt_vector[CONFIG_MAX_IRQ_LINES];
230 :
231 : #define Z_IRQ_TO_INTERRUPT_VECTOR(irq) \
232 : ((unsigned int) _irq_to_interrupt_vector[(irq)])
233 :
234 :
235 : #endif /* _ASMLANGUAGE */
236 :
237 : #ifdef __cplusplus
238 : }
239 : #endif
240 :
241 : #include <zephyr/drivers/interrupt_controller/sysapic.h>
242 :
243 : #ifdef CONFIG_X86_64
244 : #include <zephyr/arch/x86/intel64/arch.h>
245 : #else
246 : #include <zephyr/arch/x86/ia32/arch.h>
247 : #endif
248 :
249 : #include <zephyr/arch/common/ffs.h>
250 :
251 : #ifdef __cplusplus
252 : extern "C" {
253 : #endif
254 :
255 : #ifndef _ASMLANGUAGE
256 :
257 0 : void arch_irq_enable(unsigned int irq);
258 0 : void arch_irq_disable(unsigned int irq);
259 :
260 0 : uint32_t sys_clock_cycle_get_32(void);
261 :
262 : __pinned_func
263 0 : static inline uint32_t arch_k_cycle_get_32(void)
264 : {
265 : return sys_clock_cycle_get_32();
266 : }
267 :
268 0 : uint64_t sys_clock_cycle_get_64(void);
269 :
270 : __pinned_func
271 0 : static inline uint64_t arch_k_cycle_get_64(void)
272 : {
273 : return sys_clock_cycle_get_64();
274 : }
275 :
276 0 : static ALWAYS_INLINE bool arch_irq_unlocked(unsigned int key)
277 : {
278 : return (key & 0x200) != 0;
279 : }
280 :
281 : /**
282 : * @brief read timestamp register, 32-bits only, unserialized
283 : */
284 :
285 : static ALWAYS_INLINE uint32_t z_do_read_cpu_timestamp32(void)
286 : {
287 : uint32_t rv;
288 :
289 : __asm__ volatile("rdtsc" : "=a" (rv) : : "%edx");
290 :
291 : return rv;
292 : }
293 :
294 : /**
295 : * @brief read timestamp register ensuring serialization
296 : */
297 :
298 : __pinned_func
299 : static inline uint64_t z_tsc_read(void)
300 : {
301 : union {
302 : struct {
303 : uint32_t lo;
304 : uint32_t hi;
305 : };
306 : uint64_t value;
307 : } rv;
308 :
309 : #ifdef CONFIG_X86_64
310 : /*
311 : * According to Intel 64 and IA-32 Architectures Software
312 : * Developer’s Manual, volume 3, chapter 8.2.5, LFENCE provides
313 : * a more efficient method of controlling memory ordering than
314 : * the CPUID instruction. So use LFENCE here, as all 64-bit
315 : * CPUs have LFENCE.
316 : */
317 : __asm__ volatile ("lfence");
318 : #else
319 : /* rdtsc & cpuid clobbers eax, ebx, ecx and edx registers */
320 : __asm__ volatile (/* serialize */
321 : "xorl %%eax,%%eax;"
322 : "cpuid"
323 : :
324 : :
325 : : "%eax", "%ebx", "%ecx", "%edx"
326 : );
327 : #endif
328 :
329 : #ifdef CONFIG_X86_64
330 : /*
331 : * We cannot use "=A", since this would use %rax on x86_64 and
332 : * return only the lower 32bits of the TSC
333 : */
334 : __asm__ volatile ("rdtsc" : "=a" (rv.lo), "=d" (rv.hi));
335 : #else
336 : /* "=A" means that value is in eax:edx pair. */
337 : __asm__ volatile ("rdtsc" : "=A" (rv.value));
338 : #endif
339 :
340 : return rv.value;
341 : }
342 :
343 0 : static ALWAYS_INLINE void arch_nop(void)
344 : {
345 : __asm__ volatile("nop");
346 : }
347 :
348 : #endif /* _ASMLANGUAGE */
349 :
350 : #ifdef __cplusplus
351 : }
352 : #endif
353 :
354 : #endif /* ZEPHYR_INCLUDE_ARCH_X86_ARCH_H_ */
|