From 74d2d9404142020a5da9d105f8af6f1d29e271ce Mon Sep 17 00:00:00 2001 From: Jan Dolezal Date: Thu, 20 Nov 2014 15:00:30 +0100 Subject: i386: global descriptor table manipulation functions --- 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 b79c60a91d..fc9364eba4 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 e14acc4ce7..23a82de2be 100644 --- a/c/src/lib/libcpu/i386/cpu.h +++ b/c/src/lib/libcpu/i386/cpu.h @@ -28,7 +28,7 @@ */ #include -#include +#include /* * 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. -- cgit v1.2.3