--- linux/include/asm-i386/desc.h.orig Mon Jul 29 02:55:56 2002 +++ linux/include/asm-i386/desc.h Mon Jul 29 02:59:21 2002 @@ -4,69 +4,59 @@ #include /* - * The layout of the GDT under Linux: + * The layout of the per-CPU GDT under Linux: * * 0 - null - * 1 - not used + * 1 - Thread-Local Storage (TLS) segment * 2 - kernel code segment * 3 - kernel data segment - * 4 - user code segment <-- new cacheline + * 4 - user code segment <==== new cacheline * 5 - user data segment - * 6 - not used - * 7 - not used - * 8 - APM BIOS support <-- new cacheline + * 6 - TSS + * 7 - LDT + * 8 - APM BIOS support <==== new cacheline * 9 - APM BIOS support * 10 - APM BIOS support * 11 - APM BIOS support - * 12 - PNPBIOS support + * 12 - PNPBIOS support <==== new cacheline * 13 - PNPBIOS support * 14 - PNPBIOS support * 15 - PNPBIOS support - * 16 - PNPBIOS support + * 16 - PNPBIOS support <==== new cacheline * 17 - not used * 18 - not used * 19 - not used + */ +#define TLS_ENTRY 1 +#define TSS_ENTRY 6 +#define LDT_ENTRY 7 +/* + * The interrupt descriptor table has room for 256 idt's, + * the global descriptor table is dependent on the number + * of tasks we can have.. * - * The TSS+LDT descriptors are spread out a bit so that every CPU - * has an exclusive cacheline for the per-CPU TSS and LDT: - * - * 20 - CPU#0 TSS <-- new cacheline - * 21 - CPU#0 LDT - * 22 - not used - * 23 - not used - * 24 - CPU#1 TSS <-- new cacheline - * 25 - CPU#1 LDT - * 26 - not used - * 27 - not used - * ... NR_CPUS per-CPU TSS+LDT's if on SMP - * - * Entry into gdt where to find first TSS. + * We pad the GDT to cacheline boundary. */ -#define __FIRST_TSS_ENTRY 20 -#define __FIRST_LDT_ENTRY (__FIRST_TSS_ENTRY+1) - -#define __TSS(n) (((n)<<4) + __FIRST_TSS_ENTRY) -#define __LDT(n) (((n)<<4) + __FIRST_LDT_ENTRY) +#define IDT_ENTRIES 256 +#define GDT_ENTRIES 20 #ifndef __ASSEMBLY__ -struct desc_struct { - unsigned long a,b; -}; -extern struct desc_struct gdt_table[]; -extern struct desc_struct *idt, *gdt; +#include + +#define GDT_SIZE (GDT_ENTRIES*sizeof(struct desc_struct)) + +extern struct desc_struct cpu_gdt_table[NR_CPUS][GDT_ENTRIES]; struct Xgt_desc_struct { unsigned short size; unsigned long address __attribute__((packed)); -}; - -#define idt_descr (*(struct Xgt_desc_struct *)((char *)&idt - 2)) -#define gdt_descr (*(struct Xgt_desc_struct *)((char *)&gdt - 2)) +} __attribute__ ((packed)); -#define load_TR(n) __asm__ __volatile__("ltr %%ax"::"a" (__TSS(n)<<3)) +extern struct Xgt_desc_struct idt_descr, cpu_gdt_descr[NR_CPUS]; -#define __load_LDT(n) __asm__ __volatile__("lldt %%ax"::"a" (__LDT(n)<<3)) +#define load_TR_desc() __asm__ __volatile__("ltr %%ax"::"a" (TSS_ENTRY<<3)) +#define load_LDT_desc() __asm__ __volatile__("lldt %%ax"::"a" (LDT_ENTRY<<3)) /* * This is the ldt that every process will get unless we need @@ -74,14 +64,42 @@ */ extern struct desc_struct default_ldt[]; extern void set_intr_gate(unsigned int irq, void * addr); -extern void set_ldt_desc(unsigned int n, void *addr, unsigned int size); -extern void set_tss_desc(unsigned int n, void *addr); + +#define _set_tssldt_desc(n,addr,limit,type) \ +__asm__ __volatile__ ("movw %w3,0(%2)\n\t" \ + "movw %%ax,2(%2)\n\t" \ + "rorl $16,%%eax\n\t" \ + "movb %%al,4(%2)\n\t" \ + "movb %4,5(%2)\n\t" \ + "movb $0,6(%2)\n\t" \ + "movb %%ah,7(%2)\n\t" \ + "rorl $16,%%eax" \ + : "=m"(*(n)) : "a" (addr), "r"(n), "ir"(limit), "i"(type)) + +static inline void set_tss_desc(unsigned int cpu, void *addr) +{ + _set_tssldt_desc(&cpu_gdt_table[cpu][TSS_ENTRY], (int)addr, 235, 0x89); +} + +static inline void set_ldt_desc(unsigned int cpu, void *addr, unsigned int size) +{ + _set_tssldt_desc(&cpu_gdt_table[cpu][LDT_ENTRY], (int)addr, ((size << 3)-1), 0x82); +} + +#define TLS_FLAGS_MASK 0x00000003 + +#define TLS_FLAG_WRITABLE 0x00000001 +#define TLS_FLAG_CLEAR 0x00000002 + +static inline void load_TLS_desc(struct thread_struct *t, unsigned int cpu) +{ + cpu_gdt_table[cpu][TLS_ENTRY] = t->tls_desc; +} static inline void clear_LDT(void) { - int cpu = smp_processor_id(); - set_ldt_desc(cpu, &default_ldt[0], 5); - __load_LDT(cpu); + set_ldt_desc(smp_processor_id(), &default_ldt[0], 5); + load_LDT_desc(); } /* @@ -89,17 +107,16 @@ */ static inline void load_LDT (struct mm_struct *mm) { - int cpu = smp_processor_id(); void *segments = mm->context.segments; int count = LDT_ENTRIES; - if (!segments) { + if (likely(!segments)) { segments = &default_ldt[0]; count = 5; } - set_ldt_desc(cpu, segments, count); - __load_LDT(cpu); + set_ldt_desc(smp_processor_id(), segments, count); + load_LDT_desc(); } #endif /* !__ASSEMBLY__ */ --- linux/include/asm-i386/processor.h.orig Mon Jul 29 02:55:56 2002 +++ linux/include/asm-i386/processor.h Mon Jul 29 02:59:30 2002 @@ -18,6 +18,10 @@ #include #include +struct desc_struct { + unsigned long a,b; +}; + /* * Default implementation of macro that returns current * instruction pointer ("program counter"). @@ -385,6 +389,8 @@ /* IO permissions */ int ioperm; unsigned long io_bitmap[IO_BITMAP_SIZE+1]; +/* TLS cached descriptor */ + struct desc_struct tls_desc; }; #define INIT_THREAD { \ @@ -408,7 +414,7 @@ 0,0,0,0, /* esp,ebp,esi,edi */ \ 0,0,0,0,0,0, /* es,cs,ss */ \ 0,0,0,0,0,0, /* ds,fs,gs */ \ - __LDT(0),0, /* ldt */ \ + LDT_ENTRY,0, /* ldt */ \ 0, INVALID_IO_BITMAP_OFFSET, /* tace, bitmap */ \ {~0, } /* ioperm */ \ } --- linux/include/asm-i386/mmu_context.h.orig Mon Jul 29 02:55:56 2002 +++ linux/include/asm-i386/mmu_context.h Mon Jul 29 02:57:30 2002 @@ -16,7 +16,7 @@ static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk, unsigned cpu) { - if(cpu_tlbstate[cpu].state == TLBSTATE_OK) + if (cpu_tlbstate[cpu].state == TLBSTATE_OK) cpu_tlbstate[cpu].state = TLBSTATE_LAZY; } #else --- linux/drivers/pnp/pnpbios_core.c.orig Sun Jul 28 13:41:33 2002 +++ linux/drivers/pnp/pnpbios_core.c Mon Jul 29 02:57:30 2002 @@ -120,13 +120,13 @@ ".previous \n" ); -#define Q_SET_SEL(selname, address, size) \ -set_base (gdt [(selname) >> 3], __va((u32)(address))); \ -set_limit (gdt [(selname) >> 3], size) - -#define Q2_SET_SEL(selname, address, size) \ -set_base (gdt [(selname) >> 3], (u32)(address)); \ -set_limit (gdt [(selname) >> 3], size) +#define Q_SET_SEL(cpu, selname, address, size) \ +set_base(cpu_gdt_table[cpu][(selname) >> 3], __va((u32)(address))); \ +_set_limit(&cpu_gdt_table[cpu][(selname) >> 3], size) + +#define Q2_SET_SEL(cpu, selname, address, size) \ +set_base(cpu_gdt_table[cpu][(selname) >> 3], (u32)(address)); \ +_set_limit((char *)&cpu_gdt_table[cpu][(selname) >> 3], size) /* * At some point we want to use this stack frame pointer to unwind @@ -157,10 +157,11 @@ /* On some boxes IRQ's during PnP BIOS calls are deadly. */ spin_lock_irqsave(&pnp_bios_lock, flags); + /* The lock prevents us bouncing CPU here */ if (ts1_size) - Q2_SET_SEL(PNP_TS1, ts1_base, ts1_size); + Q2_SET_SEL(smp_processor_id(), PNP_TS1, ts1_base, ts1_size); if (ts2_size) - Q2_SET_SEL(PNP_TS2, ts2_base, ts2_size); + Q2_SET_SEL(smp_processor_id(), PNP_TS2, ts2_base, ts2_size); __asm__ __volatile__( "pushl %%ebp\n\t" @@ -1277,12 +1278,16 @@ check->fields.version >> 4, check->fields.version & 15, check->fields.pm16cseg, check->fields.pm16offset, check->fields.pm16dseg); - Q2_SET_SEL(PNP_CS32, &pnp_bios_callfunc, 64 * 1024); - Q_SET_SEL(PNP_CS16, check->fields.pm16cseg, 64 * 1024); - Q_SET_SEL(PNP_DS, check->fields.pm16dseg, 64 * 1024); pnp_bios_callpoint.offset = check->fields.pm16offset; pnp_bios_callpoint.segment = PNP_CS16; pnp_bios_hdr = check; + + for(i=0; i < NR_CPUS; i++) + { + Q2_SET_SEL(i, PNP_CS32, &pnp_bios_callfunc, 64 * 1024); + Q_SET_SEL(i, PNP_CS16, check->fields.pm16cseg, 64 * 1024); + Q_SET_SEL(i, PNP_DS, check->fields.pm16dseg, 64 * 1024); + } break; } if (!pnp_bios_present()) --- linux/arch/i386/mm/fault.c.orig Mon Jul 29 02:55:58 2002 +++ linux/arch/i386/mm/fault.c Mon Jul 29 02:57:30 2002 @@ -24,6 +24,7 @@ #include #include #include +#include extern void die(const char *,struct pt_regs *,long); @@ -125,7 +126,6 @@ } asmlinkage void do_invalid_op(struct pt_regs *, unsigned long); -extern unsigned long idt; /* * This routine handles page faults. It determines the address, @@ -286,7 +286,7 @@ if (boot_cpu_data.f00f_bug) { unsigned long nr; - nr = (address - idt) >> 3; + nr = (address - idt_descr.address) >> 3; if (nr == 6) { do_invalid_op(regs, 0); --- linux/arch/i386/kernel/head.S.orig Mon Jul 29 02:55:58 2002 +++ linux/arch/i386/kernel/head.S Mon Jul 29 02:57:30 2002 @@ -241,7 +241,7 @@ 2: movl %eax,%cr0 call check_x87 incb ready - lgdt gdt_descr + lgdt cpu_gdt_descr lidt idt_descr ljmp $(__KERNEL_CS),$1f 1: movl $(__KERNEL_DS),%eax # reload all the segment registers @@ -347,30 +347,30 @@ popl %eax iret + /* - * The interrupt descriptor table has room for 256 idt's, - * the global descriptor table is dependent on the number - * of tasks we can have.. + * The IDT and GDT 'descriptors' are a strange 48-bit object + * only used by the lidt and lgdt instructions. They are not + * like usual segment descriptors - they consist of a 16-bit + * segment size, and 32-bit linear address value: */ -#define IDT_ENTRIES 256 -#define GDT_ENTRIES (__TSS(NR_CPUS)) - -.globl SYMBOL_NAME(idt) -.globl SYMBOL_NAME(gdt) +.globl SYMBOL_NAME(idt_descr) +.globl SYMBOL_NAME(cpu_gdt_descr) ALIGN - .word 0 -idt_descr: + .word 0 # 32-bit align idt_desc.address + +SYMBOL_NAME(idt_descr): .word IDT_ENTRIES*8-1 # idt contains 256 entries -SYMBOL_NAME(idt): .long SYMBOL_NAME(idt_table) - .word 0 -gdt_descr: +SYMBOL_NAME(cpu_gdt_descr): .word GDT_ENTRIES*8-1 -SYMBOL_NAME(gdt): - .long SYMBOL_NAME(gdt_table) + .long SYMBOL_NAME(cpu_gdt_table) + + .fill NR_CPUS-1,6,0 # space for the other GDT descriptors + /* * This is initialized to create an identity-mapping at 0-8M (for bootup @@ -428,15 +428,15 @@ * NOTE! Make sure the gdt descriptor in head.S matches this if you * change anything. */ -ENTRY(gdt_table) +ENTRY(cpu_gdt_table) .quad 0x0000000000000000 /* NULL descriptor */ - .quad 0x0000000000000000 /* not used */ + .quad 0x0000000000000000 /* TLS descriptor */ .quad 0x00cf9a000000ffff /* 0x10 kernel 4GB code at 0x00000000 */ .quad 0x00cf92000000ffff /* 0x18 kernel 4GB data at 0x00000000 */ .quad 0x00cffa000000ffff /* 0x23 user 4GB code at 0x00000000 */ .quad 0x00cff2000000ffff /* 0x2b user 4GB data at 0x00000000 */ - .quad 0x0000000000000000 /* not used */ - .quad 0x0000000000000000 /* not used */ + .quad 0x0000000000000000 /* TSS descriptor */ + .quad 0x0000000000000000 /* LDT descriptor */ /* * The APM segments have byte granularity and their bases * and limits are set at run time. @@ -445,7 +445,7 @@ .quad 0x00409a0000000000 /* 0x48 APM CS code */ .quad 0x00009a0000000000 /* 0x50 APM CS 16 code (16 bit) */ .quad 0x0040920000000000 /* 0x58 APM DS data */ - /* Segments used for calling PnP BIOS */ + /* Segments used for calling PnP BIOS */ .quad 0x00c09a0000000000 /* 0x60 32-bit code */ .quad 0x00809a0000000000 /* 0x68 16-bit code */ .quad 0x0080920000000000 /* 0x70 16-bit data */ @@ -454,6 +454,8 @@ .quad 0x0000000000000000 /* 0x88 not used */ .quad 0x0000000000000000 /* 0x90 not used */ .quad 0x0000000000000000 /* 0x98 not used */ - /* Per CPU segments */ - .fill NR_CPUS*16,8,0 /* space for TSS's and LDT's */ + +#if CONFIG_SMP + .fill (NR_CPUS-1)*GDT_ENTRIES,8,0 /* other CPU's GDT */ +#endif --- linux/arch/i386/kernel/apm.c.orig Mon Jul 29 02:55:58 2002 +++ linux/arch/i386/kernel/apm.c Mon Jul 29 02:57:30 2002 @@ -1858,6 +1858,8 @@ */ static int __init apm_init(void) { + int i; + struct proc_dir_entry *apm_proc; if (apm_info.bios.version == 0) { @@ -1929,37 +1931,39 @@ * This is for buggy BIOS's that refer to (real mode) segment 0x40 * even though they are called in protected mode. */ - set_base(gdt[APM_40 >> 3], - __va((unsigned long)0x40 << 4)); - _set_limit((char *)&gdt[APM_40 >> 3], 4095 - (0x40 << 4)); - - apm_bios_entry.offset = apm_info.bios.offset; - apm_bios_entry.segment = APM_CS; - set_base(gdt[APM_CS >> 3], - __va((unsigned long)apm_info.bios.cseg << 4)); - set_base(gdt[APM_CS_16 >> 3], - __va((unsigned long)apm_info.bios.cseg_16 << 4)); - set_base(gdt[APM_DS >> 3], - __va((unsigned long)apm_info.bios.dseg << 4)); + for (i = 0; i < NR_CPUS; i++) { + set_base(cpu_gdt_table[i][APM_40 >> 3], + __va((unsigned long)0x40 << 4)); + _set_limit((char *)&cpu_gdt_table[i][APM_40 >> 3], 4095 - (0x40 << 4)); + + apm_bios_entry.offset = apm_info.bios.offset; + apm_bios_entry.segment = APM_CS; + set_base(cpu_gdt_table[i][APM_CS >> 3], + __va((unsigned long)apm_info.bios.cseg << 4)); + set_base(cpu_gdt_table[i][APM_CS_16 >> 3], + __va((unsigned long)apm_info.bios.cseg_16 << 4)); + set_base(cpu_gdt_table[i][APM_DS >> 3], + __va((unsigned long)apm_info.bios.dseg << 4)); #ifndef APM_RELAX_SEGMENTS - if (apm_info.bios.version == 0x100) { + if (apm_info.bios.version == 0x100) { #endif - /* For ASUS motherboard, Award BIOS rev 110 (and others?) */ - _set_limit((char *)&gdt[APM_CS >> 3], 64 * 1024 - 1); - /* For some unknown machine. */ - _set_limit((char *)&gdt[APM_CS_16 >> 3], 64 * 1024 - 1); - /* For the DEC Hinote Ultra CT475 (and others?) */ - _set_limit((char *)&gdt[APM_DS >> 3], 64 * 1024 - 1); + /* For ASUS motherboard, Award BIOS rev 110 (and others?) */ + _set_limit((char *)&cpu_gdt_table[i][APM_CS >> 3], 64 * 1024 - 1); + /* For some unknown machine. */ + _set_limit((char *)&cpu_gdt_table[i][APM_CS_16 >> 3], 64 * 1024 - 1); + /* For the DEC Hinote Ultra CT475 (and others?) */ + _set_limit((char *)&cpu_gdt_table[i][APM_DS >> 3], 64 * 1024 - 1); #ifndef APM_RELAX_SEGMENTS - } else { - _set_limit((char *)&gdt[APM_CS >> 3], - (apm_info.bios.cseg_len - 1) & 0xffff); - _set_limit((char *)&gdt[APM_CS_16 >> 3], - (apm_info.bios.cseg_16_len - 1) & 0xffff); - _set_limit((char *)&gdt[APM_DS >> 3], - (apm_info.bios.dseg_len - 1) & 0xffff); - } + } else { + _set_limit((char *)&cpu_gdt_table[i][APM_CS >> 3], + (apm_info.bios.cseg_len - 1) & 0xffff); + _set_limit((char *)&cpu_gdt_table[i][APM_CS_16 >> 3], + (apm_info.bios.cseg_16_len - 1) & 0xffff); + _set_limit((char *)&cpu_gdt_table[i][APM_DS >> 3], + (apm_info.bios.dseg_len - 1) & 0xffff); + } #endif + } apm_proc = create_proc_info_entry("apm", 0, NULL, apm_get_info); if (apm_proc) --- linux/arch/i386/kernel/i386_ksyms.c.orig Mon Jul 29 02:55:58 2002 +++ linux/arch/i386/kernel/i386_ksyms.c Mon Jul 29 02:57:30 2002 @@ -75,7 +75,6 @@ EXPORT_SYMBOL(get_cmos_time); EXPORT_SYMBOL(cpu_khz); EXPORT_SYMBOL(apm_info); -EXPORT_SYMBOL(gdt); EXPORT_SYMBOL(empty_zero_page); #ifdef CONFIG_DEBUG_IOVIRT --- linux/arch/i386/kernel/setup.c.orig Mon Jul 29 02:55:58 2002 +++ linux/arch/i386/kernel/setup.c Mon Jul 29 02:57:30 2002 @@ -3002,7 +3002,16 @@ } #endif - __asm__ __volatile__("lgdt %0": "=m" (gdt_descr)); + /* + * Initialize the per-CPU GDT with the boot GDT, + * and set up the GDT descriptor: + */ + if (nr) { + memcpy(cpu_gdt_table[nr], cpu_gdt_table[0], GDT_SIZE); + cpu_gdt_descr[nr].size = GDT_SIZE; + cpu_gdt_descr[nr].address = (unsigned long)cpu_gdt_table[nr]; + } + __asm__ __volatile__("lgdt %0": "=m" (cpu_gdt_descr[nr])); __asm__ __volatile__("lidt %0": "=m" (idt_descr)); /* @@ -3021,8 +3030,8 @@ t->esp0 = current->thread.esp0; set_tss_desc(nr,t); - gdt_table[__TSS(nr)].b &= 0xfffffdff; - load_TR(nr); + cpu_gdt_table[nr][TSS_ENTRY].b &= 0xfffffdff; + load_TR_desc(); load_LDT(&init_mm); /* Clear %fs and %gs. */ --- linux/arch/i386/kernel/entry.S.orig Mon Jul 29 02:55:58 2002 +++ linux/arch/i386/kernel/entry.S Mon Jul 29 02:57:30 2002 @@ -649,6 +649,7 @@ .long SYMBOL_NAME(sys_futex) /* 240 reserved for futex */ .long SYMBOL_NAME(sys_sched_setaffinity) .long SYMBOL_NAME(sys_sched_getaffinity) + .long SYMBOL_NAME(sys_set_thread_area) .rept NR_syscalls-(.-sys_call_table)/4 .long SYMBOL_NAME(sys_ni_syscall) --- linux/arch/i386/kernel/traps.c.orig Mon Jul 29 02:55:58 2002 +++ linux/arch/i386/kernel/traps.c Mon Jul 29 02:57:30 2002 @@ -822,11 +822,10 @@ __flush_tlb_all(); /* - * "idt" is magic - it overlaps the idt_descr - * variable so that updating idt will automatically - * update the idt descriptor.. - */ - idt = (struct desc_struct *)page; + * Update the IDT descriptor and reload the IDT so that + * it uses the read-only mapped virtual address. + */ + idt_descr.address = page; __asm__ __volatile__("lidt %0": "=m" (idt_descr)); } #endif @@ -869,37 +868,6 @@ static void __init set_call_gate(void *a, void *addr) { _set_gate(a,12,3,addr); -} - -#define _set_seg_desc(gate_addr,type,dpl,base,limit) {\ - *((gate_addr)+1) = ((base) & 0xff000000) | \ - (((base) & 0x00ff0000)>>16) | \ - ((limit) & 0xf0000) | \ - ((dpl)<<13) | \ - (0x00408000) | \ - ((type)<<8); \ - *(gate_addr) = (((base) & 0x0000ffff)<<16) | \ - ((limit) & 0x0ffff); } - -#define _set_tssldt_desc(n,addr,limit,type) \ -__asm__ __volatile__ ("movw %w3,0(%2)\n\t" \ - "movw %%ax,2(%2)\n\t" \ - "rorl $16,%%eax\n\t" \ - "movb %%al,4(%2)\n\t" \ - "movb %4,5(%2)\n\t" \ - "movb $0,6(%2)\n\t" \ - "movb %%ah,7(%2)\n\t" \ - "rorl $16,%%eax" \ - : "=m"(*(n)) : "a" (addr), "r"(n), "ir"(limit), "i"(type)) - -void set_tss_desc(unsigned int n, void *addr) -{ - _set_tssldt_desc(gdt_table+__TSS(n), (int)addr, 235, 0x89); -} - -void set_ldt_desc(unsigned int n, void *addr, unsigned int size) -{ - _set_tssldt_desc(gdt_table+__LDT(n), (int)addr, ((size << 3)-1), 0x82); } #ifdef CONFIG_X86_VISWS_APIC --- linux/arch/i386/kernel/trampoline.S.orig Mon Jul 29 02:55:58 2002 +++ linux/arch/i386/kernel/trampoline.S Mon Jul 29 02:57:30 2002 @@ -63,9 +63,14 @@ .word 0 # idt limit = 0 .word 0, 0 # idt base = 0L +# +# NOTE: here we actually use CPU#0's GDT - but that is OK, we reload +# the proper GDT shortly after booting up the secondary CPUs. +# + gdt_48: .word 0x0800 # gdt limit = 2048, 256 GDT entries - .long gdt_table-__PAGE_OFFSET # gdt base = gdt (first SMP CPU) + .long cpu_gdt_table-__PAGE_OFFSET # gdt base = gdt (first SMP CPU) .globl SYMBOL_NAME(trampoline_end) SYMBOL_NAME_LABEL(trampoline_end) --- linux/arch/i386/kernel/process.c.orig Mon Jul 29 02:56:23 2002 +++ linux/arch/i386/kernel/process.c Mon Jul 29 02:58:59 2002 @@ -689,7 +689,8 @@ { struct thread_struct *prev = &prev_p->thread, *next = &next_p->thread; - struct tss_struct *tss = init_tss + smp_processor_id(); + int cpu = smp_processor_id(); + struct tss_struct *tss = init_tss + cpu; unlazy_fpu(prev_p); @@ -706,6 +707,14 @@ asm volatile("movl %%gs,%0":"=m" (*(int *)&prev->gs)); /* + * Load the per-thread Thread-Local Storage descriptor. + * + * NOTE: it's faster to do the two stores unconditionally + * than to branch away. + */ + load_TLS_desc(next, cpu); + + /* * Restore %fs and %gs if needed. */ if (unlikely(prev->fs | prev->gs | next->fs | next->gs)) { @@ -849,3 +858,40 @@ } #undef last_sched #undef first_sched + +/* + * Set the Thread-Local Storage area: + */ +asmlinkage int sys_set_thread_area(unsigned long base, unsigned int flags) +{ + struct thread_struct *t = ¤t->thread; + int cpu = smp_processor_id(); + int writable = 0; + + /* do not allow unused flags */ + if (flags & ~TLS_FLAGS_MASK) + return -EINVAL; + + /* + * Clear the TLS? + */ + if (flags & TLS_FLAG_CLEAR) { + t->tls_desc.a = t->tls_desc.b = 0; + load_TLS_desc(t, cpu); + return 0; + } + + if (flags & TLS_FLAG_WRITABLE) + writable = 1; + + t->tls_desc.a = ((base & 0x0000ffff) << 16) | 0xffff; + + t->tls_desc.b = (base & 0xff000000) | ((base & 0x00ff0000) >> 16) | + 0xf0000 | (writable << 9) | (1 << 15) | + (1 << 22) | (1 << 23) | 0x7000; + + load_TLS_desc(t, cpu); + + return TLS_ENTRY*8 + 3; +} +