| .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 |
| # |
| |
| 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 |
| 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, 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_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 |
| |
| 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 |
| video_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 |
| mode_set: |
| 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_vesa |
| #if 0 |
| orb %ah, %ah |
| jz setmenu |
| #endif |
| |
| decb %ah |
| # jz setbios Add bios modes later |
| |
| setbad: clc |
| ret |
| |
| check_vesa: |
| 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 _setbad # AH=0 if OK |
| |
| stc |
| ret |
| |
| _setbad: jmp setbad |
| |
| .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) |
| |
| 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_video_flags, %edx |
| movl %edx, video_flags - wakeup_start (%eax) |
| movl $0x12345678, real_magic - wakeup_start (%eax) |
| movl $0x12345678, saved_magic |
| 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 |
| ret |
| .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 |
| |