|  | /* | 
|  | * LDT manipulation functions | 
|  | * | 
|  | * Copyright 1993 Robert J. Amstadt | 
|  | * Copyright 1995 Alexandre Julliard | 
|  | */ | 
|  |  | 
|  | #include "config.h" | 
|  |  | 
|  | #include <stdlib.h> | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  | #include <errno.h> | 
|  |  | 
|  | #include "winbase.h" | 
|  | #include "wine/library.h" | 
|  |  | 
|  | #ifdef __i386__ | 
|  |  | 
|  | #ifdef linux | 
|  |  | 
|  | #ifdef HAVE_SYS_SYSCALL_H | 
|  | # include <sys/syscall.h> | 
|  | #endif | 
|  |  | 
|  | struct modify_ldt_s | 
|  | { | 
|  | unsigned int  entry_number; | 
|  | unsigned long base_addr; | 
|  | unsigned int  limit; | 
|  | unsigned int  seg_32bit : 1; | 
|  | unsigned int  contents : 2; | 
|  | unsigned int  read_exec_only : 1; | 
|  | unsigned int  limit_in_pages : 1; | 
|  | unsigned int  seg_not_present : 1; | 
|  | }; | 
|  |  | 
|  | static inline int modify_ldt( int func, struct modify_ldt_s *ptr, | 
|  | unsigned long count ) | 
|  | { | 
|  | int res; | 
|  | #ifdef __PIC__ | 
|  | __asm__ __volatile__( "pushl %%ebx\n\t" | 
|  | "movl %2,%%ebx\n\t" | 
|  | "int $0x80\n\t" | 
|  | "popl %%ebx" | 
|  | : "=a" (res) | 
|  | : "0" (SYS_modify_ldt), | 
|  | "r" (func), | 
|  | "c" (ptr), | 
|  | "d" (count) ); | 
|  | #else | 
|  | __asm__ __volatile__("int $0x80" | 
|  | : "=a" (res) | 
|  | : "0" (SYS_modify_ldt), | 
|  | "b" (func), | 
|  | "c" (ptr), | 
|  | "d" (count) ); | 
|  | #endif  /* __PIC__ */ | 
|  | if (res >= 0) return res; | 
|  | errno = -res; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | #endif  /* linux */ | 
|  |  | 
|  | #if defined(__svr4__) || defined(_SCO_DS) | 
|  | #include <sys/sysi86.h> | 
|  | extern int sysi86(int,void*); | 
|  | #ifndef __sun__ | 
|  | #include <sys/seg.h> | 
|  | #endif | 
|  | #endif | 
|  |  | 
|  | #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) | 
|  | #include <machine/segments.h> | 
|  |  | 
|  | extern int i386_get_ldt(int, union descriptor *, int); | 
|  | extern int i386_set_ldt(int, union descriptor *, int); | 
|  | #endif  /* __NetBSD__ || __FreeBSD__ || __OpenBSD__ */ | 
|  |  | 
|  | #endif  /* __i386__ */ | 
|  |  | 
|  | /* local copy of the LDT */ | 
|  | struct __wine_ldt_copy wine_ldt_copy; | 
|  |  | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           ldt_get_entry | 
|  | * | 
|  | * Retrieve an LDT entry. | 
|  | */ | 
|  | void wine_ldt_get_entry( unsigned short sel, LDT_ENTRY *entry ) | 
|  | { | 
|  | int index = sel >> 3; | 
|  | wine_ldt_set_base(  entry, wine_ldt_copy.base[index] ); | 
|  | wine_ldt_set_limit( entry, wine_ldt_copy.limit[index] ); | 
|  | wine_ldt_set_flags( entry, wine_ldt_copy.flags[index] ); | 
|  | } | 
|  |  | 
|  |  | 
|  | /*********************************************************************** | 
|  | *           ldt_set_entry | 
|  | * | 
|  | * Set an LDT entry. | 
|  | */ | 
|  | int wine_ldt_set_entry( unsigned short sel, const LDT_ENTRY *entry ) | 
|  | { | 
|  | int ret = 0, index = sel >> 3; | 
|  |  | 
|  | /* Entry 0 must not be modified; its base and limit are always 0 */ | 
|  | if (!index) return 0; | 
|  |  | 
|  | #ifdef __i386__ | 
|  |  | 
|  | #ifdef linux | 
|  | { | 
|  | struct modify_ldt_s ldt_info; | 
|  |  | 
|  | ldt_info.entry_number    = index; | 
|  | ldt_info.base_addr       = (unsigned long)wine_ldt_get_base(entry); | 
|  | ldt_info.limit           = entry->LimitLow | (entry->HighWord.Bits.LimitHi << 16); | 
|  | ldt_info.seg_32bit       = entry->HighWord.Bits.Default_Big; | 
|  | ldt_info.contents        = (entry->HighWord.Bits.Type >> 2) & 3; | 
|  | ldt_info.read_exec_only  = !(entry->HighWord.Bits.Type & 2); | 
|  | ldt_info.limit_in_pages  = entry->HighWord.Bits.Granularity; | 
|  | ldt_info.seg_not_present = !entry->HighWord.Bits.Pres; | 
|  |  | 
|  | if ((ret = modify_ldt(1, &ldt_info, sizeof(ldt_info))) < 0) | 
|  | perror( "modify_ldt" ); | 
|  | } | 
|  | #endif  /* linux */ | 
|  |  | 
|  | #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) | 
|  | { | 
|  | ret = i386_set_ldt(index, (union descriptor *)entry, 1); | 
|  | if (ret < 0) | 
|  | { | 
|  | perror("i386_set_ldt"); | 
|  | fprintf( stderr, "Did you reconfigure the kernel with \"options USER_LDT\"?\n" ); | 
|  | exit(1); | 
|  | } | 
|  | } | 
|  | #endif  /* __NetBSD__ || __FreeBSD__ || __OpenBSD__ */ | 
|  |  | 
|  | #if defined(__svr4__) || defined(_SCO_DS) | 
|  | { | 
|  | struct ssd ldt_mod; | 
|  | ldt_mod.sel  = sel; | 
|  | ldt_mod.bo   = (unsigned long)wine_ldt_get_base(entry); | 
|  | ldt_mod.ls   = entry->LimitLow | (entry->HighWord.Bits.LimitHi << 16); | 
|  | ldt_mod.acc1 = entry->HighWord.Bytes.Flags1; | 
|  | ldt_mod.acc2 = entry->HighWord.Bytes.Flags2 >> 4; | 
|  | if ((ret = sysi86(SI86DSCR, &ldt_mod)) == -1) perror("sysi86"); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #endif  /* __i386__ */ | 
|  |  | 
|  | if (ret >= 0) | 
|  | { | 
|  | wine_ldt_copy.base[index]  = wine_ldt_get_base(entry); | 
|  | wine_ldt_copy.limit[index] = wine_ldt_get_limit(entry); | 
|  | wine_ldt_copy.flags[index] = (entry->HighWord.Bits.Type | | 
|  | (entry->HighWord.Bits.Default_Big ? WINE_LDT_FLAGS_32BIT : 0) | | 
|  | (wine_ldt_copy.flags[index] & WINE_LDT_FLAGS_ALLOCATED)); | 
|  | } | 
|  | return ret; | 
|  | } |