[PATCH 5/8] i386: global descriptor table manipulation functions

Jan Dolezal dolezj21 at fel.cvut.cz
Thu Nov 20 14:00:30 UTC 2014


---
 c/src/lib/libbsp/i386/shared/irq/idt.c | 156 +++++++++++++++++++++++++--------
 c/src/lib/libcpu/i386/cpu.h            |  91 ++++++++++++++++++-
 2 files changed, 207 insertions(+), 40 deletions(-)

diff --git a/c/src/lib/libbsp/i386/shared/irq/idt.c b/c/src/lib/libbsp/i386/shared/irq/idt.c
index b79c60a..fc9364e 100644
--- a/c/src/lib/libbsp/i386/shared/irq/idt.c
+++ b/c/src/lib/libbsp/i386/shared/irq/idt.c
@@ -229,50 +229,37 @@ int i386_get_idt_config (rtems_raw_irq_global_settings** config)
   return 1;
 }
 
-/*
- * Caution this function assumes the GDTR has been already set.
- */
-int i386_set_gdt_entry (unsigned short segment_selector, unsigned base,
-			unsigned limit)
+uint32_t i386_raw_gdt_entry (uint16_t segment_selector_index,
+                             segment_descriptors* sd)
 {
-    unsigned 			gdt_limit;
-    unsigned short              tmp_segment = 0;
-    unsigned int                limit_adjusted;
-    segment_descriptors* 	gdt_entry_tbl;
+    uint16_t                gdt_limit;
+    uint16_t                tmp_segment = 0;
+    segment_descriptors*    gdt_entry_tbl;
+    uint8_t                 present;
 
     i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
 
-    if (segment_selector > limit) {
+    if (segment_selector_index >= (gdt_limit+1)/8) {
+      /* index to GDT table out of bounds */
       return 0;
     }
-    /*
-     * set up limit first
-     */
-    limit_adjusted = limit;
-    if ( limit > 4095 ) {
-      gdt_entry_tbl[segment_selector].granularity = 1;
-      limit_adjusted /= 4096;
+    if (segment_selector_index == 0) {
+      /* index 0 is not usable */
+      return 0;
     }
-    gdt_entry_tbl[segment_selector].limit_15_0  = limit_adjusted & 0xffff;
-    gdt_entry_tbl[segment_selector].limit_19_16 = (limit_adjusted >> 16) & 0xf;
-    /*
-     * set up base
-     */
-    gdt_entry_tbl[segment_selector].base_address_15_0  = base & 0xffff;
-    gdt_entry_tbl[segment_selector].base_address_23_16 = (base >> 16) & 0xff;
-    gdt_entry_tbl[segment_selector].base_address_31_24 = (base >> 24) & 0xff;
-    /*
-     * set up descriptor type (this may well becomes a parameter if needed)
-     */
-    gdt_entry_tbl[segment_selector].type 		= 2;   	/* Data R/W */
-    gdt_entry_tbl[segment_selector].descriptor_type 	= 1;	/* Code or Data */
-    gdt_entry_tbl[segment_selector].privilege 		= 0; 	/* ring 0 */
-    gdt_entry_tbl[segment_selector].present 		= 1; 	/* not present */
 
+    /* put prepared descriptor into the GDT */
+    present = sd->present;
+    sd->present = 0;
+    gdt_entry_tbl[segment_selector_index].present = 0;
+    RTEMS_COMPILER_MEMORY_BARRIER();
+    gdt_entry_tbl[segment_selector_index] = *sd;
+    RTEMS_COMPILER_MEMORY_BARRIER();
+    gdt_entry_tbl[segment_selector_index].present = present;
+    sd->present = present;
     /*
-     * Now, reload all segment registers so the limit takes effect.
+     * Now, reload all segment registers so that the possible changes takes effect.
      */
-
     __asm__ volatile( "movw %%ds,%0 ; movw %0,%%ds\n\t"
                   "movw %%es,%0 ; movw %0,%%es\n\t"
                   "movw %%fs,%0 ; movw %0,%%fs\n\t"
@@ -280,7 +267,104 @@ int i386_set_gdt_entry (unsigned short segment_selector, unsigned base,
                   "movw %%ss,%0 ; movw %0,%%ss"
                    : "=r" (tmp_segment)
                    : "0"  (tmp_segment)
-		  );
-
+                 );
     return 1;
 }
+
+void i386_fill_segment_desc_base(uint32_t base,
+                                 segment_descriptors* sd)
+{
+    sd->base_address_15_0  = base & 0xffff;
+    sd->base_address_23_16 = (base >> 16) & 0xff;
+    sd->base_address_31_24 = (base >> 24) & 0xff;
+}
+
+void i386_fill_segment_desc_limit(uint32_t limit,
+                                  segment_descriptors* sd)
+{
+    sd->granularity = 0;
+    if (limit > 65535) {
+      sd->granularity = 1;
+      limit /= 4096;
+    }
+    sd->limit_15_0  = limit & 0xffff;
+    sd->limit_19_16 = (limit >> 16) & 0xf;
+}
+
+/*
+ * Caution this function assumes the GDTR has been already set.
+ */
+uint32_t i386_set_gdt_entry (uint16_t segment_selector_index, uint32_t base,
+                             uint32_t limit)
+{
+    segment_descriptors     gdt_entry;
+    memset(&gdt_entry, 0, sizeof(gdt_entry));
+
+    i386_fill_segment_desc_limit(limit, &gdt_entry);
+    i386_fill_segment_desc_base(base, &gdt_entry);
+    /*
+     * set up descriptor type (this may well becomes a parameter if needed)
+     */
+    gdt_entry.type              = 2;    /* Data R/W */
+    gdt_entry.descriptor_type   = 1;    /* Code or Data */
+    gdt_entry.privilege         = 0;    /* ring 0 */
+    gdt_entry.present           = 1;    /* not present */
+
+    /*
+     * Now, reload all segment registers so the limit takes effect.
+     */
+    return i386_raw_gdt_entry(segment_selector_index, &gdt_entry);
+}
+
+uint16_t i386_next_empty_gdt_entry ()
+{
+    uint16_t                gdt_limit;
+    segment_descriptors*    gdt_entry_tbl;
+    /* initial amount of filled descriptors */
+    static uint16_t         segment_selector_index = 2;
+
+    segment_selector_index += 1;
+    i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
+    if (segment_selector_index >= (gdt_limit+1)/8) {
+      return 0;
+    }
+    return segment_selector_index;
+}
+
+uint16_t i386_cpy_gdt_entry(uint16_t segment_selector_index,
+                            segment_descriptors* struct_to_fill)
+{
+    uint16_t                gdt_limit;
+    segment_descriptors*    gdt_entry_tbl;
+
+    i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
+
+    if (segment_selector_index >= (gdt_limit+1)/8) {
+      return 0;
+    }
+
+    *struct_to_fill = gdt_entry_tbl[segment_selector_index];
+    return segment_selector_index;
+}
+
+segment_descriptors* i386_get_gdt_entry(uint16_t segment_selector_index)
+{
+    uint16_t                gdt_limit;
+    segment_descriptors*    gdt_entry_tbl;
+
+    i386_get_info_from_GDTR (&gdt_entry_tbl, &gdt_limit);
+
+    if (segment_selector_index >= (gdt_limit+1)/8) {
+      return 0;
+    }
+    return &gdt_entry_tbl[segment_selector_index];
+}
+
+uint32_t i386_limit_gdt_entry(segment_descriptors* gdt_entry)
+{
+    uint32_t lim = (gdt_entry->limit_15_0 + (gdt_entry->limit_19_16<<16));
+    if (gdt_entry->granularity) {
+      return lim*4096+4095;
+    }
+    return lim;
+}
diff --git a/c/src/lib/libcpu/i386/cpu.h b/c/src/lib/libcpu/i386/cpu.h
index e14acc4..23a82de 100644
--- a/c/src/lib/libcpu/i386/cpu.h
+++ b/c/src/lib/libcpu/i386/cpu.h
@@ -28,7 +28,7 @@
  */
 #include <rtems/score/interrupts.h>
 
-#include <stdint.h>
+#include <rtems/score/basedefs.h>
 
 /*
  *  Segment Access Routines
@@ -256,7 +256,7 @@ typedef struct {
   unsigned int operation_size		: 1;
   unsigned int granularity		: 1;
   unsigned int base_address_31_24	: 8;
-}segment_descriptors;
+} RTEMS_COMPILER_PACKED_ATTRIBUTE segment_descriptors;
 
 /*
  * C callable function enabling to get easilly usable info from
@@ -271,11 +271,94 @@ extern void i386_get_info_from_GDTR (segment_descriptors** table,
 extern void i386_set_GDTR (segment_descriptors*,
                            uint16_t limit);
 
+/**
+ * C callable function:
+ * Puts global descriptor @sd to the global descriptor table on index
+ * @segment_selector_index
+ *
+ * @retval  0 FAILED out of GDT range or index is 0, which is not valid
+ *                   index in GDT
+ *          1 SUCCESS
+ */
+extern uint32_t i386_raw_gdt_entry (uint16_t segment_selector_index,
+                               segment_descriptors* sd);
+
+/**
+ * C callable function
+ * fills @sd with provided @base in appropriate fields of @sd
+ *
+ * @param base 32-bit address to be set as descriptor's base
+ * @param sd descriptor being filled with @base
+ */
+extern void i386_fill_segment_desc_base (uint32_t base,
+                                         segment_descriptors* sd);
+
+/**
+ * C callable function
+ * fills @sd with provided @limit in appropriate fields of @sd
+ * also influences granularity bit
+ *
+ * @param limit 32-bit value representing number of limit bytes
+ * @param sd descriptor being filled with @limit
+ */
+extern void i386_fill_segment_desc_limit (uint32_t limit,
+                                          segment_descriptors* sd);
+
 /*
  * C callable function enabling to set up one raw interrupt handler
  */
-extern int i386_set_gdt_entry (unsigned short segment_selector, unsigned base,
-					     unsigned limit);
+extern uint32_t i386_set_gdt_entry (uint16_t segment_selector,
+                                    uint32_t base,
+                                    uint32_t limit);
+
+/**
+ * C callable function returns next empty descriptor in GDT.
+ *
+ * @retval  0 FAILED GDT is full
+ *          <1;65535> segment_selector number as index to GDT
+ */
+extern uint16_t i386_next_empty_gdt_entry (void);
+
+/**
+ * Copies GDT entry at index @segment_selector to structure
+ * pointed to by @struct_to_fill
+ *
+ * @param  segment_selector index to GDT table for specifying descriptor to copy
+ * @retval  0 FAILED segment_selector out of GDT range
+ *          <1;65535> retrieved segment_selector
+ */
+extern uint16_t i386_cpy_gdt_entry (uint16_t segment_selector,
+                                    segment_descriptors* struct_to_fill);
+
+/**
+ * Returns pointer to GDT table at index given by @segment_selector
+ *
+ * @param   segment_selector index to GDT table for specifying descriptor to get
+ * @retval  NULL FAILED segment_selector out of GDT range
+ *          pointer to GDT table at @segment_selector
+ */
+extern segment_descriptors* i386_get_gdt_entry (uint16_t sgmnt_selector);
+
+/**
+ * Extracts base address from GDT entry pointed to by @gdt_entry
+ *
+ * @param  gdt_entry pointer to entry from which base should be retrieved
+ * @retval base address from GDT entry
+*/
+RTEMS_INLINE_ROUTINE void* i386_base_gdt_entry (segment_descriptors* gdt_entry)
+{
+    return (void*)(gdt_entry->base_address_15_0 |
+            (gdt_entry->base_address_23_16<<16) |
+            (gdt_entry->base_address_31_24<<24));
+}
+
+/**
+ * Extracts limit in bytes from GDT entry pointed to by @gdt_entry
+ *
+ * @param  gdt_entry pointer to entry from which limit should be retrieved
+ * @retval limit value in bytes from GDT entry
+ */
+extern uint32_t i386_limit_gdt_entry (segment_descriptors* gdt_entry);
 
 /*
  * See page 11.18 Figure 11-12.
-- 
1.9.1




More information about the devel mailing list