x86: move suspend wakeup code to C

Move wakeup code to .c, so that video mode setting code can be shared
between boot and wakeup. Remove nasty assembly code in 64-bit case by
re-using trampoline code. Stack setup was fixed to clear high 16bits
of %esp, maybe that fixes some machines.

.c code sharing and morse code was done H. Peter Anvin, Sam Ravnborg
reviewed kbuild related stuff, and it seems okay to him. Rafael did
some cleanups.

[rjw:
* Made the patch stop breaking compilation on x86-32
* Added arch/x86/kernel/acpi/sleep.h
* Got rid of compiler warnings in arch/x86/kernel/acpi/sleep.c
* Fixed 32-bit compilation on x86-64 systems
* Added include/asm-x86/trampoline.h and fixed the non-SMP
  compilation on 64-bit x86
* Removed arch/x86/kernel/acpi/sleep_32.c which was not used
* Fixed some breakage caused by the integration of smpboot.c done
  under us in the meantime]

Signed-off-by: Pavel Machek <pavel@suse.cz>
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
Pavel Machek
2008-04-10 23:28:10 +02:00
committed by Ingo Molnar
parent f49688d459
commit e44b7b7525
34 changed files with 710 additions and 772 deletions

View File

@@ -181,7 +181,7 @@ config X86_BIOS_REBOOT
config X86_TRAMPOLINE
bool
depends on X86_SMP || (X86_VOYAGER && SMP)
depends on X86_SMP || (X86_VOYAGER && SMP) || (64BIT && ACPI_SLEEP)
default y
config KTIME_SCALAR

View File

@@ -30,7 +30,7 @@ subdir- := compressed
setup-y += a20.o cmdline.o copy.o cpu.o cpucheck.o edd.o
setup-y += header.o main.o mca.o memory.o pm.o pmjump.o
setup-y += printf.o string.o tty.o video.o version.o
setup-y += printf.o string.o tty.o video.o video-mode.o version.o
setup-$(CONFIG_X86_APM_BOOT) += apm.o
setup-$(CONFIG_X86_VOYAGER) += voyager.o

View File

@@ -286,6 +286,11 @@ int getchar_timeout(void);
/* video.c */
void set_video(void);
/* video-mode.c */
int set_mode(u16 mode);
int mode_defined(u16 mode);
void probe_cards(int unsafe);
/* video-vesa.c */
void vesa_store_edid(void);

View File

