LCOV - code coverage report
Current view: top level - zephyr/arch/x86/ia32 - segmentation.h Hit Total Coverage
Test: new.info Lines: 2 113 1.8 %
Date: 2024-12-22 00:14:23

          Line data    Source code
       1           0 : /*
       2             :  * Copyright (c) 2016 Intel Corporation
       3             :  *
       4             :  * SPDX-License-Identifier: Apache-2.0
       5             :  */
       6             : 
       7             : #ifndef ZEPHYR_INCLUDE_ARCH_X86_IA32_SEGMENTATION_H_
       8             : #define ZEPHYR_INCLUDE_ARCH_X86_IA32_SEGMENTATION_H_
       9             : 
      10             : #include <zephyr/types.h>
      11             : 
      12             : /* Host gen_idt uses this header as well, don't depend on toolchain.h */
      13             : #ifndef __packed
      14             : #define __packed __attribute__((packed))
      15             : #endif
      16             : 
      17             : #ifdef __cplusplus
      18             : extern "C" {
      19             : #endif
      20             : 
      21             : /* NOTE: We currently do not have definitions for 16-bit segment, currently
      22             :  * assume everything we are working with is 32-bit
      23             :  */
      24             : 
      25           0 : #define SEG_TYPE_LDT            0x2
      26           0 : #define SEG_TYPE_TASK_GATE      0x5
      27           0 : #define SEG_TYPE_TSS            0x9
      28           0 : #define SEG_TYPE_TSS_BUSY       0xB
      29           0 : #define SEG_TYPE_CALL_GATE      0xC
      30           0 : #define SEG_TYPE_IRQ_GATE       0xE
      31           0 : #define SEG_TYPE_TRAP_GATE      0xF
      32             : 
      33           0 : #define DT_GRAN_BYTE    0
      34           0 : #define DT_GRAN_PAGE    1
      35             : 
      36           0 : #define DT_READABLE     1
      37           0 : #define DT_NON_READABLE 0
      38             : 
      39           0 : #define DT_WRITABLE     1
      40           0 : #define DT_NON_WRITABLE 0
      41             : 
      42           0 : #define DT_EXPAND_DOWN  1
      43           0 : #define DT_EXPAND_UP    0
      44             : 
      45           0 : #define DT_CONFORM      1
      46           0 : #define DT_NONCONFORM   0
      47             : 
      48           0 : #define DT_TYPE_SYSTEM          0
      49           0 : #define DT_TYPE_CODEDATA        1
      50             : 
      51             : #ifndef _ASMLANGUAGE
      52             : 
      53             : /* Section 7.2.1 of IA architecture SW developer manual, Vol 3. */
      54           0 : struct __packed task_state_segment {
      55           0 :         uint16_t backlink;
      56           0 :         uint16_t reserved_1;
      57           0 :         uint32_t esp0;
      58           0 :         uint16_t ss0;
      59           0 :         uint16_t reserved_2;
      60           0 :         uint32_t esp1;
      61           0 :         uint16_t ss1;
      62           0 :         uint16_t reserved_3;
      63           0 :         uint32_t esp2;
      64           0 :         uint16_t ss2;
      65           0 :         uint16_t reserved_4;
      66           0 :         uint32_t cr3;
      67           0 :         uint32_t eip;
      68           0 :         uint32_t eflags;
      69           0 :         uint32_t eax;
      70           0 :         uint32_t ecx;
      71           0 :         uint32_t edx;
      72           0 :         uint32_t ebx;
      73           0 :         uint32_t esp;
      74           0 :         uint32_t ebp;
      75           0 :         uint32_t esi;
      76           0 :         uint32_t edi;
      77           0 :         uint16_t es;
      78           0 :         uint16_t reserved_5;
      79           0 :         uint16_t cs;
      80           0 :         uint16_t reserved_6;
      81           0 :         uint16_t ss;
      82           0 :         uint16_t reserved_7;
      83           0 :         uint16_t ds;
      84           0 :         uint16_t reserved_8;
      85           0 :         uint16_t fs;
      86           0 :         uint16_t reserved_9;
      87           0 :         uint16_t gs;
      88           0 :         uint16_t reserved_10;
      89           0 :         uint16_t ldt_ss;
      90           0 :         uint16_t reserved_11;
      91           0 :         uint8_t t:1;            /* Trap bit */
      92           0 :         uint16_t reserved_12:15;
      93           0 :         uint16_t iomap;
      94             : };
      95             : 
      96           0 : #define SEG_SELECTOR(index, table, dpl) (index << 3 | table << 2 | dpl)
      97             : 
      98             : /* References
      99             :  *
     100             :  * Section 5.8.3 (Call gates)
     101             :  * Section 7.2.2 (TSS Descriptor)
     102             :  * Section 3.4.5 (Segment descriptors)
     103             :  * Section 6.11 (IDT Descriptors)
     104             :  *
     105             :  * IA architecture SW developer manual, Vol 3.
     106             :  */
     107           0 : struct __packed segment_descriptor {
     108             : 
     109             :         /* First DWORD: 0-15 */
     110             :         union {
     111             :                 /* IRQ, call, trap gates */
     112           0 :                 uint16_t limit_low;
     113             : 
     114             :                 /* Task gates */
     115           0 :                 uint16_t reserved_task_gate_0;
     116             : 
     117             :                 /* Everything else */
     118           0 :                 uint16_t offset_low;
     119           0 :         };
     120             : 
     121             :         /* First DWORD: 16-31 */
     122             :         union {
     123             :                 /* Call/Task/Interrupt/Trap gates */
     124           0 :                 uint16_t segment_selector;
     125             : 
     126             :                 /* TSS/LDT/Segments */
     127           0 :                 uint16_t base_low;      /* Bits 0-15 */
     128           0 :         };
     129             : 
     130             :         /* Second DWORD: 0-7 */
     131             :         union {
     132             :                 /* TSS/LDT/Segments */
     133           0 :                 uint8_t base_mid;       /* Bits 16-23 */
     134             : 
     135             :                 /* Task gates */
     136           0 :                 uint8_t reserved_task_gate_1;
     137             : 
     138             :                 /* IRQ/Trap/Call Gates */
     139             :                 struct {
     140             :                         /* Reserved except in case of call gates */
     141           0 :                         uint8_t reserved_or_param:5;
     142             : 
     143             :                         /* Bits 5-7 0 0 0 per CPU manual */
     144           0 :                         uint8_t always_0_0:3;
     145             :                 };
     146           0 :         };
     147             : 
     148             :         /* Second DWORD: 8-15 */
     149             :         union {
     150             :                 /* Code or data Segments */
     151             :                 struct {
     152             :                         /* Set by the processor, init to 0 */
     153           0 :                         uint8_t accessed:1;
     154             : 
     155             :                         /* executable ? readable : writable */
     156           0 :                         uint8_t rw:1;
     157             :                         /* executable ? conforming : direction */
     158           0 :                         uint8_t cd:1;
     159             :                         /* 1=code 0=data */
     160           0 :                         uint8_t executable:1;
     161             : 
     162             :                         /* Next 3 fields actually common to all */
     163             : 
     164             :                         /* 1=code or data, 0=system type */
     165           0 :                         uint8_t descriptor_type:1;
     166             : 
     167           0 :                         uint8_t dpl:2;
     168           0 :                         uint8_t present:1;
     169             :                 };
     170             : 
     171             :                 /* System types */
     172             :                 struct {
     173             :                         /* One of the SEG_TYPE_* macros above */
     174           0 :                         uint8_t type:4;
     175             : 
     176             :                         /* Alas, C doesn't let you do a union of the first
     177             :                          * 4 bits of a bitfield and put the rest outside of it,
     178             :                          * it ends up getting padded.
     179             :                          */
     180           0 :                         uint8_t use_other_union:4;
     181             :                 };
     182           0 :         };
     183             : 
     184             :         /* Second DWORD: 16-31 */
     185             :         union {
     186             :                 /* Call/IRQ/trap gates */
     187           0 :                 uint16_t offset_hi;
     188             : 
     189             :                 /* Task Gates */
     190           0 :                 uint16_t reserved_task_gate_2;
     191             : 
     192             :                 /* segment/LDT/TSS */
     193             :                 struct {
     194           0 :                         uint8_t limit_hi:4;
     195             : 
     196             :                         /* flags */
     197           0 :                         uint8_t avl:1;          /* CPU ignores this */
     198             : 
     199             :                         /* 1=Indicates 64-bit code segment in IA-32e mode */
     200           0 :                         uint8_t flags_l:1; /* L field */
     201             : 
     202           0 :                         uint8_t db:1; /* D/B field 1=32-bit 0=16-bit*/
     203           0 :                         uint8_t granularity:1;
     204             : 
     205           0 :                         uint8_t base_hi;        /* Bits 24-31 */
     206             :                 };
     207           0 :         };
     208             : 
     209             : };
     210             : 
     211             : 
     212             : /* Address of this passed to lidt/lgdt.
     213             :  * IA manual calls this a 'pseudo descriptor'.
     214             :  */
     215           0 : struct __packed pseudo_descriptor {
     216           0 :         uint16_t size;
     217           0 :         struct segment_descriptor *entries;
     218             : };
     219             : 
     220             : 
     221             : /*
     222             :  * Full linear address (segment selector+offset), for far jumps/calls
     223             :  */
     224           0 : struct __packed far_ptr {
     225             :         /** Far pointer offset, unused when invoking a task. */
     226           1 :         void *offset;
     227             :         /** Far pointer segment/gate selector. */
     228           1 :         uint16_t sel;
     229             : };
     230             : 
     231             : 
     232           0 : #define DT_ZERO_ENTRY { { 0 } }
     233             : 
     234             : /* NOTE: the below macros only work for fixed addresses provided at build time.
     235             :  * Base addresses or offsets cannot be &some_variable, as pointer values are not
     236             :  * known until link time and the compiler has to split the address into various
     237             :  * fields in the segment selector well before that.
     238             :  *
     239             :  * If you really need to put &some_variable as the base address in some
     240             :  * segment descriptor, you will either need to do the assignment at runtime
     241             :  * or implement some tool to populate values post-link like gen_idt does.
     242             :  */
     243             : #define _LIMIT_AND_BASE(base_p, limit_p, granularity_p) \
     244             :         .base_low = (((uint32_t)base_p) & 0xFFFF), \
     245             :         .base_mid = (((base_p) >> 16) & 0xFF), \
     246             :         .base_hi = (((base_p) >> 24) & 0xFF), \
     247             :         .limit_low = ((limit_p) & 0xFFFF), \
     248             :         .limit_hi = (((limit_p) >> 16) & 0xF), \
     249             :         .granularity = (granularity_p), \
     250             :         .flags_l = 0, \
     251             :         .db = 1, \
     252             :         .avl = 0
     253             : 
     254             : #define _SEGMENT_AND_OFFSET(segment_p, offset_p) \
     255             :         .segment_selector = (segment_p), \
     256             :         .offset_low = ((offset_p) & 0xFFFF), \
     257             :         .offset_hi = ((offset_p) >> 16)
     258             : 
     259             : #define _DESC_COMMON(dpl_p) \
     260             :         .dpl = (dpl_p), \
     261             :         .present = 1
     262             : 
     263             : #define _SYS_DESC(type_p) \
     264             :         .type = type_p, \
     265             :         .descriptor_type = 0
     266             : 
     267             : #define DT_CODE_SEG_ENTRY(base_p, limit_p, granularity_p, dpl_p, readable_p, \
     268           0 :                        conforming_p) \
     269             :         { \
     270             :                 _DESC_COMMON(dpl_p), \
     271             :                 _LIMIT_AND_BASE(base_p, limit_p, granularity_p), \
     272             :                 .accessed = 0, \
     273             :                 .rw = (readable_p), \
     274             :                 .cd = (conforming_p), \
     275             :                 .executable = 1, \
     276             :                 .descriptor_type = 1 \
     277             :         }
     278             : 
     279             : #define DT_DATA_SEG_ENTRY(base_p, limit_p, granularity_p, dpl_p, writable_p, \
     280           0 :                        direction_p) \
     281             :         { \
     282             :                 _DESC_COMMON(dpl_p), \
     283             :                 _LIMIT_AND_BASE(base_p, limit_p, granularity_p), \
     284             :                 .accessed = 0, \
     285             :                 .rw = (writable_p), \
     286             :                 .cd = (direction_p), \
     287             :                 .executable = 0, \
     288             :                 .descriptor_type = 1 \
     289             :         }
     290             : 
     291           0 : #define DT_LDT_ENTRY(base_p, limit_p, granularity_p, dpl_p) \
     292             :         { \
     293             :                 _DESC_COMMON(dpl_p), \
     294             :                 _LIMIT_AND_BASE(base_p, limit_p, granularity_p), \
     295             :                 _SYS_DESC(SEG_TYPE_LDT) \
     296             :         }
     297             : 
     298           0 : #define DT_TSS_ENTRY(base_p, limit_p, granularity_p, dpl_p) \
     299             :         { \
     300             :                 _DESC_COMMON(dpl_p), \
     301             :                 _LIMIT_AND_BASE(base_p, limit_p, granularity_p), \
     302             :                 _SYS_DESC(SEG_TYPE_TSS) \
     303             :         }
     304             : 
     305             : /* "standard" TSS segments that don't stuff extra data past the end of the
     306             :  * TSS struct
     307             :  */
     308           0 : #define DT_TSS_STD_ENTRY(base_p, dpl_p) \
     309             :         DT_TSS_ENTRY(base_p, sizeof(struct task_state_segment), DT_GRAN_BYTE, \
     310             :                      dpl_p)
     311             : 
     312           0 : #define DT_TASK_GATE_ENTRY(segment_p, dpl_p) \
     313             :         { \
     314             :                 _DESC_COMMON(dpl_p), \
     315             :                 _SYS_DESC(SEG_TYPE_TASK_GATE), \
     316             :                 .segment_selector = (segment_p) \
     317             :         }
     318             : 
     319           0 : #define DT_IRQ_GATE_ENTRY(segment_p, offset_p, dpl_p) \
     320             :         { \
     321             :                 _DESC_COMMON(dpl_p), \
     322             :                 _SEGMENT_AND_OFFSET(segment_p, offset_p), \
     323             :                 _SYS_DESC(SEG_TYPE_IRQ_GATE), \
     324             :                 .always_0_0 = 0 \
     325             :         }
     326             : 
     327           0 : #define DT_TRAP_GATE_ENTRY(segment_p, offset_p, dpl_p) \
     328             :         { \
     329             :                 _DESC_COMMON(dpl_p), \
     330             :                 _SEGMENT_AND_OFFSET(segment_p, offset_p), \
     331             :                 _SYS_DESC(SEG_TYPE_TRAP_GATE), \
     332             :                 .always_0_0 = 0 \
     333             :         }
     334             : 
     335           0 : #define DT_CALL_GATE_ENTRY(segment_p, offset_p, dpl_p, param_count_p) \
     336             :         { \
     337             :                 _DESC_COMMON(dpl_p), \
     338             :                 _SEGMENT_AND_OFFSET(segment_p, offset_p), \
     339             :                 _SYS_DESC(SEG_TYPE_TRAP_GATE), \
     340             :                 .reserved_or_param = (param_count_p), \
     341             :                 .always_0_0 = 0 \
     342             :         }
     343             : 
     344           0 : #define DTE_BASE(dt_entry) ((dt_entry)->base_low | \
     345             :                             ((dt_entry)->base_mid << 16) | \
     346             :                             ((dt_entry)->base_hi << 24))
     347             : 
     348           0 : #define DTE_LIMIT(dt_entry) ((dt_entry)->limit_low | \
     349             :                              ((dt_entry)->limit_hi << 16))
     350             : 
     351           0 : #define DTE_OFFSET(dt_entry) ((dt_entry)->offset_low | \
     352             :                               ((dt_entry)->offset_hi << 16))
     353             : 
     354           0 : #define DT_INIT(entries) { sizeof(entries) - 1, &entries[0] }
     355             : 
     356             : #ifdef CONFIG_SET_GDT
     357             : /* This is either the ROM-based GDT in crt0.S or generated by gen_gdt.py,
     358             :  * depending on CONFIG_GDT_DYNAMIC
     359             :  */
     360             : extern struct pseudo_descriptor _gdt;
     361             : #endif
     362             : 
     363             : extern const struct pseudo_descriptor z_idt;
     364             : 
     365             : /**
     366             :  * Properly set the segment descriptor segment and offset
     367             :  *
     368             :  * Used for call/interrupt/trap gates
     369             :  *
     370             :  * @param sd Segment descriptor
     371             :  * @param offset Offset within segment
     372             :  * @param segment_selector Segment selector
     373             :  */
     374             : static inline void z_sd_set_seg_offset(struct segment_descriptor *sd,
     375             :                                       uint16_t segment_selector,
     376             :                                       uint32_t offset)
     377             : {
     378             :         sd->offset_low = offset & 0xFFFFU;
     379             :         sd->offset_hi = offset >> 16U;
     380             :         sd->segment_selector = segment_selector;
     381             :         sd->always_0_0 = 0U;
     382             : }
     383             : 
     384             : 
     385             : /**
     386             :  * Initialize an segment descriptor to be a 32-bit IRQ gate
     387             :  *
     388             :  * @param sd Segment descriptor memory
     389             :  * @param seg_selector Segment selector of handler
     390             :  * @param offset offset of handler
     391             :  * @param dpl descriptor privilege level
     392             :  */
     393             : static inline void z_init_irq_gate(struct segment_descriptor *sd,
     394             :                                   uint16_t seg_selector, uint32_t offset,
     395             :                                   uint32_t dpl)
     396             : {
     397             :         z_sd_set_seg_offset(sd, seg_selector, offset);
     398             :         sd->dpl = dpl;
     399             :         sd->descriptor_type = DT_TYPE_SYSTEM;
     400             :         sd->present = 1U;
     401             :         sd->type = SEG_TYPE_IRQ_GATE;
     402             : }
     403             : 
     404             : /**
     405             :  * Set current IA task TSS
     406             :  *
     407             :  * @param sel Segment selector in GDT for desired TSS
     408             :  */
     409             : static inline void _set_tss(uint16_t sel)
     410             : {
     411             :         __asm__ __volatile__ ("ltr %0" :: "r" (sel));
     412             : }
     413             : 
     414             : 
     415             : /**
     416             :  * Get the TSS segment selector in the GDT for the current IA task
     417             :  *
     418             :  * @return Segment selector for current IA task
     419             :  */
     420             : static inline uint16_t _get_tss(void)
     421             : {
     422             :         uint16_t sel;
     423             : 
     424             :         __asm__ __volatile__ ("str %0" : "=r" (sel));
     425             :         return sel;
     426             : }
     427             : 
     428             : 
     429             : /**
     430             :  * Get the current global descriptor table
     431             :  *
     432             :  * @param gdt Pointer to memory to receive GDT pseudo descriptor information
     433             :  */
     434             : static inline void _get_gdt(struct pseudo_descriptor *gdt)
     435             : {
     436             :         __asm__ __volatile__ ("sgdt %0" : "=m" (*gdt));
     437             : }
     438             : 
     439             : 
     440             : /**
     441             :  * Get the current interrupt descriptor table
     442             :  *
     443             :  * @param idt Pointer to memory to receive IDT pseudo descriptor information
     444             :  */
     445             : static inline void _get_idt(struct pseudo_descriptor *idt)
     446             : {
     447             :         __asm__ __volatile__ ("sidt %0" : "=m" (*idt));
     448             : }
     449             : 
     450             : 
     451             : /**
     452             :  * Get the current local descriptor table (LDT)
     453             :  *
     454             :  * @return Segment selector in the GDT for the current LDT
     455             :  */
     456             : static inline uint16_t _get_ldt(void)
     457             : {
     458             :         uint16_t ret;
     459             : 
     460             :         __asm__ __volatile__ ("sldt %0" : "=m" (ret));
     461             :         return ret;
     462             : }
     463             : 
     464             : 
     465             : /**
     466             :  * Set the local descriptor table for the current IA Task
     467             :  *
     468             :  * @param ldt Segment selector in the GDT for an LDT
     469             :  */
     470             : static inline void _set_ldt(uint16_t ldt)
     471             : {
     472             :         __asm__ __volatile__ ("lldt %0" :: "m" (ldt));
     473             : 
     474             : }
     475             : 
     476             : /**
     477             :  * Set the global descriptor table
     478             :  *
     479             :  * You will most likely need to update all the data segment registers
     480             :  * and do a far call to the code segment.
     481             :  *
     482             :  * @param gdt Pointer to GDT pseudo descriptor.
     483             :  */
     484             : static inline void _set_gdt(const struct pseudo_descriptor *gdt)
     485             : {
     486             :         __asm__ __volatile__ ("lgdt %0" :: "m" (*gdt));
     487             : }
     488             : 
     489             : 
     490             : /**
     491             :  * Set the interrupt descriptor table
     492             :  *
     493             :  * @param idt Pointer to IDT pseudo descriptor.
     494             :  */
     495             : static inline void z_set_idt(const struct pseudo_descriptor *idt)
     496             : {
     497             :         __asm__ __volatile__ ("lidt %0" :: "m" (*idt));
     498             : }
     499             : 
     500             : 
     501             : /**
     502             :  * Get the segment selector for the current code segment
     503             :  *
     504             :  * @return Segment selector
     505             :  */
     506             : static inline uint16_t _get_cs(void)
     507             : {
     508             :         uint16_t cs = 0U;
     509             : 
     510             :         __asm__ __volatile__ ("mov %%cs, %0" : "=r" (cs));
     511             :         return cs;
     512             : }
     513             : 
     514             : 
     515             : /**
     516             :  * Get the segment selector for the current data segment
     517             :  *
     518             :  * @return Segment selector
     519             :  */
     520             : static inline uint16_t _get_ds(void)
     521             : {
     522             :         uint16_t ds = 0U;
     523             : 
     524             :         __asm__ __volatile__ ("mov %%ds, %0" : "=r" (ds));
     525             :         return ds;
     526             : }
     527             : 
     528             : 
     529             : #endif /* _ASMLANGUAGE */
     530             : 
     531             : #ifdef __cplusplus
     532             : }
     533             : #endif
     534             : 
     535             : #endif /* ZEPHYR_INCLUDE_ARCH_X86_IA32_SEGMENTATION_H_ */

Generated by: LCOV version 1.14