a4831e08b7
o This patch moves the code to verify long mode and SSE to a common file. This code is now shared by trampoline.S, wakeup.S, boot/setup.S and boot/compressed/head.S o So far we used to do very limited check in trampoline.S, wakeup.S and in 32bit entry point. Now all the entry paths are forced to do the exhaustive check, including SSE because verify_cpu is shared. o I am keeping this patch as last in the x86 relocatable series because previous patches have got quite some amount of testing done and don't want to distrub that. So that if there is problem introduced by this patch, at least it can be easily isolated. Signed-off-by: Eric W. Biederman <ebiederm@xmission.com> Signed-off-by: Vivek Goyal <vgoyal@in.ibm.com> Signed-off-by: Andi Kleen <ak@suse.de>
456 lines
10 KiB
ArmAsm
456 lines
10 KiB
ArmAsm
.text
|
|
#include <linux/linkage.h>
|
|
#include <asm/segment.h>
|
|
#include <asm/pgtable.h>
|
|
#include <asm/page.h>
|
|
#include <asm/msr.h>
|
|
|
|
# Copyright 2003 Pavel Machek <pavel@suse.cz>, distribute under GPLv2
|
|
#
|
|
# 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
|
|
#
|
|
|
|
|
|
ALIGN
|
|
.align 16
|
|
ENTRY(wakeup_start)
|
|
wakeup_code:
|
|
wakeup_code_start = .
|
|
.code16
|
|
|
|
# Running in *copy* of this code, somewhere in low 1MB.
|
|
|
|
movb $0xa1, %al ; outb %al, $0x80
|
|
cli
|
|
cld
|
|
# setup data segment
|
|
movw %cs, %ax
|
|
movw %ax, %ds # Make ds:0 point to wakeup_start
|
|
movw %ax, %ss
|
|
# Private stack is needed for ASUS board
|
|
mov $(wakeup_stack - wakeup_code), %sp
|
|
|
|
pushl $0 # Kill any dangerous flags
|
|
popfl
|
|
|
|
movl real_magic - wakeup_code, %eax
|
|
cmpl $0x12345678, %eax
|
|
jne bogus_real_magic
|
|
|
|
call verify_cpu # Verify the cpu supports long
|
|
# mode
|
|
testl %eax, %eax
|
|
jnz no_longmode
|
|
|
|
testl $1, video_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, video_flags - wakeup_code
|
|
jz 1f
|
|
mov video_mode - wakeup_code, %ax
|
|
call mode_seta
|
|
1:
|
|
|
|
movw $0xb800, %ax
|
|
movw %ax,%fs
|
|
movw $0x0e00 + 'L', %fs:(0x10)
|
|
|
|
movb $0xa2, %al ; outb %al, $0x80
|
|
|
|
mov %ds, %ax # Find 32bit wakeup_code addr
|
|
movzx %ax, %esi # (Convert %ds:gdt to a liner ptr)
|
|
shll $4, %esi
|
|
# Fix up the vectors
|
|
addl %esi, wakeup_32_vector - wakeup_code
|
|
addl %esi, wakeup_long64_vector - wakeup_code
|
|
addl %esi, gdt_48a + 2 - wakeup_code # Fixup the gdt pointer
|
|
|
|
lidtl %ds:idt_48a - wakeup_code
|
|
lgdtl %ds:gdt_48a - wakeup_code # load gdt with whatever is
|
|
# appropriate
|
|
|
|
movl $1, %eax # protected mode (PE) bit
|
|
lmsw %ax # This is it!
|
|
jmp 1f
|
|
1:
|
|
|
|
ljmpl *(wakeup_32_vector - wakeup_code)
|
|
|
|
.balign 4
|
|
wakeup_32_vector:
|
|
.long wakeup_32 - wakeup_code
|
|
.word __KERNEL32_CS, 0
|
|
|
|
.code32
|
|
wakeup_32:
|
|
# Running in this code, but at low address; paging is not yet turned on.
|
|
movb $0xa5, %al ; outb %al, $0x80
|
|
|
|
movl $__KERNEL_DS, %eax
|
|
movl %eax, %ds
|
|
|
|
movw $0x0e00 + 'i', %ds:(0xb8012)
|
|
movb $0xa8, %al ; outb %al, $0x80;
|
|
|
|
/*
|
|
* Prepare for entering 64bits mode
|
|
*/
|
|
|
|
/* Enable PAE */
|
|
xorl %eax, %eax
|
|
btsl $5, %eax
|
|
movl %eax, %cr4
|
|
|
|
/* Setup early boot stage 4 level pagetables */
|
|
leal (wakeup_level4_pgt - wakeup_code)(%esi), %eax
|
|
movl %eax, %cr3
|
|
|
|
/* Check if nx is implemented */
|
|
movl $0x80000001, %eax
|
|
cpuid
|
|
movl %edx,%edi
|
|
|
|
/* Enable Long Mode */
|
|
xorl %eax, %eax
|
|
btsl $_EFER_LME, %eax
|
|
|
|
/* No Execute supported? */
|
|
btl $20,%edi
|
|
jnc 1f
|
|
btsl $_EFER_NX, %eax
|
|
|
|
/* Make changes effective */
|
|
1: movl $MSR_EFER, %ecx
|
|
xorl %edx, %edx
|
|
wrmsr
|
|
|
|
xorl %eax, %eax
|
|
btsl $31, %eax /* Enable paging and in turn activate Long Mode */
|
|
btsl $0, %eax /* Enable protected mode */
|
|
|
|
/* Make changes effective */
|
|
movl %eax, %cr0
|
|
|
|
/* At this point:
|
|
CR4.PAE must be 1
|
|
CS.L must be 0
|
|
CR3 must point to PML4
|
|
Next instruction must be a branch
|
|
This must be on identity-mapped page
|
|
*/
|
|
/*
|
|
* At this point we're in long mode but in 32bit compatibility mode
|
|
* with EFER.LME = 1, CS.L = 0, CS.D = 1 (and in turn
|
|
* EFER.LMA = 1). Now we want to jump in 64bit mode, to do that we load
|
|
* the new gdt/idt that has __KERNEL_CS with CS.L = 1.
|
|
*/
|
|
|
|
/* Finally jump in 64bit mode */
|
|
ljmp *(wakeup_long64_vector - wakeup_code)(%esi)
|
|
|
|
.balign 4
|
|
wakeup_long64_vector:
|
|
.long wakeup_long64 - wakeup_code
|
|
.word __KERNEL_CS, 0
|
|
|
|
.code64
|
|
|
|
/* Hooray, we are in Long 64-bit mode (but still running in
|
|
* low memory)
|
|
*/
|
|
wakeup_long64:
|
|
/*
|
|
* We must switch to a new descriptor in kernel space for the GDT
|
|
* because soon the kernel won't have access anymore to the userspace
|
|
* addresses where we're currently running on. We have to do that here
|
|
* because in 32bit we couldn't load a 64bit linear address.
|
|
*/
|
|
lgdt cpu_gdt_descr
|
|
|
|
movw $0x0e00 + 'n', %ds:(0xb8014)
|
|
movb $0xa9, %al ; outb %al, $0x80
|
|
|
|
movq saved_magic, %rax
|
|
movq $0x123456789abcdef0, %rdx
|
|
cmpq %rdx, %rax
|
|
jne bogus_64_magic
|
|
|
|
movw $0x0e00 + 'u', %ds:(0xb8016)
|
|
|
|
nop
|
|
nop
|
|
movw $__KERNEL_DS, %ax
|
|
movw %ax, %ss
|
|
movw %ax, %ds
|
|
movw %ax, %es
|
|
movw %ax, %fs
|
|
movw %ax, %gs
|
|
movq saved_rsp, %rsp
|
|
|
|
movw $0x0e00 + 'x', %ds:(0xb8018)
|
|
movq saved_rbx, %rbx
|
|
movq saved_rdi, %rdi
|
|
movq saved_rsi, %rsi
|
|
movq saved_rbp, %rbp
|
|
|
|
movw $0x0e00 + '!', %ds:(0xb801a)
|
|
movq saved_rip, %rax
|
|
jmp *%rax
|
|
|
|
.code32
|
|
|
|
.align 64
|
|
gdta:
|
|
/* Its good to keep gdt in sync with one in trampoline.S */
|
|
.word 0, 0, 0, 0 # dummy
|
|
/* ??? Why I need the accessed bit set in order for this to work? */
|
|
.quad 0x00cf9b000000ffff # __KERNEL32_CS
|
|
.quad 0x00af9b000000ffff # __KERNEL_CS
|
|
.quad 0x00cf93000000ffff # __KERNEL_DS
|
|
|
|
idt_48a:
|
|
.word 0 # idt limit = 0
|
|
.word 0, 0 # idt base = 0L
|
|
|
|
gdt_48a:
|
|
.word 0x800 # gdt limit=2048,
|
|
# 256 GDT entries
|
|
.long gdta - wakeup_code # gdt base (relocated in later)
|
|
|
|
real_magic: .quad 0
|
|
video_mode: .quad 0
|
|
video_flags: .quad 0
|
|
|
|
.code16
|
|
bogus_real_magic:
|
|
movb $0xba,%al ; outb %al,$0x80
|
|
jmp bogus_real_magic
|
|
|
|
.code64
|
|
bogus_64_magic:
|
|
movb $0xb3,%al ; outb %al,$0x80
|
|
jmp bogus_64_magic
|
|
|
|
.code16
|
|
no_longmode:
|
|
movb $0xbc,%al ; outb %al,$0x80
|
|
jmp no_longmode
|
|
|
|
#include "../verify_cpu.S"
|
|
|
|
/* 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
|
|
.code16
|
|
mode_seta:
|
|
movw %ax, %bx
|
|
#if 0
|
|
cmpb $0xff, %ah
|
|
jz setalias
|
|
|
|
testb $VIDEO_RECALC>>8, %ah
|
|
jnz _setrec
|
|
|
|
cmpb $VIDEO_FIRST_RESOLUTION>>8, %ah
|
|
jnc setres
|
|
|
|
cmpb $VIDEO_FIRST_SPECIAL>>8, %ah
|
|
jz setspc
|
|
|
|
cmpb $VIDEO_FIRST_V7>>8, %ah
|
|
jz setv7
|
|
#endif
|
|
|
|
cmpb $VIDEO_FIRST_VESA>>8, %ah
|
|
jnc check_vesaa
|
|
#if 0
|
|
orb %ah, %ah
|
|
jz setmenu
|
|
#endif
|
|
|
|
decb %ah
|
|
# jz setbios Add bios modes later
|
|
|
|
setbada: clc
|
|
ret
|
|
|
|
check_vesaa:
|
|
subb $VIDEO_FIRST_VESA>>8, %bh
|
|
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 _setbada # AH=0 if OK
|
|
|
|
stc
|
|
ret
|
|
|
|
_setbada: jmp setbada
|
|
|
|
wakeup_stack_begin: # Stack grows down
|
|
|
|
.org 0xff0
|
|
wakeup_stack: # Just below end of page
|
|
|
|
.org 0x1000
|
|
ENTRY(wakeup_level4_pgt)
|
|
.quad level3_ident_pgt - __START_KERNEL_map + _KERNPG_TABLE
|
|
.fill 510,8,0
|
|
/* (2^48-(2*1024*1024*1024))/(2^39) = 511 */
|
|
.quad level3_kernel_pgt - __START_KERNEL_map + _KERNPG_TABLE
|
|
|
|
ENTRY(wakeup_end)
|
|
|
|
##
|
|
# acpi_copy_wakeup_routine
|
|
#
|
|
# Copy the above routine to low memory.
|
|
#
|
|
# Parameters:
|
|
# %rdi: place to copy wakeup routine to
|
|
#
|
|
# Returned address is location of code in low memory (past data and stack)
|
|
#
|
|
.code64
|
|
ENTRY(acpi_copy_wakeup_routine)
|
|
pushq %rax
|
|
pushq %rdx
|
|
|
|
movl saved_video_mode, %edx
|
|
movl %edx, video_mode - wakeup_start (,%rdi)
|
|
movl acpi_video_flags, %edx
|
|
movl %edx, video_flags - wakeup_start (,%rdi)
|
|
movq $0x12345678, real_magic - wakeup_start (,%rdi)
|
|
movq $0x123456789abcdef0, %rdx
|
|
movq %rdx, saved_magic
|
|
|
|
movq saved_magic, %rax
|
|
movq $0x123456789abcdef0, %rdx
|
|
cmpq %rdx, %rax
|
|
jne bogus_64_magic
|
|
|
|
# restore the regs we used
|
|
popq %rdx
|
|
popq %rax
|
|
ENTRY(do_suspend_lowlevel_s4bios)
|
|
ret
|
|
|
|
.align 2
|
|
.p2align 4,,15
|
|
.globl do_suspend_lowlevel
|
|
.type do_suspend_lowlevel,@function
|
|
do_suspend_lowlevel:
|
|
.LFB5:
|
|
subq $8, %rsp
|
|
xorl %eax, %eax
|
|
call save_processor_state
|
|
|
|
movq %rsp, saved_context_esp(%rip)
|
|
movq %rax, saved_context_eax(%rip)
|
|
movq %rbx, saved_context_ebx(%rip)
|
|
movq %rcx, saved_context_ecx(%rip)
|
|
movq %rdx, saved_context_edx(%rip)
|
|
movq %rbp, saved_context_ebp(%rip)
|
|
movq %rsi, saved_context_esi(%rip)
|
|
movq %rdi, saved_context_edi(%rip)
|
|
movq %r8, saved_context_r08(%rip)
|
|
movq %r9, saved_context_r09(%rip)
|
|
movq %r10, saved_context_r10(%rip)
|
|
movq %r11, saved_context_r11(%rip)
|
|
movq %r12, saved_context_r12(%rip)
|
|
movq %r13, saved_context_r13(%rip)
|
|
movq %r14, saved_context_r14(%rip)
|
|
movq %r15, saved_context_r15(%rip)
|
|
pushfq ; popq saved_context_eflags(%rip)
|
|
|
|
movq $.L97, saved_rip(%rip)
|
|
|
|
movq %rsp,saved_rsp
|
|
movq %rbp,saved_rbp
|
|
movq %rbx,saved_rbx
|
|
movq %rdi,saved_rdi
|
|
movq %rsi,saved_rsi
|
|
|
|
addq $8, %rsp
|
|
movl $3, %edi
|
|
xorl %eax, %eax
|
|
jmp acpi_enter_sleep_state
|
|
.L97:
|
|
.p2align 4,,7
|
|
.L99:
|
|
.align 4
|
|
movl $24, %eax
|
|
movw %ax, %ds
|
|
movq saved_context+58(%rip), %rax
|
|
movq %rax, %cr4
|
|
movq saved_context+50(%rip), %rax
|
|
movq %rax, %cr3
|
|
movq saved_context+42(%rip), %rax
|
|
movq %rax, %cr2
|
|
movq saved_context+34(%rip), %rax
|
|
movq %rax, %cr0
|
|
pushq saved_context_eflags(%rip) ; popfq
|
|
movq saved_context_esp(%rip), %rsp
|
|
movq saved_context_ebp(%rip), %rbp
|
|
movq saved_context_eax(%rip), %rax
|
|
movq saved_context_ebx(%rip), %rbx
|
|
movq saved_context_ecx(%rip), %rcx
|
|
movq saved_context_edx(%rip), %rdx
|
|
movq saved_context_esi(%rip), %rsi
|
|
movq saved_context_edi(%rip), %rdi
|
|
movq saved_context_r08(%rip), %r8
|
|
movq saved_context_r09(%rip), %r9
|
|
movq saved_context_r10(%rip), %r10
|
|
movq saved_context_r11(%rip), %r11
|
|
movq saved_context_r12(%rip), %r12
|
|
movq saved_context_r13(%rip), %r13
|
|
movq saved_context_r14(%rip), %r14
|
|
movq saved_context_r15(%rip), %r15
|
|
|
|
xorl %eax, %eax
|
|
addq $8, %rsp
|
|
jmp restore_processor_state
|
|
.LFE5:
|
|
.Lfe5:
|
|
.size do_suspend_lowlevel,.Lfe5-do_suspend_lowlevel
|
|
|
|
.data
|
|
ALIGN
|
|
ENTRY(saved_rbp) .quad 0
|
|
ENTRY(saved_rsi) .quad 0
|
|
ENTRY(saved_rdi) .quad 0
|
|
ENTRY(saved_rbx) .quad 0
|
|
|
|
ENTRY(saved_rip) .quad 0
|
|
ENTRY(saved_rsp) .quad 0
|
|
|
|
ENTRY(saved_magic) .quad 0
|