e9f86e351f
When reviewing GDT updates, I found the code: set_tss_desc(cpu,t); /* This just modifies memory; ... */ per_cpu(cpu_gdt_table, cpu)[GDT_ENTRY_TSS].b &= 0xfffffdff; This second line is unnecessary, since set_tss_desc() has already cleared the busy bit. Commented disassembly, line 1: c028b8bd: 8b 0c 86 mov (%esi,%eax,4),%ecx c028b8c0: 01 cb add %ecx,%ebx c028b8c2: 8d 0c 39 lea (%ecx,%edi,1),%ecx => %ecx = per_cpu(cpu_gdt_table, cpu) c028b8c5: 8d 91 80 00 00 00 lea 0x80(%ecx),%edx => %edx = &per_cpu(cpu_gdt_table, cpu)[GDT_ENTRY_TSS] c028b8cb: 66 c7 42 00 73 20 movw $0x2073,0x0(%edx) c028b8d1: 66 89 5a 02 mov %bx,0x2(%edx) c028b8d5: c1 cb 10 ror $0x10,%ebx c028b8d8: 88 5a 04 mov %bl,0x4(%edx) c028b8db: c6 42 05 89 movb $0x89,0x5(%edx) => ((char *)%edx)[5] = 0x89 (equivalent) ((char *)per_cpu(cpu_gdt_table, cpu)[GDT_ENTRY_TSS])[5] = 0x89 c028b8df: c6 42 06 00 movb $0x0,0x6(%edx) c028b8e3: 88 7a 07 mov %bh,0x7(%edx) c028b8e6: c1 cb 10 ror $0x10,%ebx => other bits Commented disassembly, line 2: c028b8e9: 8b 14 86 mov (%esi,%eax,4),%edx c028b8ec: 8d 04 3a lea (%edx,%edi,1),%eax => %eax = per_cpu(cpu_gdt_table, cpu) c028b8ef: 81 a0 84 00 00 00 ff andl $0xfffffdff,0x84(%eax) => per_cpu(cpu_gdt_table, cpu)[GDT_ENTRY_TSS].b &= 0xfffffdff; (equivalent) ((char *)per_cpu(cpu_gdt_table, cpu)[GDT_ENTRY_TSS])[5] &= 0xfd Note that (0x89 & ~0xfd) == 0; i.e, set_tss_desc(cpu,t) has already stored the type field in the GDT with the busy bit clear. Eliminating redundant and obscure code is always a good thing; in fact, I pointed out this same optimization many moons ago in arch/i386/setup.c, back when it used to be called that. Signed-off-by: Zachary Amsden <zach@vmware.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
150 lines
3.2 KiB
C
150 lines
3.2 KiB
C
/*
|
|
* Suspend support specific for i386.
|
|
*
|
|
* Distribute under GPLv2
|
|
*
|
|
* Copyright (c) 2002 Pavel Machek <pavel@suse.cz>
|
|
* Copyright (c) 2001 Patrick Mochel <mochel@osdl.org>
|
|
*/
|
|
|
|
#include <linux/config.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/types.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/sysrq.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/pm.h>
|
|
#include <linux/device.h>
|
|
#include <linux/suspend.h>
|
|
#include <linux/acpi.h>
|
|
|
|
#include <asm/uaccess.h>
|
|
#include <asm/acpi.h>
|
|
#include <asm/tlbflush.h>
|
|
#include <asm/processor.h>
|
|
|
|
static struct saved_context saved_context;
|
|
|
|
unsigned long saved_context_ebx;
|
|
unsigned long saved_context_esp, saved_context_ebp;
|
|
unsigned long saved_context_esi, saved_context_edi;
|
|
unsigned long saved_context_eflags;
|
|
|
|
void __save_processor_state(struct saved_context *ctxt)
|
|
{
|
|
kernel_fpu_begin();
|
|
|
|
/*
|
|
* descriptor tables
|
|
*/
|
|
store_gdt(&ctxt->gdt_limit);
|
|
store_idt(&ctxt->idt_limit);
|
|
store_tr(ctxt->tr);
|
|
|
|
/*
|
|
* segment registers
|
|
*/
|
|
savesegment(es, ctxt->es);
|
|
savesegment(fs, ctxt->fs);
|
|
savesegment(gs, ctxt->gs);
|
|
savesegment(ss, ctxt->ss);
|
|
|
|
/*
|
|
* control registers
|
|
*/
|
|
ctxt->cr0 = read_cr0();
|
|
ctxt->cr2 = read_cr2();
|
|
ctxt->cr3 = read_cr3();
|
|
ctxt->cr4 = read_cr4();
|
|
}
|
|
|
|
void save_processor_state(void)
|
|
{
|
|
__save_processor_state(&saved_context);
|
|
}
|
|
|
|
static void
|
|
do_fpu_end(void)
|
|
{
|
|
/* restore FPU regs if necessary */
|
|
/* Do it out of line so that gcc does not move cr0 load to some stupid place */
|
|
kernel_fpu_end();
|
|
mxcsr_feature_mask_init();
|
|
}
|
|
|
|
|
|
static void fix_processor_context(void)
|
|
{
|
|
int cpu = smp_processor_id();
|
|
struct tss_struct * t = &per_cpu(init_tss, cpu);
|
|
|
|
set_tss_desc(cpu,t); /* This just modifies memory; should not be necessary. But... This is necessary, because 386 hardware has concept of busy TSS or some similar stupidity. */
|
|
|
|
load_TR_desc(); /* This does ltr */
|
|
load_LDT(¤t->active_mm->context); /* This does lldt */
|
|
|
|
/*
|
|
* Now maybe reload the debug registers
|
|
*/
|
|
if (current->thread.debugreg[7]){
|
|
set_debugreg(current->thread.debugreg[0], 0);
|
|
set_debugreg(current->thread.debugreg[1], 1);
|
|
set_debugreg(current->thread.debugreg[2], 2);
|
|
set_debugreg(current->thread.debugreg[3], 3);
|
|
/* no 4 and 5 */
|
|
set_debugreg(current->thread.debugreg[6], 6);
|
|
set_debugreg(current->thread.debugreg[7], 7);
|
|
}
|
|
|
|
}
|
|
|
|
void __restore_processor_state(struct saved_context *ctxt)
|
|
{
|
|
/*
|
|
* control registers
|
|
*/
|
|
write_cr4(ctxt->cr4);
|
|
write_cr3(ctxt->cr3);
|
|
write_cr2(ctxt->cr2);
|
|
write_cr2(ctxt->cr0);
|
|
|
|
/*
|
|
* now restore the descriptor tables to their proper values
|
|
* ltr is done i fix_processor_context().
|
|
*/
|
|
load_gdt(&ctxt->gdt_limit);
|
|
load_idt(&ctxt->idt_limit);
|
|
|
|
/*
|
|
* segment registers
|
|
*/
|
|
loadsegment(es, ctxt->es);
|
|
loadsegment(fs, ctxt->fs);
|
|
loadsegment(gs, ctxt->gs);
|
|
loadsegment(ss, ctxt->ss);
|
|
|
|
/*
|
|
* sysenter MSRs
|
|
*/
|
|
if (boot_cpu_has(X86_FEATURE_SEP))
|
|
enable_sep_cpu();
|
|
|
|
fix_processor_context();
|
|
do_fpu_end();
|
|
mtrr_ap_init();
|
|
}
|
|
|
|
void restore_processor_state(void)
|
|
{
|
|
__restore_processor_state(&saved_context);
|
|
}
|
|
|
|
/* Needed by apm.c */
|
|
EXPORT_SYMBOL(save_processor_state);
|
|
EXPORT_SYMBOL(restore_processor_state);
|