[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