@@ -50,6 +50,7 @@ static int set_bios_mode(u8 mode)
if (new_mode == mode)
return 0; /* Mode change OK */
#ifndef _WAKEUP
if (new_mode != boot_params.screen_info.orig_video_mode) {
/* Mode setting failed, but we didn't end up where we
started. That's bad. Try to revert to the original
@@ -59,13 +60,18 @@ static int set_bios_mode(u8 mode)
: "+a" (ax)
: : "ebx", "ecx", "edx", "esi", "edi");
}
#endif
return -1;
}
static int bios_probe(void)
{
u8 mode;
#ifdef _WAKEUP
u8 saved_mode = 0x03;
#else
u8 saved_mode = boot_params.screen_info.orig_video_mode;
#endif
u16 crtc;
struct mode_info *mi;
int nmodes = 0;

173
arch/x86/boot/video-mode.c Normal file
View File

@@ -0,0 +1,173 @@
/* -*- linux-c -*- ------------------------------------------------------- *
*
* Copyright (C) 1991, 1992 Linus Torvalds
* Copyright 2007-2008 rPath, Inc. - All Rights Reserved
*
* This file is part of the Linux kernel, and is made available under
* the terms of the GNU General Public License version 2.
*
* ----------------------------------------------------------------------- */
/*
* arch/i386/boot/video-mode.c
*
* Set the video mode. This is separated out into a different
* file in order to be shared with the ACPI wakeup code.
*/
#include "boot.h"
#include "video.h"
#include "vesa.h"
/*
* Common variables
*/
int adapter; /* 0=CGA/MDA/HGC, 1=EGA, 2=VGA+ */
u16 video_segment;
int force_x, force_y; /* Don't query the BIOS for cols/rows */
int do_restore; /* Screen contents changed during mode flip */
int graphic_mode; /* Graphic mode with linear frame buffer */
/* Probe the video drivers and have them generate their mode lists. */
void probe_cards(int unsafe)
{
struct card_info *card;
static u8 probed[2];
if (probed[unsafe])
return;
probed[unsafe] = 1;
for (card = video_cards; card < video_cards_end; card++) {
if (card->unsafe == unsafe) {
if (card->probe)
card->nmodes = card->probe();
else
card->nmodes = 0;
}
}
}
/* Test if a mode is defined */
int mode_defined(u16 mode)
{
struct card_info *card;
struct mode_info *mi;
int i;
for (card = video_cards; card < video_cards_end; card++) {
mi = card->modes;
for (i = 0; i < card->nmodes; i++, mi++) {
if (mi->mode == mode)
return 1;
}
}
return 0;
}
/* Set mode (without recalc) */
static int raw_set_mode(u16 mode, u16 *real_mode)
{
int nmode, i;
struct card_info *card;
struct mode_info *mi;
/* Drop the recalc bit if set */
mode &= ~VIDEO_RECALC;
/* Scan for mode based on fixed ID, position, or resolution */
nmode = 0;
for (card = video_cards; card < video_cards_end; card++) {
mi = card->modes;
for (i = 0; i < card->nmodes; i++, mi++) {
int visible = mi->x || mi->y;
if ((mode == nmode && visible) ||
mode == mi->mode ||
mode == (mi->y << 8)+mi->x) {
*real_mode = mi->mode;
return card->set_mode(mi);
}
if (visible)
nmode++;
}
}
/* Nothing found? Is it an "exceptional" (unprobed) mode? */
for (card = video_cards; card < video_cards_end; card++) {
if (mode >= card->xmode_first &&
mode < card->xmode_first+card->xmode_n) {
struct mode_info mix;
*real_mode = mix.mode = mode;
mix.x = mix.y = 0;
return card->set_mode(&mix);
}
}
/* Otherwise, failure... */
return -1;
}
/*
* Recalculate the vertical video cutoff (hack!)
*/
static void vga_recalc_vertical(void)
{
unsigned int font_size, rows;
u16 crtc;
u8 pt, ov;
set_fs(0);
font_size = rdfs8(0x485); /* BIOS: font size (pixels) */
rows = force_y ? force_y : rdfs8(0x484)+1; /* Text rows */
rows *= font_size; /* Visible scan lines */
rows--; /* ... minus one */
crtc = vga_crtc();
pt = in_idx(crtc, 0x11);
pt &= ~0x80; /* Unlock CR0-7 */
out_idx(pt, crtc, 0x11);
out_idx((u8)rows, crtc, 0x12); /* Lower height register */
ov = in_idx(crtc, 0x07); /* Overflow register */
ov &= 0xbd;
ov |= (rows >> (8-1)) & 0x02;
ov |= (rows >> (9-6)) & 0x40;
out_idx(ov, crtc, 0x07);
}
/* Set mode (with recalc if specified) */
int set_mode(u16 mode)
{
int rv;
u16 real_mode;
/* Very special mode numbers... */
if (mode == VIDEO_CURRENT_MODE)
return 0; /* Nothing to do... */
else if (mode == NORMAL_VGA)
mode = VIDEO_80x25;
else if (mode == EXTENDED_VGA)
mode = VIDEO_8POINT;
rv = raw_set_mode(mode, &real_mode);
if (rv)
return rv;
if (mode & VIDEO_RECALC)
vga_recalc_vertical();
/* Save the canonical mode number for the kernel, not
an alias, size specification or menu position */
#ifndef _WAKEUP
boot_params.hdr.vid_mode = real_mode;
#endif
return 0;
}

View File

@@ -24,7 +24,11 @@ static struct vesa_mode_info vminfo;
__videocard video_vesa;
#ifndef _WAKEUP
static void vesa_store_mode_params_graphics(void);
#else /* _WAKEUP */
static inline void vesa_store_mode_params_graphics(void) {}
#endif /* _WAKEUP */
static int vesa_probe(void)
{
@@ -165,6 +169,8 @@ static int vesa_set_mode(struct mode_info *mode)
}
#ifndef _WAKEUP
/* Switch DAC to 8-bit mode */
static void vesa_dac_set_8bits(void)
{
@@ -288,6 +294,8 @@ void vesa_store_edid(void)
#endif /* CONFIG_FIRMWARE_EDID */
}
#endif /* not _WAKEUP */
__videocard video_vesa =
{
.card_name = "VESA",

View File

@@ -210,6 +210,8 @@ static int vga_set_mode(struct mode_info *mode)
*/
static int vga_probe(void)
{
u16 ega_bx;
static const char *card_name[] = {
"CGA/MDA/HGC", "EGA", "VGA"
};
@@ -226,12 +228,16 @@ static int vga_probe(void)
u8 vga_flag;
asm(INT10
: "=b" (boot_params.screen_info.orig_video_ega_bx)
: "=b" (ega_bx)
: "a" (0x1200), "b" (0x10) /* Check EGA/VGA */
: "ecx", "edx", "esi", "edi");
#ifndef _WAKEUP
boot_params.screen_info.orig_video_ega_bx = ega_bx;
#endif
/* If we have MDA/CGA/HGC then BL will be unchanged at 0x10 */
if ((u8)boot_params.screen_info.orig_video_ega_bx != 0x10) {
if ((u8)ega_bx != 0x10) {
/* EGA/VGA */
asm(INT10
: "=a" (vga_flag)
@@ -240,7 +246,9 @@ static int vga_probe(void)
if (vga_flag == 0x1a) {
adapter = ADAPTER_VGA;
#ifndef _WAKEUP
boot_params.screen_info.orig_video_isVGA = 1;
#endif
} else {
adapter = ADAPTER_EGA;
}

View File

@@ -18,21 +18,6 @@
#include "video.h"
#include "vesa.h"
/*
* Mode list variables
*/
static struct card_info cards[]; /* List of cards to probe for */
/*
* Common variables
*/
int adapter; /* 0=CGA/MDA/HGC, 1=EGA, 2=VGA+ */
u16 video_segment;
int force_x, force_y; /* Don't query the BIOS for cols/rows */
int do_restore = 0; /* Screen contents changed during mode flip */
int graphic_mode; /* Graphic mode with linear frame buffer */
static void store_cursor_position(void)
{
u16 curpos;
@@ -107,147 +92,6 @@ static void store_mode_params(void)
boot_params.screen_info.orig_video_lines = y;
}
/* Probe the video drivers and have them generate their mode lists. */
static void probe_cards(int unsafe)
{
struct card_info *card;
static u8 probed[2];
if (probed[unsafe])
return;
probed[unsafe] = 1;
for (card = video_cards; card < video_cards_end; card++) {
if (card->unsafe == unsafe) {
if (card->probe)
card->nmodes = card->probe();
else
card->nmodes = 0;
}
}
}
/* Test if a mode is defined */
int mode_defined(u16 mode)
{
struct card_info *card;
struct mode_info *mi;
int i;
for (card = video_cards; card < video_cards_end; card++) {
mi = card->modes;
for (i = 0; i < card->nmodes; i++, mi++) {
if (mi->mode == mode)
return 1;
}
}
return 0;
}
/* Set mode (without recalc) */
static int raw_set_mode(u16 mode, u16 *real_mode)
{
int nmode, i;
struct card_info *card;
struct mode_info *mi;
/* Drop the recalc bit if set */
mode &= ~VIDEO_RECALC;
/* Scan for mode based on fixed ID, position, or resolution */
nmode = 0;
for (card = video_cards; card < video_cards_end; card++) {
mi = card->modes;
for (i = 0; i < card->nmodes; i++, mi++) {
int visible = mi->x || mi->y;
if ((mode == nmode && visible) ||
mode == mi->mode ||
mode == (mi->y << 8)+mi->x) {
*real_mode = mi->mode;
return card->set_mode(mi);
}
if (visible)
nmode++;
}
}
/* Nothing found? Is it an "exceptional" (unprobed) mode? */
for (card = video_cards; card < video_cards_end; card++) {
if (mode >= card->xmode_first &&
mode < card->xmode_first+card->xmode_n) {
struct mode_info mix;
*real_mode = mix.mode = mode;
mix.x = mix.y = 0;
return card->set_mode(&mix);
}
}
/* Otherwise, failure... */
return -1;
}
/*
* Recalculate the vertical video cutoff (hack!)
*/
static void vga_recalc_vertical(void)
{
unsigned int font_size, rows;
u16 crtc;
u8 pt, ov;
set_fs(0);
font_size = rdfs8(0x485); /* BIOS: font size (pixels) */
rows = force_y ? force_y : rdfs8(0x484)+1; /* Text rows */
rows *= font_size; /* Visible scan lines */
rows--; /* ... minus one */
crtc = vga_crtc();
pt = in_idx(crtc, 0x11);
pt &= ~0x80; /* Unlock CR0-7 */
out_idx(pt, crtc, 0x11);
out_idx((u8)rows, crtc, 0x12); /* Lower height register */
ov = in_idx(crtc, 0x07); /* Overflow register */
ov &= 0xbd;
ov |= (rows >> (8-1)) & 0x02;
ov |= (rows >> (9-6)) & 0x40;
out_idx(ov, crtc, 0x07);
}
/* Set mode (with recalc if specified) */
static int set_mode(u16 mode)
{
int rv;
u16 real_mode;
/* Very special mode numbers... */
if (mode == VIDEO_CURRENT_MODE)
return 0; /* Nothing to do... */
else if (mode == NORMAL_VGA)
mode = VIDEO_80x25;
else if (mode == EXTENDED_VGA)
mode = VIDEO_8POINT;
rv = raw_set_mode(mode, &real_mode);
if (rv)
return rv;
if (mode & VIDEO_RECALC)
vga_recalc_vertical();
/* Save the canonical mode number for the kernel, not
an alias, size specification or menu position */
boot_params.hdr.vid_mode = real_mode;
return 0;
}
static unsigned int get_entry(void)
{
char entry_buf[4];
@@ -486,6 +330,7 @@ void set_video(void)
printf("Undefined video mode number: %x\n", mode);
mode = ASK_VGA;
}
boot_params.hdr.vid_mode = mode;
vesa_store_edid();
store_mode_params();

View File

@@ -1,7 +1,14 @@
subdir- := realmode
obj-$(CONFIG_ACPI) += boot.o
obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_$(BITS).o
obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_rm.o wakeup_$(BITS).o
ifneq ($(CONFIG_ACPI_PROCESSOR),)
obj-y += cstate.o processor.o
endif
$(obj)/wakeup_rm.o: $(obj)/realmode/wakeup.bin
$(obj)/realmode/wakeup.bin: FORCE
$(Q)$(MAKE) $(build)=$(obj)/realmode $@

View File

@@ -0,0 +1,57 @@
#
# arch/x86/kernel/acpi/realmode/Makefile
#
# This file is subject to the terms and conditions of the GNU General Public
# License. See the file "COPYING" in the main directory of this archive
# for more details.
#
targets := wakeup.bin wakeup.elf
wakeup-y += wakeup.o wakemain.o video-mode.o copy.o
# The link order of the video-*.o modules can matter. In particular,
# video-vga.o *must* be listed first, followed by video-vesa.o.
# Hardware-specific drivers should follow in the order they should be
# probed, and video-bios.o should typically be last.
wakeup-y += video-vga.o
wakeup-y += video-vesa.o
wakeup-y += video-bios.o
targets += $(wakeup-y)
bootsrc := $(src)/../../../boot
# ---------------------------------------------------------------------------
# How to compile the 16-bit code. Note we always compile for -march=i386,
# that way we can complain to the user if the CPU is insufficient.
# Compile with _SETUP since this is similar to the boot-time setup code.
KBUILD_CFLAGS := $(LINUXINCLUDE) -g -Os -D_SETUP -D_WAKEUP -D__KERNEL__ \
-I$(srctree)/$(bootsrc) \
$(cflags-y) \
-Wall -Wstrict-prototypes \
-march=i386 -mregparm=3 \
-include $(srctree)/$(bootsrc)/code16gcc.h \
-fno-strict-aliasing -fomit-frame-pointer \
$(call cc-option, -ffreestanding) \
$(call cc-option, -fno-toplevel-reorder,\
$(call cc-option, -fno-unit-at-a-time)) \
$(call cc-option, -fno-stack-protector) \
$(call cc-option, -mpreferred-stack-boundary=2)
KBUILD_CFLAGS += $(call cc-option, -m32)
KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__
WAKEUP_OBJS = $(addprefix $(obj)/,$(wakeup-y))
LDFLAGS_wakeup.elf := -T
CPPFLAGS_wakeup.lds += -P -C
$(obj)/wakeup.elf: $(src)/wakeup.lds $(WAKEUP_OBJS) FORCE
$(call if_changed,ld)
OBJCOPYFLAGS_wakeup.bin := -O binary
$(obj)/wakeup.bin: $(obj)/wakeup.elf FORCE
$(call if_changed,objcopy)

View File

@@ -0,0 +1 @@
#include "../../../boot/copy.S"

View File

@@ -0,0 +1 @@
#include "../../../boot/video-bios.c"

View File

@@ -0,0 +1 @@
#include "../../../boot/video-mode.c"

View File

@@ -0,0 +1 @@
#include "../../../boot/video-vesa.c"

View File

@@ -0,0 +1 @@
#include "../../../boot/video-vga.c"

View File

@@ -0,0 +1,81 @@
#include "wakeup.h"
#include "boot.h"
static void udelay(int loops)
{
while (loops--)
io_delay(); /* Approximately 1 us */
}
static void beep(unsigned int hz)
{
u8 enable;
if (!hz) {
enable = 0x00; /* Turn off speaker */
} else {
u16 div = 1193181/hz;
outb(0xb6, 0x43); /* Ctr 2, squarewave, load, binary */
io_delay();
outb(div, 0x42); /* LSB of counter */
io_delay();
outb(div >> 8, 0x42); /* MSB of counter */
io_delay();
enable = 0x03; /* Turn on speaker */
}
inb(0x61); /* Dummy read of System Control Port B */
io_delay();
outb(enable, 0x61); /* Enable timer 2 output to speaker */
io_delay();
}
#define DOT_HZ 880
#define DASH_HZ 587
#define US_PER_DOT 125000
/* Okay, this is totally silly, but it's kind of fun. */
static void send_morse(const char *pattern)
{
char s;
while ((s = *pattern++)) {
switch (s) {
case '.':
beep(DOT_HZ);
udelay(US_PER_DOT);
beep(0);
udelay(US_PER_DOT);
break;
case '-':
beep(DASH_HZ);
udelay(US_PER_DOT * 3);
beep(0);
udelay(US_PER_DOT);
break;
default: /* Assume it's a space */
udelay(US_PER_DOT * 3);
break;
}
}
}
void main(void)
{
/* Kill machine if structures are wrong */
if (wakeup_header.real_magic != 0x12345678)
while (1);
if (wakeup_header.realmode_flags & 4)
send_morse("...-");
if (wakeup_header.realmode_flags & 1)
asm volatile("lcallw $0xc000,$3");
if (wakeup_header.realmode_flags & 2) {
/* Need to call BIOS */
probe_cards(0);
set_mode(wakeup_header.video_mode);
}
}

View File

@@ -0,0 +1,113 @@
/*
* ACPI wakeup real mode startup stub
*/
#include <asm/segment.h>
#include <asm/msr-index.h>
#include <asm/page.h>
#include <asm/pgtable.h>
.code16
.section ".header", "a"
/* This should match the structure in wakeup.h */
.globl wakeup_header
wakeup_header:
video_mode: .short 0 /* Video mode number */
pmode_return: .byte 0x66, 0xea /* ljmpl */
.long 0 /* offset goes here */
.short __KERNEL_CS
pmode_cr0: .long 0 /* Saved %cr0 */
pmode_cr3: .long 0 /* Saved %cr3 */
pmode_cr4: .long 0 /* Saved %cr4 */
pmode_efer: .quad 0 /* Saved EFER */
pmode_gdt: .quad 0
realmode_flags: .long 0
real_magic: .long 0
trampoline_segment: .word 0
signature: .long 0x51ee1111
.text
.globl _start
.code16
wakeup_code:
_start:
cli
cld
/* Set up segments */
movw %cs, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
movl $wakeup_stack_end, %esp
/* Clear the EFLAGS */
pushl $0
popfl
/* Check header signature... */
movl signature, %eax
cmpl $0x51ee1111, %eax
jne bogus_real_magic
/* Check we really have everything... */
movl end_signature, %eax
cmpl $0x65a22c82, %eax
jne bogus_real_magic
/* Call the C code */
calll main
/* Do any other stuff... */
#ifndef CONFIG_64BIT
/* This could also be done in C code... */
movl pmode_cr3, %eax
movl %eax, %cr3
movl pmode_cr4, %ecx
jecxz 1f
movl %ecx, %cr4
1:
movl pmode_efer, %eax
movl pmode_efer + 4, %edx
movl %eax, %ecx
orl %edx, %ecx
jz 1f
movl $0xc0000080, %ecx
wrmsr
1:
lgdtl pmode_gdt
/* This really couldn't... */
movl pmode_cr0, %eax
movl %eax, %cr0
jmp pmode_return
#else
pushw $0
pushw trampoline_segment
pushw $0
lret
#endif
bogus_real_magic:
1:
hlt
jmp 1b
.data
.balign 4
.globl HEAP, heap_end
HEAP:
.long wakeup_heap
heap_end:
.long wakeup_stack
.bss
wakeup_heap:
.space 2048
wakeup_stack:
.space 2048
wakeup_stack_end:

View File

@@ -0,0 +1,36 @@
/*
* Definitions for the wakeup data structure at the head of the
* wakeup code.
*/
#ifndef ARCH_X86_KERNEL_ACPI_RM_WAKEUP_H
#define ARCH_X86_KERNEL_ACPI_RM_WAKEUP_H
#ifndef __ASSEMBLY__
#include <linux/types.h>
/* This must match data at wakeup.S */
struct wakeup_header {
u16 video_mode; /* Video mode number */
u16 _jmp1; /* ljmpl opcode, 32-bit only */
u32 pmode_entry; /* Protected mode resume point, 32-bit only */
u16 _jmp2; /* CS value, 32-bit only */
u32 pmode_cr0; /* Protected mode cr0 */
u32 pmode_cr3; /* Protected mode cr3 */
u32 pmode_cr4; /* Protected mode cr4 */
u32 pmode_efer_low; /* Protected mode EFER */
u32 pmode_efer_high;
u64 pmode_gdt;
u32 realmode_flags;
u32 real_magic;
u16 trampoline_segment; /* segment with trampoline code, 64-bit only */
u32 signature; /* To check we have correct structure */
} __attribute__((__packed__));
extern struct wakeup_header wakeup_header;
#endif
#define HEADER_OFFSET 0x3f00
#define WAKEUP_SIZE 0x4000
#endif /* ARCH_X86_KERNEL_ACPI_RM_WAKEUP_H */

View File

@@ -0,0 +1,61 @@
/*
* wakeup.ld
*
* Linker script for the real-mode wakeup code
*/
#undef i386
#include "wakeup.h"
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(_start)
SECTIONS
{
. = HEADER_OFFSET;
.header : {
*(.header)
}
. = 0;
.text : {
*(.text*)
}
. = ALIGN(16);
.rodata : {
*(.rodata*)
}
.videocards : {
video_cards = .;
*(.videocards)
video_cards_end = .;
}
. = ALIGN(16);
.data : {
*(.data*)
}
.signature : {
end_signature = .;
LONG(0x65a22c82)
}
. = ALIGN(16);
.bss : {
__bss_start = .;
*(.bss)
__bss_end = .;
}
. = ALIGN(16);
_end = .;
/DISCARD/ : {
*(.note*)
}
. = ASSERT(_end <= WAKEUP_SIZE, "Wakeup too big!");
}

View File

@@ -10,30 +10,72 @@
#include <linux/dmi.h>
#include <linux/cpumask.h>
#include <asm/smp.h>
#include "realmode/wakeup.h"
#include "sleep.h"
/* address in low memory of the wakeup routine. */
unsigned long acpi_wakeup_address;
unsigned long acpi_realmode_flags;
extern char wakeup_start, wakeup_end;
extern unsigned long acpi_copy_wakeup_routine(unsigned long);
/* address in low memory of the wakeup routine. */
static unsigned long acpi_realmode;
#ifdef CONFIG_64BIT
static char temp_stack[10240];
#endif
/**
* acpi_save_state_mem - save kernel state
*
* Create an identity mapped page table and copy the wakeup routine to
* low memory.
*
* Note that this is too late to change acpi_wakeup_address.
*/
int acpi_save_state_mem(void)
{
if (!acpi_wakeup_address) {
printk(KERN_ERR "Could not allocate memory during boot, S3 disabled\n");
struct wakeup_header *header;
if (!acpi_realmode) {
printk(KERN_ERR "Could not allocate memory during boot, "
"S3 disabled\n");
return -ENOMEM;
}
memcpy((void *)acpi_wakeup_address, &wakeup_start,
&wakeup_end - &wakeup_start);
acpi_copy_wakeup_routine(acpi_wakeup_address);
memcpy((void *)acpi_realmode, &wakeup_code_start, WAKEUP_SIZE);
header = (struct wakeup_header *)(acpi_realmode + HEADER_OFFSET);
if (header->signature != 0x51ee1111) {
printk(KERN_ERR "wakeup header does not match\n");
return -EINVAL;
}
header->video_mode = saved_video_mode;
#ifndef CONFIG_64BIT
store_gdt((struct desc_ptr *)&header->pmode_gdt);
header->pmode_efer_low = nx_enabled;
if (header->pmode_efer_low & 1) {
/* This is strange, why not save efer, always? */
rdmsr(MSR_EFER, header->pmode_efer_low,
header->pmode_efer_high);
}
#endif /* !CONFIG_64BIT */
header->pmode_cr0 = read_cr0();
header->pmode_cr4 = read_cr4();
header->realmode_flags = acpi_realmode_flags;
header->real_magic = 0x12345678;
#ifndef CONFIG_64BIT
header->pmode_entry = (u32)&wakeup_pmode_return;
header->pmode_cr3 = (u32)(swsusp_pg_dir - __PAGE_OFFSET);
saved_magic = 0x12345678;
#else /* CONFIG_64BIT */
header->trampoline_segment = setup_trampoline() >> 4;
init_rsp = (unsigned long)temp_stack + 4096;
initial_code = (unsigned long)wakeup_long64;
saved_magic = 0x123456789abcdef0;
#endif /* CONFIG_64BIT */
return 0;
}
@@ -56,15 +98,20 @@ void acpi_restore_state_mem(void)
*/
void __init acpi_reserve_bootmem(void)
{
if ((&wakeup_end - &wakeup_start) > PAGE_SIZE*2) {
if ((&wakeup_code_end - &wakeup_code_start) > WAKEUP_SIZE) {
printk(KERN_ERR
"ACPI: Wakeup code way too big, S3 disabled.\n");
return;
}
acpi_wakeup_address = (unsigned long)alloc_bootmem_low(PAGE_SIZE*2);
if (!acpi_wakeup_address)
acpi_realmode = (unsigned long)alloc_bootmem_low(WAKEUP_SIZE);
if (!acpi_realmode) {
printk(KERN_ERR "ACPI: Cannot allocate lowmem, S3 disabled.\n");
return;
}
acpi_wakeup_address = acpi_realmode;
}

Some files were not shown because too many files have changed in this diff Show More