From d25e93e6643c59ba9b80ff9d0ddb7b39bffacb04 Mon Sep 17 00:00:00 2001 From: David Mosberger Date: Mon, 20 Oct 2003 22:34:19 -0700 Subject: [PATCH] ia64: Based on patch by Arun Sharma: fix IA-32 subsystem to support NPTL. --- arch/ia64/ia32/binfmt_elf32.c | 6 +- arch/ia64/ia32/ia32_entry.S | 16 ++--- arch/ia64/ia32/ia32_ldt.c | 2 +- arch/ia64/ia32/ia32_support.c | 97 ++++++++++++++++++++---------- arch/ia64/ia32/ia32priv.h | 55 +++++++++++++++-- arch/ia64/ia32/sys_ia32.c | 108 ++++++++++++++++++++++++++++++++++ arch/ia64/kernel/process.c | 8 ++- arch/ia64/kernel/smpboot.c | 4 ++ arch/ia64/mm/init.c | 2 +- include/asm-ia64/ia32.h | 2 + include/asm-ia64/processor.h | 19 ++++++ 11 files changed, 271 insertions(+), 48 deletions(-) diff --git a/arch/ia64/ia32/binfmt_elf32.c b/arch/ia64/ia32/binfmt_elf32.c index 000a94b3ee810..8f29963bcd90e 100644 --- a/arch/ia64/ia32/binfmt_elf32.c +++ b/arch/ia64/ia32/binfmt_elf32.c @@ -62,14 +62,16 @@ extern unsigned long *ia32_gdt; struct page * ia32_install_shared_page (struct vm_area_struct *vma, unsigned long address, int no_share) { - struct page *pg = ia32_shared_page[(address - vma->vm_start)/PAGE_SIZE]; +# define NUM_SHARED_PAGES (PAGE_SIZE > IA32_PAGE_SIZE ? 1 : 2) + struct page *pg = ia32_shared_page[NUM_SHARED__PAGES * smp_processor_id() + + (address - vma->vm_start)/PAGE_SIZE]; get_page(pg); return pg; } static struct vm_operations_struct ia32_shared_page_vm_ops = { - .nopage =ia32_install_shared_page + .nopage = ia32_install_shared_page }; void diff --git a/arch/ia64/ia32/ia32_entry.S b/arch/ia64/ia32/ia32_entry.S index ff4976ebcdf71..eb95ae85ce9b9 100644 --- a/arch/ia64/ia32/ia32_entry.S +++ b/arch/ia64/ia32/ia32_entry.S @@ -32,7 +32,7 @@ END(ia32_execve) ENTRY(ia32_clone) .prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(2) - alloc r16=ar.pfs,2,2,4,0 + alloc r16=ar.pfs,5,2,6,0 DO_SAVE_SWITCH_STACK mov loc0=rp mov loc1=r16 // save ar.pfs across do_fork @@ -41,6 +41,8 @@ ENTRY(ia32_clone) mov out3=16 // stacksize (compensates for 16-byte scratch area) adds out2=IA64_SWITCH_STACK_SIZE+16,sp // out2 = ®s dep out0=0,in0,CLONE_IDLETASK_BIT,1 // out0 = clone_flags & ~CLONE_IDLETASK + zxt4 out4=in2 // out4 = parent_tidptr + zxt4 out5=in4 // out5 = child_tidptr br.call.sptk.many rp=do_fork .ret0: .restore sp adds sp=IA64_SWITCH_STACK_SIZE,sp // pop the switch stack @@ -437,13 +439,13 @@ ia32_syscall_table: data8 sys_ni_syscall /* 235 */ data8 sys_ni_syscall data8 sys_ni_syscall - data8 sys_ni_syscall + data8 sys_tkill data8 sys_ni_syscall data8 compat_sys_futex /* 240 */ data8 compat_sys_sched_setaffinity data8 compat_sys_sched_getaffinity - data8 sys_ni_syscall - data8 sys_ni_syscall + data8 sys32_set_thread_area + data8 sys32_get_thread_area data8 sys_ni_syscall /* 245 */ data8 sys_ni_syscall data8 sys_ni_syscall @@ -451,13 +453,13 @@ ia32_syscall_table: data8 sys_ni_syscall data8 sys_ni_syscall /* 250 */ data8 sys_ni_syscall - data8 sys_ni_syscall + data8 sys_exit_group data8 sys_ni_syscall data8 sys_epoll_create data8 sys32_epoll_ctl /* 255 */ data8 sys32_epoll_wait - data8 sys_ni_syscall - data8 sys_ni_syscall + data8 sys_remap_file_pages + data8 sys_set_tid_address data8 sys_ni_syscall data8 sys_ni_syscall /* 260 */ data8 sys_ni_syscall diff --git a/arch/ia64/ia32/ia32_ldt.c b/arch/ia64/ia32/ia32_ldt.c index 515503ae08350..4cc8242de8c7d 100644 --- a/arch/ia64/ia32/ia32_ldt.c +++ b/arch/ia64/ia32/ia32_ldt.c @@ -82,7 +82,7 @@ read_default_ldt (void * ptr, unsigned long bytecount) static int write_ldt (void * ptr, unsigned long bytecount, int oldmode) { - struct ia32_modify_ldt_ldt_s ldt_info; + struct ia32_user_desc ldt_info; __u64 entry; int ret; diff --git a/arch/ia64/ia32/ia32_support.c b/arch/ia64/ia32/ia32_support.c index 4d373270fe3b1..6f32b4e519d9e 100644 --- a/arch/ia64/ia32/ia32_support.c +++ b/arch/ia64/ia32/ia32_support.c @@ -23,14 +23,16 @@ #include #include #include +#include #include "ia32priv.h" extern void die_if_kernel (char *str, struct pt_regs *regs, long err); struct exec_domain ia32_exec_domain; -struct page *ia32_shared_page[(2*IA32_PAGE_SIZE + PAGE_SIZE - 1)/PAGE_SIZE]; -unsigned long *ia32_gdt; +struct page *ia32_shared_page[(PAGE_ALIGN(IA32_PAGE_SIZE)/PAGE_SIZE) * NR_CPUS]; +unsigned long *ia32_boot_gdt; +unsigned long *cpu_gdt_table[NR_CPUS]; static unsigned long load_desc (u16 selector) @@ -43,8 +45,8 @@ load_desc (u16 selector) table = (unsigned long *) IA32_LDT_OFFSET; limit = IA32_LDT_ENTRIES; } else { - table = ia32_gdt; - limit = IA32_PAGE_SIZE / sizeof(ia32_gdt[0]); + table = cpu_gdt_table[smp_processor_id()]; + limit = IA32_PAGE_SIZE / sizeof(ia32_boot_gdt[0]); } index = selector >> IA32_SEGSEL_INDEX_SHIFT; if (index >= limit) @@ -66,6 +68,34 @@ ia32_load_segment_descriptors (struct task_struct *task) regs->ar_ssd = load_desc(regs->r17 >> 16); /* SSD */ } +int +ia32_clone_tls (struct task_struct *child, struct pt_regs *childregs) +{ + struct desc_struct *desc; + struct ia32_user_desc info; + int idx; + + if (copy_from_user(&info, (void *)(childregs->r14 & 0xffffffff), sizeof(info))) + return -EFAULT; + if (LDT_empty(&info)) + return -EINVAL; + + idx = info.entry_number; + if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) + return -EINVAL; + + desc = child->thread.tls_array + idx - GDT_ENTRY_TLS_MIN; + desc->a = LDT_entry_a(&info); + desc->b = LDT_entry_b(&info); + + /* XXX: can this be done in a cleaner way ? */ + load_TLS(&child->thread, smp_processor_id()); + ia32_load_segment_descriptors(child); + load_TLS(¤t->thread, smp_processor_id()); + + return 0; +} + void ia32_save_state (struct task_struct *t) { @@ -83,14 +113,13 @@ ia32_load_state (struct task_struct *t) { unsigned long eflag, fsr, fcr, fir, fdr, tssd; struct pt_regs *regs = ia64_task_regs(t); - int nr = get_cpu(); /* LDT and TSS depend on CPU number: */ eflag = t->thread.eflag; fsr = t->thread.fsr; fcr = t->thread.fcr; fir = t->thread.fir; fdr = t->thread.fdr; - tssd = load_desc(_TSS(nr)); /* TSSD */ + tssd = load_desc(_TSS); /* TSSD */ ia64_setreg(_IA64_REG_AR_EFLAG, eflag); ia64_setreg(_IA64_REG_AR_FSR, fsr); @@ -102,8 +131,10 @@ ia32_load_state (struct task_struct *t) ia64_set_kr(IA64_KR_IO_BASE, IA32_IOBASE); ia64_set_kr(IA64_KR_TSSD, tssd); - regs->r17 = (_TSS(nr) << 48) | (_LDT(nr) << 32) | (__u32) regs->r17; - regs->r30 = load_desc(_LDT(nr)); /* LDTD */ + regs->r17 = (_TSS << 48) | (_LDT << 32) | (__u32) regs->r17; + regs->r30 = load_desc(_LDT); /* LDTD */ + load_TLS(&t->thread, smp_processor_id()); + put_cpu(); } @@ -113,37 +144,43 @@ ia32_load_state (struct task_struct *t) void ia32_gdt_init (void) { - unsigned long *tss; + int cpu = smp_processor_id(); + + ia32_shared_page[cpu] = alloc_page(GFP_KERNEL); + cpu_gdt_table[cpu] = page_address(ia32_shared_page[cpu]); + + /* Copy from the boot cpu's GDT */ + memcpy(cpu_gdt_table[cpu], ia32_boot_gdt, PAGE_SIZE); +} + + +/* + * Setup IA32 GDT and TSS + */ +void +ia32_boot_gdt_init (void) +{ unsigned long ldt_size; - int nr; ia32_shared_page[0] = alloc_page(GFP_KERNEL); - ia32_gdt = page_address(ia32_shared_page[0]); - tss = ia32_gdt + IA32_PAGE_SIZE/sizeof(ia32_gdt[0]); - - if (IA32_PAGE_SIZE == PAGE_SIZE) { - ia32_shared_page[1] = alloc_page(GFP_KERNEL); - tss = page_address(ia32_shared_page[1]); - } + ia32_boot_gdt = page_address(ia32_shared_page[0]); + cpu_gdt_table[0] = ia32_boot_gdt; /* CS descriptor in IA-32 (scrambled) format */ - ia32_gdt[__USER_CS >> 3] = IA32_SEG_DESCRIPTOR(0, (IA32_PAGE_OFFSET-1) >> IA32_PAGE_SHIFT, - 0xb, 1, 3, 1, 1, 1, 1); + ia32_boot_gdt[__USER_CS >> 3] + = IA32_SEG_DESCRIPTOR(0, (IA32_PAGE_OFFSET-1) >> IA32_PAGE_SHIFT, + 0xb, 1, 3, 1, 1, 1, 1); /* DS descriptor in IA-32 (scrambled) format */ - ia32_gdt[__USER_DS >> 3] = IA32_SEG_DESCRIPTOR(0, (IA32_PAGE_OFFSET-1) >> IA32_PAGE_SHIFT, - 0x3, 1, 3, 1, 1, 1, 1); + ia32_boot_gdt[__USER_DS >> 3] + = IA32_SEG_DESCRIPTOR(0, (IA32_PAGE_OFFSET-1) >> IA32_PAGE_SHIFT, + 0x3, 1, 3, 1, 1, 1, 1); - /* We never change the TSS and LDT descriptors, so we can share them across all CPUs. */ ldt_size = PAGE_ALIGN(IA32_LDT_ENTRIES*IA32_LDT_ENTRY_SIZE); - for (nr = 0; nr < NR_CPUS; ++nr) { - ia32_gdt[_TSS(nr) >> IA32_SEGSEL_INDEX_SHIFT] - = IA32_SEG_DESCRIPTOR(IA32_TSS_OFFSET, 235, - 0xb, 0, 3, 1, 1, 1, 0); - ia32_gdt[_LDT(nr) >> IA32_SEGSEL_INDEX_SHIFT] - = IA32_SEG_DESCRIPTOR(IA32_LDT_OFFSET, ldt_size - 1, - 0x2, 0, 3, 1, 1, 1, 0); - } + ia32_boot_gdt[TSS_ENTRY] = IA32_SEG_DESCRIPTOR(IA32_TSS_OFFSET, 235, + 0xb, 0, 3, 1, 1, 1, 0); + ia32_boot_gdt[LDT_ENTRY] = IA32_SEG_DESCRIPTOR(IA32_LDT_OFFSET, ldt_size - 1, + 0x2, 0, 3, 1, 1, 1, 0); } /* diff --git a/arch/ia64/ia32/ia32priv.h b/arch/ia64/ia32/ia32priv.h index 83c0dd520f83b..e7b4fa608109b 100644 --- a/arch/ia64/ia32/ia32priv.h +++ b/arch/ia64/ia32/ia32priv.h @@ -10,6 +10,8 @@ #include #include +#include + /* * 32 bit structures for IA32 support. */ @@ -327,15 +329,23 @@ void ia64_elf32_init(struct pt_regs *regs); #define __USER_CS 0x23 #define __USER_DS 0x2B -#define FIRST_TSS_ENTRY 6 -#define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY+1) -#define _TSS(n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3)) -#define _LDT(n) ((((unsigned long) n)<<4)+(FIRST_LDT_ENTRY<<3)) +/* + * The per-cpu GDT has 32 entries: see + */ +#define GDT_ENTRIES 32 + +#define GDT_SIZE (GDT_ENTRIES * 8) + +#define TSS_ENTRY 14 +#define LDT_ENTRY (TSS_ENTRY + 1) #define IA32_SEGSEL_RPL (0x3 << 0) #define IA32_SEGSEL_TI (0x1 << 2) #define IA32_SEGSEL_INDEX_SHIFT 3 +#define _TSS ((unsigned long) TSS_ENTRY << IA32_SEGSEL_INDEX_SHIFT) +#define _LDT ((unsigned long) LDT_ENTRY << IA32_SEGSEL_INDEX_SHIFT) + #define IA32_SEG_BASE 16 #define IA32_SEG_TYPE 40 #define IA32_SEG_SYS 44 @@ -419,7 +429,42 @@ void ia64_elf32_init(struct pt_regs *regs); #define IA32_LDT_ENTRIES 8192 /* Maximum number of LDT entries supported. */ #define IA32_LDT_ENTRY_SIZE 8 /* The size of each LDT entry. */ -struct ia32_modify_ldt_ldt_s { +#define LDT_entry_a(info) \ + ((((info)->base_addr & 0x0000ffff) << 16) | ((info)->limit & 0x0ffff)) + +#define LDT_entry_b(info) \ + (((info)->base_addr & 0xff000000) | \ + (((info)->base_addr & 0x00ff0000) >> 16) | \ + ((info)->limit & 0xf0000) | \ + (((info)->read_exec_only ^ 1) << 9) | \ + ((info)->contents << 10) | \ + (((info)->seg_not_present ^ 1) << 15) | \ + ((info)->seg_32bit << 22) | \ + ((info)->limit_in_pages << 23) | \ + ((info)->useable << 20) | \ + 0x7100) + +#define LDT_empty(info) ( \ + (info)->base_addr == 0 && \ + (info)->limit == 0 && \ + (info)->contents == 0 && \ + (info)->read_exec_only == 1 && \ + (info)->seg_32bit == 0 && \ + (info)->limit_in_pages == 0 && \ + (info)->seg_not_present == 1 && \ + (info)->useable == 0 ) + +static inline void +load_TLS (struct thread_struct *t, unsigned int cpu) +{ + extern struct desc_struct *cpu_gdt_table[NR_CPUS]; + + cpu_gdt_table[cpu][GDT_ENTRY_TLS_MIN + 0] = t->tls_array[0]; + cpu_gdt_table[cpu][GDT_ENTRY_TLS_MIN + 1] = t->tls_array[1]; + cpu_gdt_table[cpu][GDT_ENTRY_TLS_MIN + 2] = t->tls_array[2]; +} + +struct ia32_user_desc { unsigned int entry_number; unsigned int base_addr; unsigned int limit; diff --git a/arch/ia64/ia32/sys_ia32.c b/arch/ia64/ia32/sys_ia32.c index 95baf7ddd117a..3ba190a2b6bf9 100644 --- a/arch/ia64/ia32/sys_ia32.c +++ b/arch/ia64/ia32/sys_ia32.c @@ -2817,6 +2817,114 @@ sys32_epoll_wait(int epfd, struct epoll_event32 *events, int maxevents, return numevents; } +/* + * Get a yet unused TLS descriptor index. + */ +static int +get_free_idx (void) +{ + struct thread_struct *t = ¤t->thread; + int idx; + + for (idx = 0; idx < GDT_ENTRY_TLS_ENTRIES; idx++) + if (desc_empty(t->tls_array + idx)) + return idx + GDT_ENTRY_TLS_MIN; + return -ESRCH; +} + +/* + * Set a given TLS descriptor: + */ +asmlinkage int +sys32_set_thread_area (struct ia32_user_desc *u_info) +{ + struct thread_struct *t = ¤t->thread; + struct ia32_user_desc info; + struct desc_struct *desc; + int cpu, idx; + + if (copy_from_user(&info, u_info, sizeof(info))) + return -EFAULT; + idx = info.entry_number; + + /* + * index -1 means the kernel should try to find and allocate an empty descriptor: + */ + if (idx == -1) { + idx = get_free_idx(); + if (idx < 0) + return idx; + if (put_user(idx, &u_info->entry_number)) + return -EFAULT; + } + + if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) + return -EINVAL; + + desc = t->tls_array + idx - GDT_ENTRY_TLS_MIN; + + cpu = smp_processor_id(); + + if (LDT_empty(&info)) { + desc->a = 0; + desc->b = 0; + } else { + desc->a = LDT_entry_a(&info); + desc->b = LDT_entry_b(&info); + } + load_TLS(t, cpu); + return 0; +} + +/* + * Get the current Thread-Local Storage area: + */ + +#define GET_BASE(desc) ( \ + (((desc)->a >> 16) & 0x0000ffff) | \ + (((desc)->b << 16) & 0x00ff0000) | \ + ( (desc)->b & 0xff000000) ) + +#define GET_LIMIT(desc) ( \ + ((desc)->a & 0x0ffff) | \ + ((desc)->b & 0xf0000) ) + +#define GET_32BIT(desc) (((desc)->b >> 23) & 1) +#define GET_CONTENTS(desc) (((desc)->b >> 10) & 3) +#define GET_WRITABLE(desc) (((desc)->b >> 9) & 1) +#define GET_LIMIT_PAGES(desc) (((desc)->b >> 23) & 1) +#define GET_PRESENT(desc) (((desc)->b >> 15) & 1) +#define GET_USEABLE(desc) (((desc)->b >> 20) & 1) + +asmlinkage int +sys32_get_thread_area (struct ia32_user_desc *u_info) +{ + struct ia32_user_desc info; + struct desc_struct *desc; + int idx; + + if (get_user(idx, &u_info->entry_number)) + return -EFAULT; + if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) + return -EINVAL; + + desc = current->thread.tls_array + idx - GDT_ENTRY_TLS_MIN; + + info.entry_number = idx; + info.base_addr = GET_BASE(desc); + info.limit = GET_LIMIT(desc); + info.seg_32bit = GET_32BIT(desc); + info.contents = GET_CONTENTS(desc); + info.read_exec_only = !GET_WRITABLE(desc); + info.limit_in_pages = GET_LIMIT_PAGES(desc); + info.seg_not_present = !GET_PRESENT(desc); + info.useable = GET_USEABLE(desc); + + if (copy_to_user(u_info, &info, sizeof(info))) + return -EFAULT; + return 0; +} + #ifdef NOTYET /* UNTESTED FOR IA64 FROM HERE DOWN */ struct ncp_mount_data32 { diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c index cbefd04e8a050..ff17b9b87a149 100644 --- a/arch/ia64/kernel/process.c +++ b/arch/ia64/kernel/process.c @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -324,7 +325,7 @@ copy_thread (int nr, unsigned long clone_flags, memcpy((void *) child_rbs, (void *) rbs, rbs_size); if (user_mode(child_ptregs)) { - if (clone_flags & CLONE_SETTLS) + if ((clone_flags & CLONE_SETTLS) && !IS_IA32_PROCESS(regs)) child_ptregs->r13 = regs->r16; /* see sys_clone2() in entry.S */ if (user_stack_base) { child_ptregs->r12 = user_stack_base + user_stack_size - 16; @@ -383,8 +384,11 @@ copy_thread (int nr, unsigned long clone_flags, * If we're cloning an IA32 task then save the IA32 extra * state from the current task to the new task */ - if (IS_IA32_PROCESS(ia64_task_regs(current))) + if (IS_IA32_PROCESS(ia64_task_regs(current))) { ia32_save_state(p); + if (clone_flags & CLONE_SETTLS) + retval = ia32_clone_tls(p, child_ptregs); + } #endif #ifdef CONFIG_PERFMON diff --git a/arch/ia64/kernel/smpboot.c b/arch/ia64/kernel/smpboot.c index 554815286ab94..4bf9eed493859 100644 --- a/arch/ia64/kernel/smpboot.c +++ b/arch/ia64/kernel/smpboot.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -312,6 +313,9 @@ smp_callin (void) local_irq_enable(); calibrate_delay(); local_cpu_data->loops_per_jiffy = loops_per_jiffy; +#ifdef CONFIG_IA32_SUPPORT + ia32_gdt_init(); +#endif if (!(sal_platform_features & IA64_SAL_PLATFORM_FEATURE_ITC_DRIFT)) { /* diff --git a/arch/ia64/mm/init.c b/arch/ia64/mm/init.c index fe56b93221c99..65191db78a67e 100644 --- a/arch/ia64/mm/init.c +++ b/arch/ia64/mm/init.c @@ -558,6 +558,6 @@ mem_init (void) setup_gate(); /* setup gate pages before we free up boot memory... */ #ifdef CONFIG_IA32_SUPPORT - ia32_gdt_init(); + ia32_boot_gdt_init(); #endif } diff --git a/include/asm-ia64/ia32.h b/include/asm-ia64/ia32.h index 0694eb941079b..4c8fd32b8b78f 100644 --- a/include/asm-ia64/ia32.h +++ b/include/asm-ia64/ia32.h @@ -9,9 +9,11 @@ #ifdef CONFIG_IA32_SUPPORT extern void ia32_cpu_init (void); +extern void ia32_boot_gdt_init (void); extern void ia32_gdt_init (void); extern int ia32_exception (struct pt_regs *regs, unsigned long isr); extern int ia32_intercept (struct pt_regs *regs, unsigned long isr); +extern int ia32_clone_tls (struct task_struct *child, struct pt_regs *childregs); #endif /* !CONFIG_IA32_SUPPORT */ diff --git a/include/asm-ia64/processor.h b/include/asm-ia64/processor.h index ed83487c5dc1f..5948902ae28b8 100644 --- a/include/asm-ia64/processor.h +++ b/include/asm-ia64/processor.h @@ -230,6 +230,22 @@ typedef struct { (int *) (addr)); \ }) +#ifdef CONFIG_IA32_SUPPORT +struct desc_struct { + unsigned int a, b; +}; + +#define desc_empty(desc) (!((desc)->a + (desc)->b)) +#define desc_equal(desc1, desc2) (((desc1)->a == (desc2)->a) && ((desc1)->b == (desc2)->b)) + +#define GDT_ENTRY_TLS_ENTRIES 3 +#define GDT_ENTRY_TLS_MIN 6 +#define GDT_ENTRY_TLS_MAX (GDT_ENTRY_TLS_MIN + GDT_ENTRY_TLS_ENTRIES - 1) + +#define TLS_SIZE (GDT_ENTRY_TLS_ENTRIES * 8) + +#endif + struct thread_struct { __u32 flags; /* various thread flags (see IA64_THREAD_*) */ /* writing on_ustack is performance-critical, so it's worth spending 8 bits on it... */ @@ -249,6 +265,9 @@ struct thread_struct { __u64 fdr; /* IA32 fp except. data reg */ __u64 old_k1; /* old value of ar.k1 */ __u64 old_iob; /* old IOBase value */ + /* cached TLS descriptors. */ + struct desc_struct tls_array[GDT_ENTRY_TLS_ENTRIES]; + # define INIT_THREAD_IA32 .eflag = 0, \ .fsr = 0, \ .fcr = 0x17800000037fULL, \ -- 2.53.0