| /* | |
| * LDT manipulation functions | |
| * | |
| * Copyright 1993 Robert J. Amstadt | |
| * Copyright 1995 Alexandre Julliard | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| */ | |
| #include "config.h" | |
| #include "wine/port.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; | |
| unsigned int useable: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; | |
| ldt_info.useable = entry->HighWord.Bits.Sys; | |
| if ((ret = modify_ldt(0x11, &ldt_info, sizeof(ldt_info))) < 0) | |
| perror( "modify_ldt" ); | |
| } | |
| #endif /* linux */ | |
| #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) | |
| { | |
| LDT_ENTRY entry_copy = *entry; | |
| /* The kernel will only let us set LDTs with user priority level */ | |
| if (entry_copy.HighWord.Bits.Pres | |
| && entry_copy.HighWord.Bits.Dpl != 3) | |
| entry_copy.HighWord.Bits.Dpl = 3; | |
| ret = i386_set_ldt(index, (union descriptor *)&entry_copy, 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; | |
| } | |
| /*********************************************************************** | |
| * selector access functions | |
| */ | |
| #ifdef __i386__ | |
| # ifndef _MSC_VER | |
| /* Nothing needs to be done for MS C, it will do with inline versions from the winnt.h */ | |
| __ASM_GLOBAL_FUNC( wine_get_cs, "movw %cs,%ax\n\tret" ) | |
| __ASM_GLOBAL_FUNC( wine_get_ds, "movw %ds,%ax\n\tret" ) | |
| __ASM_GLOBAL_FUNC( wine_get_es, "movw %es,%ax\n\tret" ) | |
| __ASM_GLOBAL_FUNC( wine_get_fs, "movw %fs,%ax\n\tret" ) | |
| __ASM_GLOBAL_FUNC( wine_get_gs, "movw %gs,%ax\n\tret" ) | |
| __ASM_GLOBAL_FUNC( wine_get_ss, "movw %ss,%ax\n\tret" ) | |
| __ASM_GLOBAL_FUNC( wine_set_fs, "movl 4(%esp),%eax\n\tmovw %ax,%fs\n\tret" ) | |
| __ASM_GLOBAL_FUNC( wine_set_gs, "movl 4(%esp),%eax\n\tmovw %ax,%gs\n\tret" ) | |
| # endif /* defined(_MSC_VER) */ | |
| #endif /* defined(__i386__) */ |