1.text 2#include <linux/linkage.h> 3#include <asm/segment.h> 4#include <asm/pgtable.h> 5#include <asm/page.h> 6#include <asm/msr.h> 7#include <asm/asm-offsets.h> 8 9# Copyright 2003 Pavel Machek <pavel@suse.cz>, distribute under GPLv2 10# 11# wakeup_code runs in real mode, and at unknown address (determined at run-time). 12# Therefore it must only use relative jumps/calls. 13# 14# Do we need to deal with A20? It is okay: ACPI specs says A20 must be enabled 15# 16# If physical address of wakeup_code is 0x12345, BIOS should call us with 17# cs = 0x1234, eip = 0x05 18# 19 20#define BEEP \ 21 inb $97, %al; \ 22 outb %al, $0x80; \ 23 movb $3, %al; \ 24 outb %al, $97; \ 25 outb %al, $0x80; \ 26 movb $-74, %al; \ 27 outb %al, $67; \ 28 outb %al, $0x80; \ 29 movb $-119, %al; \ 30 outb %al, $66; \ 31 outb %al, $0x80; \ 32 movb $15, %al; \ 33 outb %al, $66; 34 35 36ALIGN 37 .align 16 38ENTRY(wakeup_start) 39wakeup_code: 40 wakeup_code_start = . 41 .code16 42 43# Running in *copy* of this code, somewhere in low 1MB. 44 45 cli 46 cld 47 # setup data segment 48 movw %cs, %ax 49 movw %ax, %ds # Make ds:0 point to wakeup_start 50 movw %ax, %ss 51 52 # Data segment must be set up before we can see whether to beep. 53 testl $4, realmode_flags - wakeup_code 54 jz 1f 55 BEEP 561: 57 58 # Private stack is needed for ASUS board 59 mov $(wakeup_stack - wakeup_code), %sp 60 61 pushl $0 # Kill any dangerous flags 62 popfl 63 64 movl real_magic - wakeup_code, %eax 65 cmpl $0x12345678, %eax 66 jne bogus_real_magic 67 68 testl $1, realmode_flags - wakeup_code 69 jz 1f 70 lcall $0xc000,$3 71 movw %cs, %ax 72 movw %ax, %ds # Bios might have played with that 73 movw %ax, %ss 741: 75 76 testl $2, realmode_flags - wakeup_code 77 jz 1f 78 mov video_mode - wakeup_code, %ax 79 call mode_set 801: 81 82 mov %ds, %ax # Find 32bit wakeup_code addr 83 movzx %ax, %esi # (Convert %ds:gdt to a liner ptr) 84 shll $4, %esi 85 # Fix up the vectors 86 addl %esi, wakeup_32_vector - wakeup_code 87 addl %esi, wakeup_long64_vector - wakeup_code 88 addl %esi, gdt_48a + 2 - wakeup_code # Fixup the gdt pointer 89 90 lidtl %ds:idt_48a - wakeup_code 91 lgdtl %ds:gdt_48a - wakeup_code # load gdt with whatever is 92 # appropriate 93 94 movl $1, %eax # protected mode (PE) bit 95 lmsw %ax # This is it! 96 jmp 1f 971: 98 99 ljmpl *(wakeup_32_vector - wakeup_code) 100 101 .balign 4 102wakeup_32_vector: 103 .long wakeup_32 - wakeup_code 104 .word __KERNEL32_CS, 0 105 106 .code32 107wakeup_32: 108# Running in this code, but at low address; paging is not yet turned on. 109 110 movl $__KERNEL_DS, %eax 111 movl %eax, %ds 112 113 /* 114 * Prepare for entering 64bits mode 115 */ 116 117 /* Enable PAE */ 118 xorl %eax, %eax 119 btsl $5, %eax 120 movl %eax, %cr4 121 122 /* Setup early boot stage 4 level pagetables */ 123 leal (wakeup_level4_pgt - wakeup_code)(%esi), %eax 124 movl %eax, %cr3 125 126 /* Check if nx is implemented */ 127 movl $0x80000001, %eax 128 cpuid 129 movl %edx,%edi 130 131 /* Enable Long Mode */ 132 xorl %eax, %eax 133 btsl $_EFER_LME, %eax 134 135 /* No Execute supported? */ 136 btl $20,%edi 137 jnc 1f 138 btsl $_EFER_NX, %eax 139 140 /* Make changes effective */ 1411: movl $MSR_EFER, %ecx 142 xorl %edx, %edx 143 wrmsr 144 145 xorl %eax, %eax 146 btsl $31, %eax /* Enable paging and in turn activate Long Mode */ 147 btsl $0, %eax /* Enable protected mode */ 148 149 /* Make changes effective */ 150 movl %eax, %cr0 151 152 /* At this point: 153 CR4.PAE must be 1 154 CS.L must be 0 155 CR3 must point to PML4 156 Next instruction must be a branch 157 This must be on identity-mapped page 158 */ 159 /* 160 * At this point we're in long mode but in 32bit compatibility mode 161 * with EFER.LME = 1, CS.L = 0, CS.D = 1 (and in turn 162 * EFER.LMA = 1). Now we want to jump in 64bit mode, to do that we load 163 * the new gdt/idt that has __KERNEL_CS with CS.L = 1. 164 */ 165 166 /* Finally jump in 64bit mode */ 167 ljmp *(wakeup_long64_vector - wakeup_code)(%esi) 168 169 .balign 4 170wakeup_long64_vector: 171 .long wakeup_long64 - wakeup_code 172 .word __KERNEL_CS, 0 173 174.code64 175 176 /* Hooray, we are in Long 64-bit mode (but still running in 177 * low memory) 178 */ 179wakeup_long64: 180 /* 181 * We must switch to a new descriptor in kernel space for the GDT 182 * because soon the kernel won't have access anymore to the userspace 183 * addresses where we're currently running on. We have to do that here 184 * because in 32bit we couldn't load a 64bit linear address. 185 */ 186 lgdt cpu_gdt_descr 187 188 movq saved_magic, %rax 189 movq $0x123456789abcdef0, %rdx 190 cmpq %rdx, %rax 191 jne bogus_64_magic 192 193 nop 194 nop 195 movw $__KERNEL_DS, %ax 196 movw %ax, %ss 197 movw %ax, %ds 198 movw %ax, %es 199 movw %ax, %fs 200 movw %ax, %gs 201 movq saved_rsp, %rsp 202 203 movq saved_rbx, %rbx 204 movq saved_rdi, %rdi 205 movq saved_rsi, %rsi 206 movq saved_rbp, %rbp 207 208 movq saved_rip, %rax 209 jmp *%rax 210 211.code32 212 213 .align 64 214gdta: 215 /* Its good to keep gdt in sync with one in trampoline.S */ 216 .word 0, 0, 0, 0 # dummy 217 /* ??? Why I need the accessed bit set in order for this to work? */ 218 .quad 0x00cf9b000000ffff # __KERNEL32_CS 219 .quad 0x00af9b000000ffff # __KERNEL_CS 220 .quad 0x00cf93000000ffff # __KERNEL_DS 221 222idt_48a: 223 .word 0 # idt limit = 0 224 .word 0, 0 # idt base = 0L 225 226gdt_48a: 227 .word 0x800 # gdt limit=2048, 228 # 256 GDT entries 229 .long gdta - wakeup_code # gdt base (relocated in later) 230 231real_magic: .quad 0 232video_mode: .quad 0 233realmode_flags: .quad 0 234 235.code16 236bogus_real_magic: 237 jmp bogus_real_magic 238 239.code64 240bogus_64_magic: 241 jmp bogus_64_magic 242 243/* This code uses an extended set of video mode numbers. These include: 244 * Aliases for standard modes 245 * NORMAL_VGA (-1) 246 * EXTENDED_VGA (-2) 247 * ASK_VGA (-3) 248 * Video modes numbered by menu position -- NOT RECOMMENDED because of lack 249 * of compatibility when extending the table. These are between 0x00 and 0xff. 250 */ 251#define VIDEO_FIRST_MENU 0x0000 252 253/* Standard BIOS video modes (BIOS number + 0x0100) */ 254#define VIDEO_FIRST_BIOS 0x0100 255 256/* VESA BIOS video modes (VESA number + 0x0200) */ 257#define VIDEO_FIRST_VESA 0x0200 258 259/* Video7 special modes (BIOS number + 0x0900) */ 260#define VIDEO_FIRST_V7 0x0900 261 262# Setting of user mode (AX=mode ID) => CF=success 263 264# For now, we only handle VESA modes (0x0200..0x03ff). To handle other 265# modes, we should probably compile in the video code from the boot 266# directory. 267.code16 268mode_set: 269 movw %ax, %bx 270 subb $VIDEO_FIRST_VESA>>8, %bh 271 cmpb $2, %bh 272 jb check_vesa 273 274setbad: 275 clc 276 ret 277 278check_vesa: 279 orw $0x4000, %bx # Use linear frame buffer 280 movw $0x4f02, %ax # VESA BIOS mode set call 281 int $0x10 282 cmpw $0x004f, %ax # AL=4f if implemented 283 jnz setbad # AH=0 if OK 284 285 stc 286 ret 287 288wakeup_stack_begin: # Stack grows down 289 290.org 0xff0 291wakeup_stack: # Just below end of page 292 293.org 0x1000 294ENTRY(wakeup_level4_pgt) 295 .quad level3_ident_pgt - __START_KERNEL_map + _KERNPG_TABLE 296 .fill 510,8,0 297 /* (2^48-(2*1024*1024*1024))/(2^39) = 511 */ 298 .quad level3_kernel_pgt - __START_KERNEL_map + _KERNPG_TABLE 299 300ENTRY(wakeup_end) 301 302## 303# acpi_copy_wakeup_routine 304# 305# Copy the above routine to low memory. 306# 307# Parameters: 308# %rdi: place to copy wakeup routine to 309# 310# Returned address is location of code in low memory (past data and stack) 311# 312 .code64 313ENTRY(acpi_copy_wakeup_routine) 314 pushq %rax 315 pushq %rdx 316 317 movl saved_video_mode, %edx 318 movl %edx, video_mode - wakeup_start (,%rdi) 319 movl acpi_realmode_flags, %edx 320 movl %edx, realmode_flags - wakeup_start (,%rdi) 321 movq $0x12345678, real_magic - wakeup_start (,%rdi) 322 movq $0x123456789abcdef0, %rdx 323 movq %rdx, saved_magic 324 325 movq saved_magic, %rax 326 movq $0x123456789abcdef0, %rdx 327 cmpq %rdx, %rax 328 jne bogus_64_magic 329 330 # restore the regs we used 331 popq %rdx 332 popq %rax 333ENTRY(do_suspend_lowlevel_s4bios) 334 ret 335 336 .align 2 337 .p2align 4,,15 338.globl do_suspend_lowlevel 339 .type do_suspend_lowlevel,@function 340do_suspend_lowlevel: 341.LFB5: 342 subq $8, %rsp 343 xorl %eax, %eax 344 call save_processor_state 345 346 movq $saved_context, %rax 347 movq %rsp, pt_regs_sp(%rax) 348 movq %rbp, pt_regs_bp(%rax) 349 movq %rsi, pt_regs_si(%rax) 350 movq %rdi, pt_regs_di(%rax) 351 movq %rbx, pt_regs_bx(%rax) 352 movq %rcx, pt_regs_cx(%rax) 353 movq %rdx, pt_regs_dx(%rax) 354 movq %r8, pt_regs_r8(%rax) 355 movq %r9, pt_regs_r9(%rax) 356 movq %r10, pt_regs_r10(%rax) 357 movq %r11, pt_regs_r11(%rax) 358 movq %r12, pt_regs_r12(%rax) 359 movq %r13, pt_regs_r13(%rax) 360 movq %r14, pt_regs_r14(%rax) 361 movq %r15, pt_regs_r15(%rax) 362 pushfq 363 popq pt_regs_flags(%rax) 364 365 movq $.L97, saved_rip(%rip) 366 367 movq %rsp, saved_rsp 368 movq %rbp, saved_rbp 369 movq %rbx, saved_rbx 370 movq %rdi, saved_rdi 371 movq %rsi, saved_rsi 372 373 addq $8, %rsp 374 movl $3, %edi 375 xorl %eax, %eax 376 jmp acpi_enter_sleep_state 377.L97: 378 .p2align 4,,7 379.L99: 380 .align 4 381 movl $24, %eax 382 movw %ax, %ds 383 384 /* We don't restore %rax, it must be 0 anyway */ 385 movq $saved_context, %rax 386 movq saved_context_cr4(%rax), %rbx 387 movq %rbx, %cr4 388 movq saved_context_cr3(%rax), %rbx 389 movq %rbx, %cr3 390 movq saved_context_cr2(%rax), %rbx 391 movq %rbx, %cr2 392 movq saved_context_cr0(%rax), %rbx 393 movq %rbx, %cr0 394 pushq pt_regs_flags(%rax) 395 popfq 396 movq pt_regs_sp(%rax), %rsp 397 movq pt_regs_bp(%rax), %rbp 398 movq pt_regs_si(%rax), %rsi 399 movq pt_regs_di(%rax), %rdi 400 movq pt_regs_bx(%rax), %rbx 401 movq pt_regs_cx(%rax), %rcx 402 movq pt_regs_dx(%rax), %rdx 403 movq pt_regs_r8(%rax), %r8 404 movq pt_regs_r9(%rax), %r9 405 movq pt_regs_r10(%rax), %r10 406 movq pt_regs_r11(%rax), %r11 407 movq pt_regs_r12(%rax), %r12 408 movq pt_regs_r13(%rax), %r13 409 movq pt_regs_r14(%rax), %r14 410 movq pt_regs_r15(%rax), %r15 411 412 xorl %eax, %eax 413 addq $8, %rsp 414 jmp restore_processor_state 415.LFE5: 416.Lfe5: 417 .size do_suspend_lowlevel,.Lfe5-do_suspend_lowlevel 418 419.data 420ALIGN 421ENTRY(saved_rbp) .quad 0 422ENTRY(saved_rsi) .quad 0 423ENTRY(saved_rdi) .quad 0 424ENTRY(saved_rbx) .quad 0 425 426ENTRY(saved_rip) .quad 0 427ENTRY(saved_rsp) .quad 0 428 429ENTRY(saved_magic) .quad 0 430