LCOV - code coverage report
Current view: top level - zephyr/arch/x86/ia32 - arch.h Coverage Total Hit
Test: new.info Lines: 33.3 % 30 10
Test Date: 2025-09-05 16:43:28

            Line data    Source code
       1            1 : /*
       2              :  * Copyright (c) 2010-2014 Wind River Systems, Inc.
       3              :  *
       4              :  * SPDX-License-Identifier: Apache-2.0
       5              :  */
       6              : 
       7              : /**
       8              :  * @file
       9              :  * @brief IA-32 specific kernel interface header
      10              :  *
      11              :  * This header contains the IA-32 portion of the X86 specific kernel
      12              :  * interface (see include/zephyr/arch/x86/cpu.h).
      13              :  */
      14              : 
      15              : #ifndef ZEPHYR_INCLUDE_ARCH_X86_IA32_ARCH_H_
      16              : #define ZEPHYR_INCLUDE_ARCH_X86_IA32_ARCH_H_
      17              : 
      18              : #include "sys_io.h"
      19              : #include <stdbool.h>
      20              : #include <zephyr/kernel_structs.h>
      21              : #include <zephyr/arch/common/ffs.h>
      22              : #include <zephyr/sys/util.h>
      23              : #include <zephyr/arch/exception.h>
      24              : #include <zephyr/arch/x86/ia32/gdbstub.h>
      25              : #include <zephyr/arch/x86/ia32/thread.h>
      26              : #include <zephyr/arch/x86/ia32/syscall.h>
      27              : 
      28              : #ifndef _ASMLANGUAGE
      29              : #include <stddef.h>       /* for size_t */
      30              : 
      31              : #include <zephyr/arch/common/addr_types.h>
      32              : #include <zephyr/arch/x86/ia32/segmentation.h>
      33              : #include <zephyr/pm/pm.h>
      34              : 
      35              : #endif /* _ASMLANGUAGE */
      36              : 
      37              : /* GDT layout */
      38            0 : #define CODE_SEG        0x08
      39            0 : #define DATA_SEG        0x10
      40            0 : #define MAIN_TSS        0x18
      41            0 : #define DF_TSS          0x20
      42              : 
      43              : /*
      44              :  * Use for thread local storage.
      45              :  * Match these to gen_gdt.py.
      46              :  * The 0x03 is added to limit privilege.
      47              :  */
      48              : #if defined(CONFIG_USERSPACE)
      49            0 : #define GS_TLS_SEG      (0x38 | 0x03)
      50              : #elif defined(CONFIG_X86_STACK_PROTECTION)
      51              : #define GS_TLS_SEG      (0x28 | 0x03)
      52              : #else
      53              : #define GS_TLS_SEG      (0x18 | 0x03)
      54              : #endif
      55              : 
      56              : /**
      57              :  * Macro used internally by NANO_CPU_INT_REGISTER and NANO_CPU_INT_REGISTER_ASM.
      58              :  * Not meant to be used explicitly by platform, driver or application code.
      59              :  */
      60            1 : #define MK_ISR_NAME(x) __isr__##x
      61              : 
      62              : #define Z_DYN_STUB_SIZE                 8
      63              : #define Z_DYN_STUB_OFFSET               0
      64              : #define Z_DYN_STUB_LONG_JMP_EXTRA_SIZE  3
      65              : #define Z_DYN_STUB_PER_BLOCK            32
      66              : 
      67              : 
      68              : #ifndef _ASMLANGUAGE
      69              : 
      70              : #ifdef __cplusplus
      71              : extern "C" {
      72              : #endif
      73              : 
      74              : /* interrupt/exception/error related definitions */
      75              : 
      76            0 : typedef struct s_isrList {
      77              :         /** Address of ISR/stub */
      78            1 :         void            *fnc;
      79              :         /** IRQ associated with the ISR/stub, or -1 if this is not
      80              :          * associated with a real interrupt; in this case vec must
      81              :          * not be -1
      82              :          */
      83            1 :         unsigned int    irq;
      84              :         /** Priority associated with the IRQ. Ignored if vec is not -1 */
      85            1 :         unsigned int    priority;
      86              :         /** Vector number associated with ISR/stub, or -1 to assign based
      87              :          * on priority
      88              :          */
      89            1 :         unsigned int    vec;
      90              :         /** Privilege level associated with ISR/stub */
      91            1 :         unsigned int    dpl;
      92              : 
      93              :         /** If nonzero, specifies a TSS segment selector. Will configure
      94              :          * a task gate instead of an interrupt gate. fnc parameter will be
      95              :          * ignored
      96              :          */
      97            1 :         unsigned int    tss;
      98            0 : } ISR_LIST;
      99              : 
     100              : 
     101              : /**
     102              :  * @brief Connect a routine to an interrupt vector
     103              :  *
     104              :  * This macro "connects" the specified routine, @a r, to the specified interrupt
     105              :  * vector, @a v using the descriptor privilege level @a d. On the IA-32
     106              :  * architecture, an interrupt vector is a value from 0 to 255. This macro
     107              :  * populates the special intList section with the address of the routine, the
     108              :  * vector number and the descriptor privilege level. The genIdt tool then picks
     109              :  * up this information and generates an actual IDT entry with this information
     110              :  * properly encoded.
     111              :  *
     112              :  * The @a d argument specifies the privilege level for the interrupt-gate
     113              :  * descriptor; (hardware) interrupts and exceptions should specify a level of 0,
     114              :  * whereas handlers for user-mode software generated interrupts should specify 3.
     115              :  * @param r Routine to be connected
     116              :  * @param n IRQ number
     117              :  * @param p IRQ priority
     118              :  * @param v Interrupt Vector
     119              :  * @param d Descriptor Privilege Level
     120              :  */
     121              : 
     122            1 : #define NANO_CPU_INT_REGISTER(r, n, p, v, d) \
     123              :          static ISR_LIST __attribute__((section(".intList"))) \
     124              :                          __attribute__((used)) MK_ISR_NAME(r) = \
     125              :                         { \
     126              :                                 .fnc = &(r), \
     127              :                                 .irq = (n), \
     128              :                                 .priority = (p), \
     129              :                                 .vec = (v), \
     130              :                                 .dpl = (d), \
     131              :                                 .tss = 0 \
     132              :                         }
     133              : 
     134              : /**
     135              :  * @brief Connect an IA hardware task to an interrupt vector
     136              :  *
     137              :  * This is very similar to NANO_CPU_INT_REGISTER but instead of connecting
     138              :  * a handler function, the interrupt will induce an IA hardware task
     139              :  * switch to another hardware task instead.
     140              :  *
     141              :  * @param tss_p GDT/LDT segment selector for the TSS representing the task
     142              :  * @param irq_p IRQ number
     143              :  * @param priority_p IRQ priority
     144              :  * @param vec_p Interrupt vector
     145              :  * @param dpl_p Descriptor privilege level
     146              :  */
     147              : #define _X86_IDT_TSS_REGISTER(tss_p, irq_p, priority_p, vec_p, dpl_p) \
     148              :         static ISR_LIST __attribute__((section(".intList"))) \
     149              :                         __attribute__((used)) MK_ISR_NAME(vec_p) = \
     150              :                         { \
     151              :                                 .fnc = NULL, \
     152              :                                 .irq = (irq_p), \
     153              :                                 .priority = (priority_p), \
     154              :                                 .vec = (vec_p), \
     155              :                                 .dpl = (dpl_p), \
     156              :                                 .tss = (tss_p) \
     157              :                         }
     158              : 
     159              : /**
     160              :  * Code snippets for populating the vector ID and priority into the intList
     161              :  *
     162              :  * The 'magic' of static interrupts is accomplished by building up an array
     163              :  * 'intList' at compile time, and the gen_idt tool uses this to create the
     164              :  * actual IDT data structure.
     165              :  *
     166              :  * For controllers like APIC, the vectors in the IDT are not normally assigned
     167              :  * at build time; instead the sentinel value -1 is saved, and gen_idt figures
     168              :  * out the right vector to use based on our priority scheme. Groups of 16
     169              :  * vectors starting at 32 correspond to each priority level.
     170              :  *
     171              :  * These macros are only intended to be used by IRQ_CONNECT() macro.
     172              :  */
     173              : #define _VECTOR_ARG(irq_p)      (-1)
     174              : 
     175              : #ifdef CONFIG_LINKER_USE_PINNED_SECTION
     176              : #define IRQSTUBS_TEXT_SECTION   ".pinned_text.irqstubs"
     177              : #else
     178            0 : #define IRQSTUBS_TEXT_SECTION   ".text.irqstubs"
     179              : #endif
     180              : 
     181              : /* Internally this function does a few things:
     182              :  *
     183              :  * 1. There is a declaration of the interrupt parameters in the .intList
     184              :  * section, used by gen_idt to create the IDT. This does the same thing
     185              :  * as the NANO_CPU_INT_REGISTER() macro, but is done in assembly as we
     186              :  * need to populate the .fnc member with the address of the assembly
     187              :  * IRQ stub that we generate immediately afterwards.
     188              :  *
     189              :  * 2. The IRQ stub itself is declared. The code will go in its own named
     190              :  * section .text.irqstubs section (which eventually gets linked into 'text')
     191              :  * and the stub shall be named (isr_name)_irq(irq_line)_stub
     192              :  *
     193              :  * 3. The IRQ stub pushes the ISR routine and its argument onto the stack
     194              :  * and then jumps to the common interrupt handling code in _interrupt_enter().
     195              :  *
     196              :  * 4. z_irq_controller_irq_config() is called at runtime to set the mapping
     197              :  * between the vector and the IRQ line as well as triggering flags
     198              :  */
     199            0 : #define ARCH_IRQ_CONNECT(irq_p, priority_p, isr_p, isr_param_p, flags_p) \
     200              : { \
     201              :         __asm__ __volatile__(                                                   \
     202              :                 ".pushsection .intList\n\t" \
     203              :                 ".long %c[isr]_irq%c[irq]_stub\n\t"   /* ISR_LIST.fnc */ \
     204              :                 ".long %c[irq]\n\t"           /* ISR_LIST.irq */ \
     205              :                 ".long %c[priority]\n\t"      /* ISR_LIST.priority */ \
     206              :                 ".long %c[vector]\n\t"                /* ISR_LIST.vec */ \
     207              :                 ".long 0\n\t"                 /* ISR_LIST.dpl */ \
     208              :                 ".long 0\n\t"                 /* ISR_LIST.tss */ \
     209              :                 ".popsection\n\t" \
     210              :                 ".pushsection " IRQSTUBS_TEXT_SECTION "\n\t" \
     211              :                 ".global %c[isr]_irq%c[irq]_stub\n\t" \
     212              :                 "%c[isr]_irq%c[irq]_stub:\n\t" \
     213              :                 "endbr32\n\t" \
     214              :                 "pushl %[isr_param]\n\t" \
     215              :                 "pushl %[isr]\n\t" \
     216              :                 "jmp _interrupt_enter\n\t" \
     217              :                 ".popsection\n\t" \
     218              :                 : \
     219              :                 : [isr] "i" (isr_p), \
     220              :                   [isr_param] "i" (isr_param_p), \
     221              :                   [priority] "i" (priority_p), \
     222              :                   [vector] "i" _VECTOR_ARG(irq_p), \
     223              :                   [irq] "i" (irq_p)); \
     224              :         z_irq_controller_irq_config(Z_IRQ_TO_INTERRUPT_VECTOR(irq_p), (irq_p), \
     225              :                                    (flags_p)); \
     226              : }
     227              : 
     228              : #ifdef CONFIG_PCIE
     229              : 
     230              : #define ARCH_PCIE_IRQ_CONNECT(bdf_p, irq_p, priority_p,                 \
     231              :                               isr_p, isr_param_p, flags_p)              \
     232              :         ARCH_IRQ_CONNECT(irq_p, priority_p, isr_p, isr_param_p, flags_p)
     233              : 
     234              : #endif /* CONFIG_PCIE */
     235              : 
     236              : /* Direct interrupts won't work as expected with KPTI turned on, because
     237              :  * all non-user accessible pages in the page table are marked non-present.
     238              :  * It's likely possible to add logic to ARCH_ISR_DIRECT_HEADER/FOOTER to do
     239              :  * the necessary trampolining to switch page tables / stacks, but this
     240              :  * probably loses all the latency benefits that direct interrupts provide
     241              :  * and one might as well use a regular interrupt anyway.
     242              :  */
     243              : #ifndef CONFIG_X86_KPTI
     244            0 : #define ARCH_IRQ_DIRECT_CONNECT(irq_p, priority_p, isr_p, flags_p) \
     245              : { \
     246              :         NANO_CPU_INT_REGISTER(isr_p, irq_p, priority_p, -1, 0); \
     247              :         z_irq_controller_irq_config(Z_IRQ_TO_INTERRUPT_VECTOR(irq_p), (irq_p), \
     248              :                                    (flags_p)); \
     249              : }
     250              : 
     251              : #ifdef CONFIG_PM
     252              : static inline void arch_irq_direct_pm(void)
     253              : {
     254              :         if (_kernel.idle) {
     255              :                 _kernel.idle = 0;
     256              :                 pm_system_resume();
     257              :         }
     258              : }
     259              : 
     260              : #define ARCH_ISR_DIRECT_PM() arch_irq_direct_pm()
     261              : #else
     262            0 : #define ARCH_ISR_DIRECT_PM() do { } while (false)
     263              : #endif
     264              : 
     265            0 : #define ARCH_ISR_DIRECT_HEADER() arch_isr_direct_header()
     266            0 : #define ARCH_ISR_DIRECT_FOOTER(swap) arch_isr_direct_footer(swap)
     267              : 
     268              : /* FIXME:
     269              :  * tracing/tracing.h cannot be included here due to circular dependency
     270              :  */
     271              : #if defined(CONFIG_TRACING)
     272              : void sys_trace_isr_enter(void);
     273              : void sys_trace_isr_exit(void);
     274              : #endif
     275              : 
     276            0 : static inline void arch_isr_direct_header(void)
     277              : {
     278              : #if defined(CONFIG_TRACING)
     279              :         sys_trace_isr_enter();
     280              : #endif
     281              : 
     282              :         /* We're not going to unlock IRQs, but we still need to increment this
     283              :          * so that arch_is_in_isr() works
     284              :          */
     285              :         ++_kernel.cpus[0].nested;
     286              : }
     287              : 
     288              : /*
     289              :  * FIXME: z_swap_irqlock is an inline function declared in a private header and
     290              :  *        cannot be referenced from a public header, so we move it to an
     291              :  *        external function.
     292              :  */
     293            0 : void arch_isr_direct_footer_swap(unsigned int key);
     294              : 
     295            0 : static inline void arch_isr_direct_footer(int swap)
     296              : {
     297              :         z_irq_controller_eoi();
     298              : #if defined(CONFIG_TRACING)
     299              :         sys_trace_isr_exit();
     300              : #endif
     301              :         --_kernel.cpus[0].nested;
     302              : 
     303              :         /* Call swap if all the following is true:
     304              :          *
     305              :          * 1) swap argument was enabled to this function
     306              :          * 2) We are not in a nested interrupt
     307              :          * 3) Next thread to run in the ready queue is not this thread
     308              :          */
     309              :         if (swap != 0 && _kernel.cpus[0].nested == 0 &&
     310              :             _kernel.ready_q.cache != _current) {
     311              :                 unsigned int flags;
     312              : 
     313              :                 /* Fetch EFLAGS argument to z_swap() */
     314              :                 __asm__ volatile (
     315              :                         "pushfl\n\t"
     316              :                         "popl %0\n\t"
     317              :                         : "=g" (flags)
     318              :                         :
     319              :                         : "memory"
     320              :                         );
     321              : 
     322              :                 arch_isr_direct_footer_swap(flags);
     323              :         }
     324              : }
     325              : 
     326            0 : #define ARCH_ISR_DIRECT_DECLARE(name) \
     327              :         static inline int name##_body(void); \
     328              :         __attribute__ ((interrupt)) void name(void *stack_frame) \
     329              :         { \
     330              :                 ARG_UNUSED(stack_frame); \
     331              :                 int check_reschedule; \
     332              :                 ISR_DIRECT_HEADER(); \
     333              :                 check_reschedule = name##_body(); \
     334              :                 ISR_DIRECT_FOOTER(check_reschedule); \
     335              :         } \
     336              :         static inline int name##_body(void)
     337              : #endif /* !CONFIG_X86_KPTI */
     338              : 
     339            0 : static ALWAYS_INLINE unsigned int arch_irq_lock(void)
     340              : {
     341              :         unsigned int key;
     342              : 
     343              :         __asm__ volatile ("pushfl; cli; popl %0" : "=g" (key) :: "memory");
     344              : 
     345              :         return key;
     346              : }
     347              : 
     348              : 
     349              : /**
     350              :  * The NANO_SOFT_IRQ macro must be used as the value for the @a irq parameter
     351              :  * to NANO_CPU_INT_REGISTER when connecting to an interrupt that does not
     352              :  * correspond to any IRQ line (such as spurious vector or SW IRQ)
     353              :  */
     354            1 : #define NANO_SOFT_IRQ   ((unsigned int) (-1))
     355              : 
     356              : #ifdef CONFIG_X86_ENABLE_TSS
     357              : extern struct task_state_segment _main_tss;
     358              : #endif
     359              : 
     360            0 : #define ARCH_EXCEPT(reason_p) do { \
     361              :         __asm__ volatile( \
     362              :                 "push %[reason]\n\t" \
     363              :                 "int %[vector]\n\t" \
     364              :                 : \
     365              :                 : [vector] "i" (Z_X86_OOPS_VECTOR), \
     366              :                   [reason] "i" (reason_p) \
     367              :                 : "memory"); \
     368              :         CODE_UNREACHABLE; /* LCOV_EXCL_LINE */ \
     369              : } while (false)
     370              : 
     371              : /*
     372              :  * Dynamic thread object memory alignment.
     373              :  *
     374              :  * If support for SSEx extensions is enabled a 16 byte boundary is required,
     375              :  * since the 'fxsave' and 'fxrstor' instructions require this. In all other
     376              :  * cases a 4 byte boundary is sufficient.
     377              :  */
     378              : #if defined(CONFIG_EAGER_FPU_SHARING) || defined(CONFIG_LAZY_FPU_SHARING)
     379              : #ifdef CONFIG_SSE
     380              : #define ARCH_DYNAMIC_OBJ_K_THREAD_ALIGNMENT     16
     381              : #else
     382              : #define ARCH_DYNAMIC_OBJ_K_THREAD_ALIGNMENT     (sizeof(void *))
     383              : #endif
     384              : #else
     385              : /* No special alignment requirements, simply align on pointer size. */
     386            0 : #define ARCH_DYNAMIC_OBJ_K_THREAD_ALIGNMENT     (sizeof(void *))
     387              : #endif /* CONFIG_*_FP_SHARING */
     388              : 
     389              : 
     390              : #ifdef __cplusplus
     391              : }
     392              : #endif
     393              : 
     394              : #endif /* !_ASMLANGUAGE */
     395              : 
     396              : #endif /* ZEPHYR_INCLUDE_ARCH_X86_IA32_ARCH_H_ */
        

Generated by: LCOV version 2.0-1