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