x86, realmode: Move ACPI wakeup to unified realmode code
Migrated ACPI wakeup code to the real-mode blob. Code existing in .x86_trampoline can be completely removed. Static descriptor table in wakeup_asm.S is courtesy of H. Peter Anvin. Signed-off-by: Jarkko Sakkinen <jarkko.sakkinen@intel.com> Link: http://lkml.kernel.org/r/1336501366-28617-7-git-send-email-jarkko.sakkinen@intel.com Cc: Rafael J. Wysocki <rjw@sisk.pl> Cc: Len Brown <len.brown@intel.com> Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
This commit is contained in:

committed by
H. Peter Anvin

parent
48927bbb97
commit
c9b77ccb52
@@ -1,14 +1,7 @@
|
||||
subdir- := realmode
|
||||
|
||||
obj-$(CONFIG_ACPI) += boot.o
|
||||
obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_rm.o wakeup_$(BITS).o
|
||||
obj-$(CONFIG_ACPI_SLEEP) += sleep.o wakeup_$(BITS).o
|
||||
|
||||
ifneq ($(CONFIG_ACPI_PROCESSOR),)
|
||||
obj-y += cstate.o
|
||||
endif
|
||||
|
||||
$(obj)/wakeup_rm.o: $(obj)/realmode/wakeup.bin
|
||||
|
||||
$(obj)/realmode/wakeup.bin: FORCE
|
||||
$(Q)$(MAKE) $(build)=$(obj)/realmode
|
||||
|
||||
|
3
arch/x86/kernel/acpi/realmode/.gitignore
vendored
3
arch/x86/kernel/acpi/realmode/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
wakeup.bin
|
||||
wakeup.elf
|
||||
wakeup.lds
|
@@ -1,59 +0,0 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
always := wakeup.bin
|
||||
targets := wakeup.elf wakeup.lds
|
||||
|
||||
wakeup-y += wakeup.o wakemain.o video-mode.o copy.o bioscall.o regs.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__
|
||||
GCOV_PROFILE := n
|
||||
|
||||
WAKEUP_OBJS = $(addprefix $(obj)/,$(wakeup-y))
|
||||
|
||||
LDFLAGS_wakeup.elf := -T
|
||||
|
||||
CPPFLAGS_wakeup.lds += -P -C
|
||||
|
||||
$(obj)/wakeup.elf: $(obj)/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)
|
@@ -1 +0,0 @@
|
||||
#include "../../../boot/bioscall.S"
|
@@ -1 +0,0 @@
|
||||
#include "../../../boot/copy.S"
|
@@ -1 +0,0 @@
|
||||
#include "../../../boot/regs.c"
|
@@ -1 +0,0 @@
|
||||
#include "../../../boot/video-bios.c"
|
@@ -1 +0,0 @@
|
||||
#include "../../../boot/video-mode.c"
|
@@ -1 +0,0 @@
|
||||
#include "../../../boot/video-vesa.c"
|
@@ -1 +0,0 @@
|
||||
#include "../../../boot/video-vga.c"
|
@@ -1,81 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
}
|
@@ -1,170 +0,0 @@
|
||||
/*
|
||||
* ACPI wakeup real mode startup stub
|
||||
*/
|
||||
#include <asm/segment.h>
|
||||
#include <asm/msr-index.h>
|
||||
#include <asm/page_types.h>
|
||||
#include <asm/pgtable_types.h>
|
||||
#include <asm/processor-flags.h>
|
||||
#include "wakeup.h"
|
||||
|
||||
.code16
|
||||
.section ".jump", "ax"
|
||||
.globl _start
|
||||
_start:
|
||||
cli
|
||||
jmp wakeup_code
|
||||
|
||||
/* This should match the structure in wakeup.h */
|
||||
.section ".header", "a"
|
||||
.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
|
||||
pmode_misc_en: .quad 0 /* Saved MISC_ENABLE MSR */
|
||||
pmode_behavior: .long 0 /* Wakeup behavior flags */
|
||||
realmode_flags: .long 0
|
||||
real_magic: .long 0
|
||||
trampoline_segment: .word 0
|
||||
_pad1: .byte 0
|
||||
wakeup_jmp: .byte 0xea /* ljmpw */
|
||||
wakeup_jmp_off: .word 3f
|
||||
wakeup_jmp_seg: .word 0
|
||||
wakeup_gdt: .quad 0, 0, 0
|
||||
signature: .long WAKEUP_HEADER_SIGNATURE
|
||||
|
||||
.text
|
||||
.code16
|
||||
wakeup_code:
|
||||
cld
|
||||
|
||||
/* Apparently some dimwit BIOS programmers don't know how to
|
||||
program a PM to RM transition, and we might end up here with
|
||||
junk in the data segment descriptor registers. The only way
|
||||
to repair that is to go into PM and fix it ourselves... */
|
||||
movw $16, %cx
|
||||
lgdtl %cs:wakeup_gdt
|
||||
movl %cr0, %eax
|
||||
orb $X86_CR0_PE, %al
|
||||
movl %eax, %cr0
|
||||
jmp 1f
|
||||
1: ljmpw $8, $2f
|
||||
2:
|
||||
movw %cx, %ds
|
||||
movw %cx, %es
|
||||
movw %cx, %ss
|
||||
movw %cx, %fs
|
||||
movw %cx, %gs
|
||||
|
||||
andb $~X86_CR0_PE, %al
|
||||
movl %eax, %cr0
|
||||
jmp wakeup_jmp
|
||||
3:
|
||||
/* Set up segments */
|
||||
movw %cs, %ax
|
||||
movw %ax, %ds
|
||||
movw %ax, %es
|
||||
movw %ax, %ss
|
||||
lidtl wakeup_idt
|
||||
|
||||
movl $wakeup_stack_end, %esp
|
||||
|
||||
/* Clear the EFLAGS */
|
||||
pushl $0
|
||||
popfl
|
||||
|
||||
/* Check header signature... */
|
||||
movl signature, %eax
|
||||
cmpl $WAKEUP_HEADER_SIGNATURE, %eax
|
||||
jne bogus_real_magic
|
||||
|
||||
/* Check we really have everything... */
|
||||
movl end_signature, %eax
|
||||
cmpl $WAKEUP_END_SIGNATURE, %eax
|
||||
jne bogus_real_magic
|
||||
|
||||
/* Call the C code */
|
||||
calll main
|
||||
|
||||
/* Restore MISC_ENABLE before entering protected mode, in case
|
||||
BIOS decided to clear XD_DISABLE during S3. */
|
||||
movl pmode_behavior, %eax
|
||||
btl $WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %eax
|
||||
jnc 1f
|
||||
|
||||
movl pmode_misc_en, %eax
|
||||
movl pmode_misc_en + 4, %edx
|
||||
movl $MSR_IA32_MISC_ENABLE, %ecx
|
||||
wrmsr
|
||||
1:
|
||||
|
||||
/* 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 $MSR_EFER, %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 8
|
||||
|
||||
/* This is the standard real-mode IDT */
|
||||
wakeup_idt:
|
||||
.word 0xffff /* limit */
|
||||
.long 0 /* address */
|
||||
.word 0
|
||||
|
||||
.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:
|
||||
|
||||
.section ".signature","a"
|
||||
end_signature:
|
||||
.long WAKEUP_END_SIGNATURE
|
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* 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 pmode_misc_en_low; /* Protected mode MISC_ENABLE */
|
||||
u32 pmode_misc_en_high;
|
||||
u32 pmode_behavior; /* Wakeup routine behavior flags */
|
||||
u32 realmode_flags;
|
||||
u32 real_magic;
|
||||
u16 trampoline_segment; /* segment with trampoline code, 64-bit only */
|
||||
u8 _pad1;
|
||||
u8 wakeup_jmp;
|
||||
u16 wakeup_jmp_off;
|
||||
u16 wakeup_jmp_seg;
|
||||
u64 wakeup_gdt[3];
|
||||
u32 signature; /* To check we have correct structure */
|
||||
} __attribute__((__packed__));
|
||||
|
||||
extern struct wakeup_header wakeup_header;
|
||||
#endif
|
||||
|
||||
#define WAKEUP_HEADER_OFFSET 8
|
||||
#define WAKEUP_HEADER_SIGNATURE 0x51ee1111
|
||||
#define WAKEUP_END_SIGNATURE 0x65a22c82
|
||||
|
||||
/* Wakeup behavior bits */
|
||||
#define WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE 0
|
||||
|
||||
#endif /* ARCH_X86_KERNEL_ACPI_RM_WAKEUP_H */
|
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
{
|
||||
. = 0;
|
||||
.jump : {
|
||||
*(.jump)
|
||||
} = 0x90909090
|
||||
|
||||
. = WAKEUP_HEADER_OFFSET;
|
||||
.header : {
|
||||
*(.header)
|
||||
}
|
||||
|
||||
. = ALIGN(16);
|
||||
.text : {
|
||||
*(.text*)
|
||||
} = 0x90909090
|
||||
|
||||
. = ALIGN(16);
|
||||
.rodata : {
|
||||
*(.rodata*)
|
||||
}
|
||||
|
||||
.videocards : {
|
||||
video_cards = .;
|
||||
*(.videocards)
|
||||
video_cards_end = .;
|
||||
}
|
||||
|
||||
. = ALIGN(16);
|
||||
.data : {
|
||||
*(.data*)
|
||||
}
|
||||
|
||||
. = ALIGN(16);
|
||||
.bss : {
|
||||
__bss_start = .;
|
||||
*(.bss)
|
||||
__bss_end = .;
|
||||
}
|
||||
|
||||
.signature : {
|
||||
*(.signature)
|
||||
}
|
||||
|
||||
_end = .;
|
||||
|
||||
/DISCARD/ : {
|
||||
*(.note*)
|
||||
}
|
||||
}
|
@@ -14,8 +14,9 @@
|
||||
#include <asm/desc.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/realmode.h>
|
||||
|
||||
#include "realmode/wakeup.h"
|
||||
#include "../../realmode/rm/wakeup/wakeup.h"
|
||||
#include "sleep.h"
|
||||
|
||||
unsigned long acpi_realmode_flags;
|
||||
@@ -36,13 +37,9 @@ asmlinkage void acpi_enter_s3(void)
|
||||
*/
|
||||
int acpi_suspend_lowlevel(void)
|
||||
{
|
||||
struct wakeup_header *header;
|
||||
/* address in low memory of the wakeup routine. */
|
||||
char *acpi_realmode;
|
||||
struct wakeup_header *header =
|
||||
(struct wakeup_header *) __va(real_mode_header.wakeup_header);
|
||||
|
||||
acpi_realmode = TRAMPOLINE_SYM(acpi_wakeup_code);
|
||||
|
||||
header = (struct wakeup_header *)(acpi_realmode + WAKEUP_HEADER_OFFSET);
|
||||
if (header->signature != WAKEUP_HEADER_SIGNATURE) {
|
||||
printk(KERN_ERR "wakeup header does not match\n");
|
||||
return -EINVAL;
|
||||
@@ -50,27 +47,6 @@ int acpi_suspend_lowlevel(void)
|
||||
|
||||
header->video_mode = saved_video_mode;
|
||||
|
||||
header->wakeup_jmp_seg = acpi_wakeup_address >> 4;
|
||||
|
||||
/*
|
||||
* Set up the wakeup GDT. We set these up as Big Real Mode,
|
||||
* that is, with limits set to 4 GB. At least the Lenovo
|
||||
* Thinkpad X61 is known to need this for the video BIOS
|
||||
* initialization quirk to work; this is likely to also
|
||||
* be the case for other laptops or integrated video devices.
|
||||
*/
|
||||
|
||||
/* GDT[0]: GDT self-pointer */
|
||||
header->wakeup_gdt[0] =
|
||||
(u64)(sizeof(header->wakeup_gdt) - 1) +
|
||||
((u64)__pa(&header->wakeup_gdt) << 16);
|
||||
/* GDT[1]: big real mode-like code segment */
|
||||
header->wakeup_gdt[1] =
|
||||
GDT_ENTRY(0x809b, acpi_wakeup_address, 0xfffff);
|
||||
/* GDT[2]: big real mode-like data segment */
|
||||
header->wakeup_gdt[2] =
|
||||
GDT_ENTRY(0x8093, acpi_wakeup_address, 0xfffff);
|
||||
|
||||
#ifndef CONFIG_64BIT
|
||||
store_gdt((struct desc_ptr *)&header->pmode_gdt);
|
||||
|
||||
@@ -95,7 +71,6 @@ int acpi_suspend_lowlevel(void)
|
||||
header->pmode_cr3 = (u32)__pa(&initial_page_table);
|
||||
saved_magic = 0x12345678;
|
||||
#else /* CONFIG_64BIT */
|
||||
header->trampoline_segment = trampoline_address() >> 4;
|
||||
#ifdef CONFIG_SMP
|
||||
stack_start = (unsigned long)temp_stack + sizeof(temp_stack);
|
||||
early_gdt_descr.address =
|
||||
|
@@ -2,8 +2,8 @@
|
||||
* Variables and functions used by the code in sleep.c
|
||||
*/
|
||||
|
||||
#include <asm/trampoline.h>
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/realmode.h>
|
||||
|
||||
extern unsigned long saved_video_mode;
|
||||
extern long saved_magic;
|
||||
|
@@ -1,12 +0,0 @@
|
||||
/*
|
||||
* Wrapper script for the realmode binary as a transport object
|
||||
* before copying to low memory.
|
||||
*/
|
||||
#include <asm/page_types.h>
|
||||
|
||||
.section ".x86_trampoline","a"
|
||||
.balign PAGE_SIZE
|
||||
.globl acpi_wakeup_code
|
||||
acpi_wakeup_code:
|
||||
.incbin "arch/x86/kernel/acpi/realmode/wakeup.bin"
|
||||
.size acpi_wakeup_code, .-acpi_wakeup_code
|
Reference in New Issue
Block a user