--- c/src/lib/libbsp/i386/shared/irq/idt.c | 147 +++++++++++++++++++++++++-------- c/src/lib/libcpu/i386/cpu.h | 83 ++++++++++++++++++- 2 files changed, 194 insertions(+), 36 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..e6f1d57 100644 --- a/c/src/lib/libbsp/i386/shared/irq/idt.c +++ b/c/src/lib/libbsp/i386/shared/irq/idt.c @@ -229,50 +229,33 @@ 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) +int i386_raw_gdt_entry (unsigned short segment_selector_index, segment_descriptors* sd) { - unsigned gdt_limit; + unsigned gdt_limit; unsigned short tmp_segment = 0; - unsigned int limit_adjusted; - segment_descriptors* gdt_entry_tbl; + segment_descriptors* gdt_entry_tbl; + unsigned int 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] = *sd; + sd->present = present; + gdt_entry_tbl[segment_selector_index].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 +263,101 @@ 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; } + +inline void i386_fill_segment_desc_base(unsigned 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; +} + +inline void i386_fill_segment_desc_limit(unsigned 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. + */ +int i386_set_gdt_entry (unsigned short segment_selector_index, unsigned base, + unsigned 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); +} + +unsigned short i386_next_empty_gdt_entry () +{ + unsigned gdt_limit; + segment_descriptors* gdt_entry_tbl; + /* initial amount of filled descriptors */ + static unsigned short 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; +} + +unsigned short i386_cpy_gdt_entry(unsigned short segment_selector_index, segment_descriptors* strucToFill) +{ + unsigned 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; + } + + *strucToFill = gdt_entry_tbl[segment_selector_index]; + return segment_selector_index; +} + +segment_descriptors* i386_get_gdt_entry(unsigned short segment_selector_index) +{ + unsigned 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]; +} + +unsigned i386_limit_gdt_entry(segment_descriptors* gdt_entry) +{ + unsigned 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 4c56731..049a35c 100644 --- a/c/src/lib/libcpu/i386/cpu.h +++ b/c/src/lib/libcpu/i386/cpu.h @@ -269,11 +269,92 @@ extern void i386_get_info_from_GDTR (segment_descriptors** table, extern void i386_set_GDTR (segment_descriptors*, unsigned limit); +/** + * C callable function: + * Puts global descriptor @sd to the global descriptor table on index + * @segment_selector_index + * + * @return 0 FAILED out of GDT range or index is 0, which is not valid + * index in GDT + * 1 SUCCESS + */ +extern int i386_raw_gdt_entry ( unsigned short 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(unsigned 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(unsigned 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); + unsigned limit); + +/** + * C callable function returns next empty descriptor in GDT. + * + * @return 0 FAILED GDT is full + * <1;65535> segment_selector number as index to GDT + */ +extern unsigned short i386_next_empty_gdt_entry (void); + +/** + * Copies GDT entry at index @segment_selector to structure + * pointed to by @strucToFill + * + * @param segment_selector index to GDT table for specifying descriptor to copy + * @return 0 FAILED segment_selector out of GDT range + * <1;65535> retrieved segment_selector + */ +extern unsigned short i386_cpy_gdt_entry(unsigned short segment_selector, + segment_descriptors* strucToFill); + +/** + * Returns pointer to GDT table at index given by @segment_selector + * + * @param segment_selector index to GDT table for specifying descriptor to get + * @return NULL FAILED segment_selector out of GDT range + * pointer to GDT table at @segment_selector + */ +extern segment_descriptors* i386_get_gdt_entry(unsigned short segment_selector); + +/** + * Extracts base address from GDT entry pointed to by @gdt_entry + * + * @param gdt_entry pointer to entry from which base should be retrieved + * @return base address from GDT entry +*/ +extern inline 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 + * @return limit value in bytes from GDT entry + */ +extern unsigned i386_limit_gdt_entry(segment_descriptors* gdt_entry); /* * See page 11.18 Figure 11-12. -- 1.9.1 _______________________________________________ devel mailing list devel@rtems.org http://lists.rtems.org/mailman/listinfo/devel