i386: move kernel/acpi
Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
5
arch/x86/kernel/acpi/Makefile
Normal file
5
arch/x86/kernel/acpi/Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
ifeq ($(CONFIG_X86_32),y)
|
||||
include ${srctree}/arch/x86/kernel/acpi/Makefile_32
|
||||
else
|
||||
include ${srctree}/arch/x86_64/kernel/acpi/Makefile_64
|
||||
endif
|
10
arch/x86/kernel/acpi/Makefile_32
Normal file
10
arch/x86/kernel/acpi/Makefile_32
Normal file
@ -0,0 +1,10 @@
|
||||
obj-$(CONFIG_ACPI) += boot.o
|
||||
ifneq ($(CONFIG_PCI),)
|
||||
obj-$(CONFIG_X86_IO_APIC) += earlyquirk_32.o
|
||||
endif
|
||||
obj-$(CONFIG_ACPI_SLEEP) += sleep_32.o wakeup_32.o
|
||||
|
||||
ifneq ($(CONFIG_ACPI_PROCESSOR),)
|
||||
obj-y += cstate.o processor.o
|
||||
endif
|
||||
|
1326
arch/x86/kernel/acpi/boot.c
Normal file
1326
arch/x86/kernel/acpi/boot.c
Normal file
File diff suppressed because it is too large
Load Diff
164
arch/x86/kernel/acpi/cstate.c
Normal file
164
arch/x86/kernel/acpi/cstate.c
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* arch/i386/kernel/acpi/cstate.c
|
||||
*
|
||||
* Copyright (C) 2005 Intel Corporation
|
||||
* Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
|
||||
* - Added _PDC for SMP C-states on Intel CPUs
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include <acpi/processor.h>
|
||||
#include <asm/acpi.h>
|
||||
|
||||
/*
|
||||
* Initialize bm_flags based on the CPU cache properties
|
||||
* On SMP it depends on cache configuration
|
||||
* - When cache is not shared among all CPUs, we flush cache
|
||||
* before entering C3.
|
||||
* - When cache is shared among all CPUs, we use bm_check
|
||||
* mechanism as in UP case
|
||||
*
|
||||
* This routine is called only after all the CPUs are online
|
||||
*/
|
||||
void acpi_processor_power_init_bm_check(struct acpi_processor_flags *flags,
|
||||
unsigned int cpu)
|
||||
{
|
||||
struct cpuinfo_x86 *c = cpu_data + cpu;
|
||||
|
||||
flags->bm_check = 0;
|
||||
if (num_online_cpus() == 1)
|
||||
flags->bm_check = 1;
|
||||
else if (c->x86_vendor == X86_VENDOR_INTEL) {
|
||||
/*
|
||||
* Today all CPUs that support C3 share cache.
|
||||
* TBD: This needs to look at cache shared map, once
|
||||
* multi-core detection patch makes to the base.
|
||||
*/
|
||||
flags->bm_check = 1;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(acpi_processor_power_init_bm_check);
|
||||
|
||||
/* The code below handles cstate entry with monitor-mwait pair on Intel*/
|
||||
|
||||
struct cstate_entry {
|
||||
struct {
|
||||
unsigned int eax;
|
||||
unsigned int ecx;
|
||||
} states[ACPI_PROCESSOR_MAX_POWER];
|
||||
};
|
||||
static struct cstate_entry *cpu_cstate_entry; /* per CPU ptr */
|
||||
|
||||
static short mwait_supported[ACPI_PROCESSOR_MAX_POWER];
|
||||
|
||||
#define MWAIT_SUBSTATE_MASK (0xf)
|
||||
#define MWAIT_SUBSTATE_SIZE (4)
|
||||
|
||||
#define CPUID_MWAIT_LEAF (5)
|
||||
#define CPUID5_ECX_EXTENSIONS_SUPPORTED (0x1)
|
||||
#define CPUID5_ECX_INTERRUPT_BREAK (0x2)
|
||||
|
||||
#define MWAIT_ECX_INTERRUPT_BREAK (0x1)
|
||||
|
||||
#define NATIVE_CSTATE_BEYOND_HALT (2)
|
||||
|
||||
int acpi_processor_ffh_cstate_probe(unsigned int cpu,
|
||||
struct acpi_processor_cx *cx, struct acpi_power_register *reg)
|
||||
{
|
||||
struct cstate_entry *percpu_entry;
|
||||
struct cpuinfo_x86 *c = cpu_data + cpu;
|
||||
|
||||
cpumask_t saved_mask;
|
||||
int retval;
|
||||
unsigned int eax, ebx, ecx, edx;
|
||||
unsigned int edx_part;
|
||||
unsigned int cstate_type; /* C-state type and not ACPI C-state type */
|
||||
unsigned int num_cstate_subtype;
|
||||
|
||||
if (!cpu_cstate_entry || c->cpuid_level < CPUID_MWAIT_LEAF )
|
||||
return -1;
|
||||
|
||||
if (reg->bit_offset != NATIVE_CSTATE_BEYOND_HALT)
|
||||
return -1;
|
||||
|
||||
percpu_entry = per_cpu_ptr(cpu_cstate_entry, cpu);
|
||||
percpu_entry->states[cx->index].eax = 0;
|
||||
percpu_entry->states[cx->index].ecx = 0;
|
||||
|
||||
/* Make sure we are running on right CPU */
|
||||
saved_mask = current->cpus_allowed;
|
||||
retval = set_cpus_allowed(current, cpumask_of_cpu(cpu));
|
||||
if (retval)
|
||||
return -1;
|
||||
|
||||
cpuid(CPUID_MWAIT_LEAF, &eax, &ebx, &ecx, &edx);
|
||||
|
||||
/* Check whether this particular cx_type (in CST) is supported or not */
|
||||
cstate_type = (cx->address >> MWAIT_SUBSTATE_SIZE) + 1;
|
||||
edx_part = edx >> (cstate_type * MWAIT_SUBSTATE_SIZE);
|
||||
num_cstate_subtype = edx_part & MWAIT_SUBSTATE_MASK;
|
||||
|
||||
retval = 0;
|
||||
if (num_cstate_subtype < (cx->address & MWAIT_SUBSTATE_MASK)) {
|
||||
retval = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* mwait ecx extensions INTERRUPT_BREAK should be supported for C2/C3 */
|
||||
if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED) ||
|
||||
!(ecx & CPUID5_ECX_INTERRUPT_BREAK)) {
|
||||
retval = -1;
|
||||
goto out;
|
||||
}
|
||||
percpu_entry->states[cx->index].ecx = MWAIT_ECX_INTERRUPT_BREAK;
|
||||
|
||||
/* Use the hint in CST */
|
||||
percpu_entry->states[cx->index].eax = cx->address;
|
||||
|
||||
if (!mwait_supported[cstate_type]) {
|
||||
mwait_supported[cstate_type] = 1;
|
||||
printk(KERN_DEBUG "Monitor-Mwait will be used to enter C-%d "
|
||||
"state\n", cx->type);
|
||||
}
|
||||
|
||||
out:
|
||||
set_cpus_allowed(current, saved_mask);
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_processor_ffh_cstate_probe);
|
||||
|
||||
void acpi_processor_ffh_cstate_enter(struct acpi_processor_cx *cx)
|
||||
{
|
||||
unsigned int cpu = smp_processor_id();
|
||||
struct cstate_entry *percpu_entry;
|
||||
|
||||
percpu_entry = per_cpu_ptr(cpu_cstate_entry, cpu);
|
||||
mwait_idle_with_hints(percpu_entry->states[cx->index].eax,
|
||||
percpu_entry->states[cx->index].ecx);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_processor_ffh_cstate_enter);
|
||||
|
||||
static int __init ffh_cstate_init(void)
|
||||
{
|
||||
struct cpuinfo_x86 *c = &boot_cpu_data;
|
||||
if (c->x86_vendor != X86_VENDOR_INTEL)
|
||||
return -1;
|
||||
|
||||
cpu_cstate_entry = alloc_percpu(struct cstate_entry);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit ffh_cstate_exit(void)
|
||||
{
|
||||
free_percpu(cpu_cstate_entry);
|
||||
cpu_cstate_entry = NULL;
|
||||
}
|
||||
|
||||
arch_initcall(ffh_cstate_init);
|
||||
__exitcall(ffh_cstate_exit);
|
84
arch/x86/kernel/acpi/earlyquirk_32.c
Normal file
84
arch/x86/kernel/acpi/earlyquirk_32.c
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Do early PCI probing for bug detection when the main PCI subsystem is
|
||||
* not up yet.
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include <asm/pci-direct.h>
|
||||
#include <asm/acpi.h>
|
||||
#include <asm/apic.h>
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
static int __init nvidia_hpet_check(struct acpi_table_header *header)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __init check_bridge(int vendor, int device)
|
||||
{
|
||||
#ifdef CONFIG_ACPI
|
||||
static int warned;
|
||||
/* According to Nvidia all timer overrides are bogus unless HPET
|
||||
is enabled. */
|
||||
if (!acpi_use_timer_override && vendor == PCI_VENDOR_ID_NVIDIA) {
|
||||
if (!warned && acpi_table_parse(ACPI_SIG_HPET,
|
||||
nvidia_hpet_check)) {
|
||||
warned = 1;
|
||||
acpi_skip_timer_override = 1;
|
||||
printk(KERN_INFO "Nvidia board "
|
||||
"detected. Ignoring ACPI "
|
||||
"timer override.\n");
|
||||
printk(KERN_INFO "If you got timer trouble "
|
||||
"try acpi_use_timer_override\n");
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (vendor == PCI_VENDOR_ID_ATI && timer_over_8254 == 1) {
|
||||
timer_over_8254 = 0;
|
||||
printk(KERN_INFO "ATI board detected. Disabling timer routing "
|
||||
"over 8254.\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __init check_acpi_pci(void)
|
||||
{
|
||||
int num, slot, func;
|
||||
|
||||
/* Assume the machine supports type 1. If not it will
|
||||
always read ffffffff and should not have any side effect.
|
||||
Actually a few buggy systems can machine check. Allow the user
|
||||
to disable it by command line option at least -AK */
|
||||
if (!early_pci_allowed())
|
||||
return;
|
||||
|
||||
/* Poor man's PCI discovery */
|
||||
for (num = 0; num < 32; num++) {
|
||||
for (slot = 0; slot < 32; slot++) {
|
||||
for (func = 0; func < 8; func++) {
|
||||
u32 class;
|
||||
u32 vendor;
|
||||
class = read_pci_config(num, slot, func,
|
||||
PCI_CLASS_REVISION);
|
||||
if (class == 0xffffffff)
|
||||
break;
|
||||
|
||||
if ((class >> 16) != PCI_CLASS_BRIDGE_PCI)
|
||||
continue;
|
||||
|
||||
vendor = read_pci_config(num, slot, func,
|
||||
PCI_VENDOR_ID);
|
||||
|
||||
if (check_bridge(vendor & 0xffff, vendor >> 16))
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
75
arch/x86/kernel/acpi/processor.c
Normal file
75
arch/x86/kernel/acpi/processor.c
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* arch/i386/kernel/acpi/processor.c
|
||||
*
|
||||
* Copyright (C) 2005 Intel Corporation
|
||||
* Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
|
||||
* - Added _PDC for platforms with Intel CPUs
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include <acpi/processor.h>
|
||||
#include <asm/acpi.h>
|
||||
|
||||
static void init_intel_pdc(struct acpi_processor *pr, struct cpuinfo_x86 *c)
|
||||
{
|
||||
struct acpi_object_list *obj_list;
|
||||
union acpi_object *obj;
|
||||
u32 *buf;
|
||||
|
||||
/* allocate and initialize pdc. It will be used later. */
|
||||
obj_list = kmalloc(sizeof(struct acpi_object_list), GFP_KERNEL);
|
||||
if (!obj_list) {
|
||||
printk(KERN_ERR "Memory allocation error\n");
|
||||
return;
|
||||
}
|
||||
|
||||
obj = kmalloc(sizeof(union acpi_object), GFP_KERNEL);
|
||||
if (!obj) {
|
||||
printk(KERN_ERR "Memory allocation error\n");
|
||||
kfree(obj_list);
|
||||
return;
|
||||
}
|
||||
|
||||
buf = kmalloc(12, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
printk(KERN_ERR "Memory allocation error\n");
|
||||
kfree(obj);
|
||||
kfree(obj_list);
|
||||
return;
|
||||
}
|
||||
|
||||
buf[0] = ACPI_PDC_REVISION_ID;
|
||||
buf[1] = 1;
|
||||
buf[2] = ACPI_PDC_C_CAPABILITY_SMP;
|
||||
|
||||
if (cpu_has(c, X86_FEATURE_EST))
|
||||
buf[2] |= ACPI_PDC_EST_CAPABILITY_SWSMP;
|
||||
|
||||
obj->type = ACPI_TYPE_BUFFER;
|
||||
obj->buffer.length = 12;
|
||||
obj->buffer.pointer = (u8 *) buf;
|
||||
obj_list->count = 1;
|
||||
obj_list->pointer = obj;
|
||||
pr->pdc = obj_list;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialize _PDC data based on the CPU vendor */
|
||||
void arch_acpi_processor_init_pdc(struct acpi_processor *pr)
|
||||
{
|
||||
unsigned int cpu = pr->id;
|
||||
struct cpuinfo_x86 *c = cpu_data + cpu;
|
||||
|
||||
pr->pdc = NULL;
|
||||
if (c->x86_vendor == X86_VENDOR_INTEL)
|
||||
init_intel_pdc(pr, c);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(arch_acpi_processor_init_pdc);
|
110
arch/x86/kernel/acpi/sleep_32.c
Normal file
110
arch/x86/kernel/acpi/sleep_32.c
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* sleep.c - x86-specific ACPI sleep support.
|
||||
*
|
||||
* Copyright (C) 2001-2003 Patrick Mochel
|
||||
* Copyright (C) 2001-2003 Pavel Machek <pavel@suse.cz>
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/cpumask.h>
|
||||
|
||||
#include <asm/smp.h>
|
||||
|
||||
/* address in low memory of the wakeup routine. */
|
||||
unsigned long acpi_wakeup_address = 0;
|
||||
unsigned long acpi_realmode_flags;
|
||||
extern char wakeup_start, wakeup_end;
|
||||
|
||||
extern unsigned long FASTCALL(acpi_copy_wakeup_routine(unsigned long));
|
||||
|
||||
/**
|
||||
* acpi_save_state_mem - save kernel state
|
||||
*
|
||||
* Create an identity mapped page table and copy the wakeup routine to
|
||||
* low memory.
|
||||
*/
|
||||
int acpi_save_state_mem(void)
|
||||
{
|
||||
if (!acpi_wakeup_address)
|
||||
return 1;
|
||||
memcpy((void *)acpi_wakeup_address, &wakeup_start,
|
||||
&wakeup_end - &wakeup_start);
|
||||
acpi_copy_wakeup_routine(acpi_wakeup_address);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* acpi_restore_state - undo effects of acpi_save_state_mem
|
||||
*/
|
||||
void acpi_restore_state_mem(void)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_reserve_bootmem - do _very_ early ACPI initialisation
|
||||
*
|
||||
* We allocate a page from the first 1MB of memory for the wakeup
|
||||
* routine for when we come back from a sleep state. The
|
||||
* runtime allocator allows specification of <16MB pages, but not
|
||||
* <1MB pages.
|
||||
*/
|
||||
void __init acpi_reserve_bootmem(void)
|
||||
{
|
||||
if ((&wakeup_end - &wakeup_start) > PAGE_SIZE) {
|
||||
printk(KERN_ERR
|
||||
"ACPI: Wakeup code way too big, S3 disabled.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
acpi_wakeup_address = (unsigned long)alloc_bootmem_low(PAGE_SIZE);
|
||||
if (!acpi_wakeup_address)
|
||||
printk(KERN_ERR "ACPI: Cannot allocate lowmem, S3 disabled.\n");
|
||||
}
|
||||
|
||||
static int __init acpi_sleep_setup(char *str)
|
||||
{
|
||||
while ((str != NULL) && (*str != '\0')) {
|
||||
if (strncmp(str, "s3_bios", 7) == 0)
|
||||
acpi_realmode_flags |= 1;
|
||||
if (strncmp(str, "s3_mode", 7) == 0)
|
||||
acpi_realmode_flags |= 2;
|
||||
if (strncmp(str, "s3_beep", 7) == 0)
|
||||
acpi_realmode_flags |= 4;
|
||||
str = strchr(str, ',');
|
||||
if (str != NULL)
|
||||
str += strspn(str, ", \t");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("acpi_sleep=", acpi_sleep_setup);
|
||||
|
||||
/* Ouch, we want to delete this. We already have better version in userspace, in
|
||||
s2ram from suspend.sf.net project */
|
||||
static __init int reset_videomode_after_s3(struct dmi_system_id *d)
|
||||
{
|
||||
acpi_realmode_flags |= 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __initdata struct dmi_system_id acpisleep_dmi_table[] = {
|
||||
{ /* Reset video mode after returning from ACPI S3 sleep */
|
||||
.callback = reset_videomode_after_s3,
|
||||
.ident = "Toshiba Satellite 4030cdt",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "S4030CDT/4.3"),
|
||||
},
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
static int __init acpisleep_dmi_init(void)
|
||||
{
|
||||
dmi_check_system(acpisleep_dmi_table);
|
||||
return 0;
|
||||
}
|
||||
|
||||
core_initcall(acpisleep_dmi_init);
|
321
arch/x86/kernel/acpi/wakeup_32.S
Normal file
321
arch/x86/kernel/acpi/wakeup_32.S
Normal file
@ -0,0 +1,321 @@
|
||||
.text
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/segment.h>
|
||||
#include <asm/page.h>
|
||||
|
||||
#
|
||||
# wakeup_code runs in real mode, and at unknown address (determined at run-time).
|
||||
# Therefore it must only use relative jumps/calls.
|
||||
#
|
||||
# Do we need to deal with A20? It is okay: ACPI specs says A20 must be enabled
|
||||
#
|
||||
# If physical address of wakeup_code is 0x12345, BIOS should call us with
|
||||
# cs = 0x1234, eip = 0x05
|
||||
#
|
||||
|
||||
#define BEEP \
|
||||
inb $97, %al; \
|
||||
outb %al, $0x80; \
|
||||
movb $3, %al; \
|
||||
outb %al, $97; \
|
||||
outb %al, $0x80; \
|
||||
movb $-74, %al; \
|
||||
outb %al, $67; \
|
||||
outb %al, $0x80; \
|
||||
movb $-119, %al; \
|
||||
outb %al, $66; \
|
||||
outb %al, $0x80; \
|
||||
movb $15, %al; \
|
||||
outb %al, $66;
|
||||
|
||||
ALIGN
|
||||
.align 4096
|
||||
ENTRY(wakeup_start)
|
||||
wakeup_code:
|
||||
wakeup_code_start = .
|
||||
.code16
|
||||
|
||||
movw $0xb800, %ax
|
||||
movw %ax,%fs
|
||||
movw $0x0e00 + 'L', %fs:(0x10)
|
||||
|
||||
cli
|
||||
cld
|
||||
|
||||
# setup data segment
|
||||
movw %cs, %ax
|
||||
movw %ax, %ds # Make ds:0 point to wakeup_start
|
||||
movw %ax, %ss
|
||||
|
||||
testl $4, realmode_flags - wakeup_code
|
||||
jz 1f
|
||||
BEEP
|
||||
1:
|
||||
mov $(wakeup_stack - wakeup_code), %sp # Private stack is needed for ASUS board
|
||||
movw $0x0e00 + 'S', %fs:(0x12)
|
||||
|
||||
pushl $0 # Kill any dangerous flags
|
||||
popfl
|
||||
|
||||
movl real_magic - wakeup_code, %eax
|
||||
cmpl $0x12345678, %eax
|
||||
jne bogus_real_magic
|
||||
|
||||
testl $1, realmode_flags - wakeup_code
|
||||
jz 1f
|
||||
lcall $0xc000,$3
|
||||
movw %cs, %ax
|
||||
movw %ax, %ds # Bios might have played with that
|
||||
movw %ax, %ss
|
||||
1:
|
||||
|
||||
testl $2, realmode_flags - wakeup_code
|
||||
jz 1f
|
||||
mov video_mode - wakeup_code, %ax
|
||||
call mode_set
|
||||
1:
|
||||
|
||||
# set up page table
|
||||
movl $swsusp_pg_dir-__PAGE_OFFSET, %eax
|
||||
movl %eax, %cr3
|
||||
|
||||
testl $1, real_efer_save_restore - wakeup_code
|
||||
jz 4f
|
||||
# restore efer setting
|
||||
movl real_save_efer_edx - wakeup_code, %edx
|
||||
movl real_save_efer_eax - wakeup_code, %eax
|
||||
mov $0xc0000080, %ecx
|
||||
wrmsr
|
||||
4:
|
||||
# make sure %cr4 is set correctly (features, etc)
|
||||
movl real_save_cr4 - wakeup_code, %eax
|
||||
movl %eax, %cr4
|
||||
movw $0xb800, %ax
|
||||
movw %ax,%fs
|
||||
movw $0x0e00 + 'i', %fs:(0x12)
|
||||
|
||||
# need a gdt -- use lgdtl to force 32-bit operands, in case
|
||||
# the GDT is located past 16 megabytes.
|
||||
lgdtl real_save_gdt - wakeup_code
|
||||
|
||||
movl real_save_cr0 - wakeup_code, %eax
|
||||
movl %eax, %cr0
|
||||
jmp 1f
|
||||
1:
|
||||
movw $0x0e00 + 'n', %fs:(0x14)
|
||||
|
||||
movl real_magic - wakeup_code, %eax
|
||||
cmpl $0x12345678, %eax
|
||||
jne bogus_real_magic
|
||||
|
||||
testl $8, realmode_flags - wakeup_code
|
||||
jz 1f
|
||||
BEEP
|
||||
1:
|
||||
ljmpl $__KERNEL_CS, $wakeup_pmode_return
|
||||
|
||||
real_save_gdt: .word 0
|
||||
.long 0
|
||||
real_save_cr0: .long 0
|
||||
real_save_cr3: .long 0
|
||||
real_save_cr4: .long 0
|
||||
real_magic: .long 0
|
||||
video_mode: .long 0
|
||||
realmode_flags: .long 0
|
||||
beep_flags: .long 0
|
||||
real_efer_save_restore: .long 0
|
||||
real_save_efer_edx: .long 0
|
||||
real_save_efer_eax: .long 0
|
||||
|
||||
bogus_real_magic:
|
||||
movw $0x0e00 + 'B', %fs:(0x12)
|
||||
jmp bogus_real_magic
|
||||
|
||||
/* This code uses an extended set of video mode numbers. These include:
|
||||
* Aliases for standard modes
|
||||
* NORMAL_VGA (-1)
|
||||
* EXTENDED_VGA (-2)
|
||||
* ASK_VGA (-3)
|
||||
* Video modes numbered by menu position -- NOT RECOMMENDED because of lack
|
||||
* of compatibility when extending the table. These are between 0x00 and 0xff.
|
||||
*/
|
||||
#define VIDEO_FIRST_MENU 0x0000
|
||||
|
||||
/* Standard BIOS video modes (BIOS number + 0x0100) */
|
||||
#define VIDEO_FIRST_BIOS 0x0100
|
||||
|
||||
/* VESA BIOS video modes (VESA number + 0x0200) */
|
||||
#define VIDEO_FIRST_VESA 0x0200
|
||||
|
||||
/* Video7 special modes (BIOS number + 0x0900) */
|
||||
#define VIDEO_FIRST_V7 0x0900
|
||||
|
||||
# Setting of user mode (AX=mode ID) => CF=success
|
||||
|
||||
# For now, we only handle VESA modes (0x0200..0x03ff). To handle other
|
||||
# modes, we should probably compile in the video code from the boot
|
||||
# directory.
|
||||
mode_set:
|
||||
movw %ax, %bx
|
||||
subb $VIDEO_FIRST_VESA>>8, %bh
|
||||
cmpb $2, %bh
|
||||
jb check_vesa
|
||||
|
||||
setbad:
|
||||
clc
|
||||
ret
|
||||
|
||||
check_vesa:
|
||||
orw $0x4000, %bx # Use linear frame buffer
|
||||
movw $0x4f02, %ax # VESA BIOS mode set call
|
||||
int $0x10
|
||||
cmpw $0x004f, %ax # AL=4f if implemented
|
||||
jnz setbad # AH=0 if OK
|
||||
|
||||
stc
|
||||
ret
|
||||
|
||||
.code32
|
||||
ALIGN
|
||||
|
||||
.org 0x800
|
||||
wakeup_stack_begin: # Stack grows down
|
||||
|
||||
.org 0xff0 # Just below end of page
|
||||
wakeup_stack:
|
||||
ENTRY(wakeup_end)
|
||||
|
||||
.org 0x1000
|
||||
|
||||
wakeup_pmode_return:
|
||||
movw $__KERNEL_DS, %ax
|
||||
movw %ax, %ss
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %fs
|
||||
movw %ax, %gs
|
||||
movw $0x0e00 + 'u', 0xb8016
|
||||
|
||||
# reload the gdt, as we need the full 32 bit address
|
||||
lgdt saved_gdt
|
||||
lidt saved_idt
|
||||
lldt saved_ldt
|
||||
ljmp $(__KERNEL_CS),$1f
|
||||
1:
|
||||
movl %cr3, %eax
|
||||
movl %eax, %cr3
|
||||
wbinvd
|
||||
|
||||
# and restore the stack ... but you need gdt for this to work
|
||||
movl saved_context_esp, %esp
|
||||
|
||||
movl %cs:saved_magic, %eax
|
||||
cmpl $0x12345678, %eax
|
||||
jne bogus_magic
|
||||
|
||||
# jump to place where we left off
|
||||
movl saved_eip,%eax
|
||||
jmp *%eax
|
||||
|
||||
bogus_magic:
|
||||
movw $0x0e00 + 'B', 0xb8018
|
||||
jmp bogus_magic
|
||||
|
||||
|
||||
##
|
||||
# acpi_copy_wakeup_routine
|
||||
#
|
||||
# Copy the above routine to low memory.
|
||||
#
|
||||
# Parameters:
|
||||
# %eax: place to copy wakeup routine to
|
||||
#
|
||||
# Returned address is location of code in low memory (past data and stack)
|
||||
#
|
||||
ENTRY(acpi_copy_wakeup_routine)
|
||||
|
||||
pushl %ebx
|
||||
sgdt saved_gdt
|
||||
sidt saved_idt
|
||||
sldt saved_ldt
|
||||
str saved_tss
|
||||
|
||||
movl nx_enabled, %edx
|
||||
movl %edx, real_efer_save_restore - wakeup_start (%eax)
|
||||
testl $1, real_efer_save_restore - wakeup_start (%eax)
|
||||
jz 2f
|
||||
# save efer setting
|
||||
pushl %eax
|
||||
movl %eax, %ebx
|
||||
mov $0xc0000080, %ecx
|
||||
rdmsr
|
||||
movl %edx, real_save_efer_edx - wakeup_start (%ebx)
|
||||
movl %eax, real_save_efer_eax - wakeup_start (%ebx)
|
||||
popl %eax
|
||||
2:
|
||||
|
||||
movl %cr3, %edx
|
||||
movl %edx, real_save_cr3 - wakeup_start (%eax)
|
||||
movl %cr4, %edx
|
||||
movl %edx, real_save_cr4 - wakeup_start (%eax)
|
||||
movl %cr0, %edx
|
||||
movl %edx, real_save_cr0 - wakeup_start (%eax)
|
||||
sgdt real_save_gdt - wakeup_start (%eax)
|
||||
|
||||
movl saved_videomode, %edx
|
||||
movl %edx, video_mode - wakeup_start (%eax)
|
||||
movl acpi_realmode_flags, %edx
|
||||
movl %edx, realmode_flags - wakeup_start (%eax)
|
||||
movl $0x12345678, real_magic - wakeup_start (%eax)
|
||||
movl $0x12345678, saved_magic
|
||||
popl %ebx
|
||||
ret
|
||||
|
||||
save_registers:
|
||||
leal 4(%esp), %eax
|
||||
movl %eax, saved_context_esp
|
||||
movl %ebx, saved_context_ebx
|
||||
movl %ebp, saved_context_ebp
|
||||
movl %esi, saved_context_esi
|
||||
movl %edi, saved_context_edi
|
||||
pushfl ; popl saved_context_eflags
|
||||
|
||||
movl $ret_point, saved_eip
|
||||
ret
|
||||
|
||||
|
||||
restore_registers:
|
||||
movl saved_context_ebp, %ebp
|
||||
movl saved_context_ebx, %ebx
|
||||
movl saved_context_esi, %esi
|
||||
movl saved_context_edi, %edi
|
||||
pushl saved_context_eflags ; popfl
|
||||
ret
|
||||
|
||||
ENTRY(do_suspend_lowlevel)
|
||||
call save_processor_state
|
||||
call save_registers
|
||||
pushl $3
|
||||
call acpi_enter_sleep_state
|
||||
addl $4, %esp
|
||||
|
||||
# In case of S3 failure, we'll emerge here. Jump
|
||||
# to ret_point to recover
|
||||
jmp ret_point
|
||||
.p2align 4,,7
|
||||
ret_point:
|
||||
call restore_registers
|
||||
call restore_processor_state
|
||||
ret
|
||||
|
||||
.data
|
||||
ALIGN
|
||||
ENTRY(saved_magic) .long 0
|
||||
ENTRY(saved_eip) .long 0
|
||||
|
||||
# saved registers
|
||||
saved_gdt: .long 0,0
|
||||
saved_idt: .long 0,0
|
||||
saved_ldt: .long 0
|
||||
saved_tss: .long 0
|
||||
|
Reference in New Issue
Block a user