source: rtems/cpukit/score/cpu/i386/include/rtems/score/i386.h @ a660e9dc

Last change on this file since a660e9dc was a660e9dc, checked in by Sebastian Huber <sebastian.huber@…>, on 09/08/22 at 08:37:05

Do not use RTEMS_INLINE_ROUTINE

Directly use "static inline" which is available in C99 and later. This brings
the RTEMS implementation closer to standard C.

Close #3935.

  • Property mode set to 100644
File size: 18.3 KB
Line 
1/* SPDX-License-Identifier: BSD-2-Clause */
2
3/**
4 * @file
5 *
6 * @brief Intel I386 CPU Dependent Source
7 *
8 * @addtogroup RTEMSScoreCPUi386
9 *
10 * This include file contains information pertaining to the Intel
11 * i386 processor.
12 */
13
14/*
15 *  COPYRIGHT (c) 1989-2016.
16 *  On-Line Applications Research Corporation (OAR).
17 *
18 *  Copyright (C) 1998  Eric Valette (valette@crf.canon.fr)
19 *                     Canon Centre Recherche France.
20 *
21 * Redistribution and use in source and binary forms, with or without
22 * modification, are permitted provided that the following conditions
23 * are met:
24 * 1. Redistributions of source code must retain the above copyright
25 *    notice, this list of conditions and the following disclaimer.
26 * 2. Redistributions in binary form must reproduce the above copyright
27 *    notice, this list of conditions and the following disclaimer in the
28 *    documentation and/or other materials provided with the distribution.
29 *
30 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
31 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
34 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
35 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
36 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
37 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
38 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
39 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
40 * POSSIBILITY OF SUCH DAMAGE.
41 */
42
43#ifndef _RTEMS_SCORE_I386_H
44#define _RTEMS_SCORE_I386_H
45
46#ifdef __cplusplus
47extern "C" {
48#endif
49
50#include <rtems/score/interrupts.h>
51#include <rtems/score/registers.h>
52
53/*
54 *  This section contains the information required to build
55 *  RTEMS for a particular member of the Intel i386
56 *  family when executing in protected mode.  It does
57 *  this by setting variables to indicate which implementation
58 *  dependent features are present in a particular member
59 *  of the family.
60 *
61 *  Currently recognized:
62 *    i386_fp    (i386 DX or SX w/i387)
63 *    i486dx
64 *    pentium
65 *    pentiumpro
66 *
67 *  CPU Model Feature Flags:
68 *
69 *  I386_HAS_BSWAP:  Defined to "1" if the instruction for endian swapping
70 *                   (bswap) should be used.  This instruction appears to
71 *                   be present in all i486's and above.
72 *
73 *  I386_HAS_FPU:    Defined to "1" if the CPU has an FPU.
74 *                   As of at least gcc 4.7, i386 soft-float was obsoleted.
75 *                   Thus this is always set to "1".
76 */
77#define I386_HAS_FPU 1
78
79#if defined(__pentiumpro__)
80
81  #define CPU_MODEL_NAME  "Pentium Pro"
82
83#elif defined(__i586__)
84
85  #if defined(__pentium__)
86    #define CPU_MODEL_NAME "Pentium"
87  #elif defined(__k6__)
88    #define CPU_MODEL_NAME "K6"
89  #else
90    #define CPU_MODEL_NAME "i586"
91  #endif
92
93#elif defined(__i486__)
94
95  #define CPU_MODEL_NAME  "i486dx"
96
97#elif defined(__i386__)
98
99  #define I386_HAS_BSWAP  0
100  #define CPU_MODEL_NAME  "i386 with i387"
101
102#else
103  #error "Unknown CPU Model"
104#endif
105
106/*
107 *  Set default values for CPU model feature flags
108 *
109 *  NOTE: These settings are chosen to reflect most of the family members.
110 */
111#ifndef I386_HAS_BSWAP
112#define I386_HAS_BSWAP 1
113#endif
114
115/*
116 *  Define the name of the CPU family.
117 */
118#define CPU_NAME "Intel i386"
119
120#ifndef ASM
121
122/*
123 *  The following routine swaps the endian format of an unsigned int.
124 *  It must be static so it can be referenced indirectly.
125 */
126
127static inline uint32_t i386_swap_u32(
128  uint32_t value
129)
130{
131  uint32_t lout;
132
133#if (I386_HAS_BSWAP == 0)
134  __asm__ volatile( "rorw  $8,%%ax;"
135                "rorl  $16,%0;"
136                "rorw  $8,%%ax" : "=a" (lout) : "0" (value) );
137#else
138    __asm__ volatile( "bswap %0" : "=r"  (lout) : "0"   (value));
139#endif
140  return( lout );
141}
142#define CPU_swap_u32( _value )  i386_swap_u32( _value )
143
144static inline uint16_t i386_swap_u16(
145  uint16_t value
146)
147{
148  unsigned short      sout;
149
150  __asm__ volatile( "rorw $8,%0" : "=r"  (sout) : "0"   (value));
151  return (sout);
152}
153#define CPU_swap_u16( _value )  i386_swap_u16( _value )
154
155/*
156 * Added for pagination management
157 */
158static inline unsigned int i386_get_cr0(void)
159{
160  unsigned int segment = 0;
161
162  __asm__ volatile ( "movl %%cr0,%0" : "=r" (segment) : "0" (segment) );
163
164  return segment;
165}
166
167static inline void i386_set_cr0(unsigned int segment)
168{
169  __asm__ volatile ( "movl %0,%%cr0" : "=r" (segment) : "0" (segment) );
170}
171
172static inline unsigned int i386_get_cr2(void)
173{
174  unsigned int segment = 0;
175
176  __asm__ volatile ( "movl %%cr2,%0" : "=r" (segment) : "0" (segment) );
177
178  return segment;
179}
180
181static inline unsigned int i386_get_cr3(void)
182{
183  unsigned int segment = 0;
184
185  __asm__ volatile ( "movl %%cr3,%0" : "=r" (segment) : "0" (segment) );
186
187  return segment;
188}
189
190static inline void i386_set_cr3(unsigned int segment)
191{
192  __asm__ volatile ( "movl %0,%%cr3" : "=r" (segment) : "0" (segment) );
193}
194
195/* routines */
196
197/*
198 *  i386_Logical_to_physical
199 *
200 *  Converts logical address to physical address.
201 */
202void *i386_Logical_to_physical(
203  unsigned short  segment,
204  void           *address
205);
206
207/*
208 *  i386_Physical_to_logical
209 *
210 *  Converts physical address to logical address.
211 */
212void *i386_Physical_to_logical(
213  unsigned short  segment,
214  void           *address
215);
216
217/**
218 * @brief Converts real mode pointer {segment, offset} to physical address.
219 *
220 * i386_Real_to_physical
221 *
222 * @param[in] segment used with \p offset to compute physical address
223 * @param[in] offset used with \p segment to compute physical address
224 * @retval    physical address
225 */
226static inline void *i386_Real_to_physical(
227    uint16_t segment,
228    uint16_t offset)
229{
230    return (void *)(((uint32_t)segment<<4)+offset);
231}
232
233/**
234 * @brief Retrieves real mode pointer elements {segmnet, offset} from
235 * physical address.
236 *
237 * i386_Physical_to_real
238 * Function returns the highest segment (base) address possible.
239 * Example:    input   address - 0x4B3A2
240 *             output  segment - 0x4B3A
241 *                     offset  - 0x2
242 *             input   address - 0x10F12E
243 *             output  segment - 0xFFFF
244 *                     offset  - 0xF13E
245 *
246 * @param[in]  address address to be converted, must be less than 0x10FFEF
247 * @param[out] segment segment computed from \p address
248 * @param[out] offset offset computed from \p address
249 * @retval  0 address not convertible
250 * @retval  1 segment and offset extracted
251 */
252int i386_Physical_to_real(
253  void *address,
254  uint16_t *segment,
255  uint16_t *offset
256);
257
258/*
259 *  Segment Access Routines
260 *
261 *  NOTE:  Unfortunately, these are still static inlines even when the
262 *         "macro" implementation of the generic code is used.
263 */
264
265static __inline__ unsigned short i386_get_cs(void)
266{
267  unsigned short segment = 0;
268
269  __asm__ volatile ( "movw %%cs,%0" : "=r" (segment) : "0" (segment) );
270
271  return segment;
272}
273
274static __inline__ unsigned short i386_get_ds(void)
275{
276  unsigned short segment = 0;
277
278  __asm__ volatile ( "movw %%ds,%0" : "=r" (segment) : "0" (segment) );
279
280  return segment;
281}
282
283static __inline__ unsigned short i386_get_es(void)
284{
285  unsigned short segment = 0;
286
287  __asm__ volatile ( "movw %%es,%0" : "=r" (segment) : "0" (segment) );
288
289  return segment;
290}
291
292static __inline__ unsigned short i386_get_ss(void)
293{
294  unsigned short segment = 0;
295
296  __asm__ volatile ( "movw %%ss,%0" : "=r" (segment) : "0" (segment) );
297
298  return segment;
299}
300
301static __inline__ unsigned short i386_get_fs(void)
302{
303  unsigned short segment = 0;
304
305  __asm__ volatile ( "movw %%fs,%0" : "=r" (segment) : "0" (segment) );
306
307  return segment;
308}
309
310static __inline__ unsigned short i386_get_gs(void)
311{
312  unsigned short segment = 0;
313
314  __asm__ volatile ( "movw %%gs,%0" : "=r" (segment) : "0" (segment) );
315
316  return segment;
317}
318
319/*
320 *  IO Port Access Routines
321 */
322
323#define i386_outport_byte( _port, _value ) \
324do { unsigned short __port  = _port; \
325     unsigned char  __value = _value; \
326     \
327     __asm__ volatile ( "outb %0,%1" : : "a" (__value), "d" (__port) ); \
328   } while (0)
329
330#define i386_outport_word( _port, _value ) \
331do { unsigned short __port  = _port; \
332     unsigned short __value = _value; \
333     \
334     __asm__ volatile ( "outw %0,%1" : : "a" (__value), "d" (__port) ); \
335   } while (0)
336
337#define i386_outport_long( _port, _value ) \
338do { unsigned short __port  = _port; \
339     unsigned int  __value = _value; \
340     \
341     __asm__ volatile ( "outl %0,%1" : : "a" (__value), "d" (__port) ); \
342   } while (0)
343
344#define i386_inport_byte( _port, _value ) \
345do { unsigned short __port  = _port; \
346     unsigned char  __value = 0; \
347     \
348     __asm__ volatile ( "inb %1,%0" : "=a" (__value) \
349                                : "d"  (__port) \
350                  ); \
351     _value = __value; \
352   } while (0)
353
354#define i386_inport_word( _port, _value ) \
355do { unsigned short __port  = _port; \
356     unsigned short __value = 0; \
357     \
358     __asm__ volatile ( "inw %1,%0" : "=a" (__value) \
359                                : "d"  (__port) \
360                  ); \
361     _value = __value; \
362   } while (0)
363
364#define i386_inport_long( _port, _value ) \
365do { unsigned short __port  = _port; \
366     unsigned int  __value = 0; \
367     \
368     __asm__ volatile ( "inl %1,%0" : "=a" (__value) \
369                                : "d"  (__port) \
370                  ); \
371     _value = __value; \
372   } while (0)
373
374/*
375 * Type definition for raw interrupts.
376 */
377
378typedef unsigned char  rtems_vector_offset;
379
380typedef struct __rtems_raw_irq_connect_data__{
381 /*
382  * IDT vector offset (IRQ line + PC386_IRQ_VECTOR_BASE)
383  */
384  rtems_vector_offset           idtIndex;
385  /*
386   * IDT raw handler. See comment on handler properties below in function prototype.
387   */
388  rtems_raw_irq_hdl             hdl;
389  /*
390   * function for enabling raw interrupts. In order to be consistent
391   * with the fact that the raw connexion can defined in the
392   * libcpu library, this library should have no knowledge of
393   * board specific hardware to manage interrupts and thus the
394   * "on" routine must enable the irq both at device and PIC level.
395   *
396   */
397    rtems_raw_irq_enable        on;
398  /*
399   * function for disabling raw interrupts. In order to be consistent
400   * with the fact that the raw connexion can defined in the
401   * libcpu library, this library should have no knowledge of
402   * board specific hardware to manage interrupts and thus the
403   * "on" routine must disable the irq both at device and PIC level.
404   *
405   */
406  rtems_raw_irq_disable         off;
407  /*
408   * function enabling to know what interrupt may currently occur
409   */
410  rtems_raw_irq_is_enabled      isOn;
411}rtems_raw_irq_connect_data;
412
413typedef struct {
414  /*
415   * size of all the table fields (*Tbl) described below.
416   */
417  unsigned int                  idtSize;
418  /*
419   * Default handler used when disconnecting interrupts.
420   */
421  rtems_raw_irq_connect_data    defaultRawEntry;
422  /*
423   * Table containing initials/current value.
424   */
425  rtems_raw_irq_connect_data*   rawIrqHdlTbl;
426}rtems_raw_irq_global_settings;
427
428#include <rtems/score/idtr.h>
429
430/*
431 * C callable function enabling to get handler currently connected to a vector
432 *
433 */
434rtems_raw_irq_hdl get_hdl_from_vector(rtems_vector_offset);
435
436/*
437 * C callable function enabling to set up one raw idt entry
438 */
439extern int i386_set_idt_entry (const rtems_raw_irq_connect_data*);
440
441/*
442 * C callable function enabling to get one current raw idt entry
443 */
444extern int i386_get_current_idt_entry (rtems_raw_irq_connect_data*);
445
446/*
447 * C callable function enabling to remove one current raw idt entry
448 */
449extern int i386_delete_idt_entry (const rtems_raw_irq_connect_data*);
450
451/*
452 * C callable function enabling to init idt.
453 *
454 * CAUTION : this function assumes that the IDTR register
455 * has been already set.
456 */
457extern int i386_init_idt (rtems_raw_irq_global_settings* config);
458
459/*
460 * C callable function enabling to get actual idt configuration
461 */
462extern int i386_get_idt_config (rtems_raw_irq_global_settings** config);
463
464
465/*
466 * See page 11.12 Figure 11-8.
467 *
468 */
469/**
470 * @brief describes one entry of Global/Local Descriptor Table
471 */
472typedef struct {
473  unsigned int limit_15_0               : 16;
474  unsigned int base_address_15_0        : 16;
475  unsigned int base_address_23_16       : 8;
476  unsigned int type                     : 4;
477  unsigned int descriptor_type          : 1;
478  unsigned int privilege                : 2;
479  unsigned int present                  : 1;
480  unsigned int limit_19_16              : 4;
481  unsigned int available                : 1;
482  unsigned int fixed_value_bits         : 1;
483  unsigned int operation_size           : 1;
484  unsigned int granularity              : 1;
485  unsigned int base_address_31_24       : 8;
486} RTEMS_PACKED segment_descriptors;
487
488/*
489 * C callable function enabling to get easilly usable info from
490 * the actual value of GDT register.
491 */
492extern void i386_get_info_from_GDTR (segment_descriptors** table,
493                                     uint16_t* limit);
494/*
495 * C callable function enabling to change the value of GDT register. Must be called
496 * with interrupts masked at processor level!!!.
497 */
498extern void i386_set_GDTR (segment_descriptors*,
499                           uint16_t limit);
500
501/**
502 * @brief Allows to set a GDT entry.
503 *
504 * Puts global descriptor \p sd to the global descriptor table on index
505 * \p segment_selector_index
506 *
507 * @param[in] segment_selector_index index to GDT entry
508 * @param[in] sd structure to be coppied to given \p segment_selector in GDT
509 * @retval  0 FAILED out of GDT range or index is 0, which is not valid
510 *                   index in GDT
511 * @retval  1 SUCCESS
512 */
513extern uint32_t i386_raw_gdt_entry (uint16_t segment_selector_index,
514                               segment_descriptors* sd);
515
516/**
517 * @brief fills \p sd with provided \p base in appropriate fields of \p sd
518 *
519 * @param[in] base 32-bit address to be set as descriptor's base
520 * @param[out] sd descriptor being filled with \p base
521 */
522extern void i386_fill_segment_desc_base (uint32_t base,
523                                         segment_descriptors* sd);
524
525/**
526 * @brief fills \p sd with provided \p limit in appropriate fields of \p sd
527 *
528 * sets granularity bit if necessary
529 *
530 * @param[in] limit 32-bit value representing number of limit bytes
531 * @param[out] sd descriptor being filled with \p limit
532 */
533extern void i386_fill_segment_desc_limit (uint32_t limit,
534                                          segment_descriptors* sd);
535
536/*
537 * C callable function enabling to set up one raw interrupt handler
538 */
539extern uint32_t i386_set_gdt_entry (uint16_t segment_selector,
540                                    uint32_t base,
541                                    uint32_t limit);
542
543/**
544 * @brief Returns next empty descriptor in GDT.
545 *
546 * Number of descriptors that can be returned depends on \a GDT_SIZE
547 *
548 * @retval  0 FAILED GDT is full
549 * @retval  <1;65535> segment_selector number as index to GDT
550 */
551extern uint16_t i386_next_empty_gdt_entry (void);
552
553/**
554 * @brief Copies GDT entry at index \p segment_selector to structure
555 * pointed to by \p struct_to_fill
556 *
557 * @param[in] segment_selector index to GDT table specifying descriptor to copy
558 * @param[out] struct_to_fill pointer to memory where will be descriptor coppied
559 * @retval  0 FAILED segment_selector out of GDT range
560 * @retval  <1;65535> retrieved segment_selector
561 */
562extern uint16_t i386_cpy_gdt_entry (uint16_t segment_selector,
563                                    segment_descriptors* struct_to_fill);
564
565/**
566 * @brief Returns pointer to GDT table at index given by \p segment_selector
567 *
568 * @param[in] sgmnt_selector index to GDT table for specifying descriptor to get
569 * @retval  NULL FAILED segment_selector out of GDT range
570 * @retval  pointer to GDT table at \p segment_selector
571 */
572extern segment_descriptors* i386_get_gdt_entry (uint16_t sgmnt_selector);
573
574/**
575 * @brief Extracts base address from GDT entry pointed to by \p gdt_entry
576 *
577 * @param[in]  gdt_entry pointer to entry from which base should be retrieved
578 * @retval base address from GDT entry
579*/
580static inline void* i386_base_gdt_entry (segment_descriptors* gdt_entry)
581{
582    return (void*)(gdt_entry->base_address_15_0 |
583            (gdt_entry->base_address_23_16<<16) |
584            (gdt_entry->base_address_31_24<<24));
585}
586
587/**
588 * @brief Extracts limit in bytes from GDT entry pointed to by \p gdt_entry
589 *
590 * @param[in]  gdt_entry pointer to entry from which limit should be retrieved
591 * @retval limit value in bytes from GDT entry
592 */
593extern uint32_t i386_limit_gdt_entry (segment_descriptors* gdt_entry);
594
595/*
596 * See page 11.18 Figure 11-12.
597 *
598 */
599
600typedef struct {
601  unsigned int offset                   : 12;
602  unsigned int page                     : 10;
603  unsigned int directory                : 10;
604}la_bits;
605
606typedef union {
607  la_bits       bits;
608  unsigned int  address;
609}linear_address;
610
611
612/*
613 * See page 11.20 Figure 11-14.
614 *
615 */
616
617typedef struct {
618  unsigned int present                  : 1;
619  unsigned int writable                 : 1;
620  unsigned int user                     : 1;
621  unsigned int write_through            : 1;
622  unsigned int cache_disable            : 1;
623  unsigned int accessed                 : 1;
624  unsigned int reserved1                : 1;
625  unsigned int page_size                : 1;
626  unsigned int reserved2                : 1;
627  unsigned int available                : 3;
628  unsigned int page_frame_address       : 20;
629}page_dir_bits;
630
631typedef union {
632  page_dir_bits bits;
633  unsigned int  dir_entry;
634}page_dir_entry;
635
636typedef struct {
637  unsigned int present                  : 1;
638  unsigned int writable                 : 1;
639  unsigned int user                     : 1;
640  unsigned int write_through            : 1;
641  unsigned int cache_disable            : 1;
642  unsigned int accessed                 : 1;
643  unsigned int dirty                    : 1;
644  unsigned int reserved2                : 2;
645  unsigned int available                : 3;
646  unsigned int page_frame_address       : 20;
647}page_table_bits;
648
649typedef union {
650  page_table_bits       bits;
651  unsigned int          table_entry;
652} page_table_entry;
653
654/*
655 * definitions related to page table entry
656 */
657#define PG_SIZE 0x1000
658#define MASK_OFFSET 0xFFF
659#define MAX_ENTRY (PG_SIZE/sizeof(page_dir_entry))
660#define FOUR_MB       0x400000
661#define MASK_FLAGS 0x1A
662
663#define PTE_PRESENT             0x01
664#define PTE_WRITABLE            0x02
665#define PTE_USER                0x04
666#define PTE_WRITE_THROUGH       0x08
667#define PTE_CACHE_DISABLE       0x10
668
669typedef struct {
670  page_dir_entry pageDirEntry[MAX_ENTRY];
671} page_directory;
672
673typedef struct {
674  page_table_entry pageTableEntry[MAX_ENTRY];
675} page_table;
676
677/* Simpler names for the i80x86 I/O instructions */
678#define outport_byte( _port, _value ) i386_outport_byte( _port, _value )
679#define outport_word( _port, _value ) i386_outport_word( _port, _value )
680#define outport_long( _port, _value ) i386_outport_long( _port, _value )
681#define inport_byte( _port, _value )  i386_inport_byte( _port, _value )
682#define inport_word( _port, _value )  i386_inport_word( _port, _value )
683#define inport_long( _port, _value )  i386_inport_long( _port, _value )
684
685#ifdef __cplusplus
686}
687#endif
688
689#endif /* !ASM */
690
691#endif
Note: See TracBrowser for help on using the repository browser.