.text #include #include #include # # 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 .data ALIGN ENTRY(saved_magic) .long 0 ENTRY(saved_eip) .long 0 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 ALIGN # saved registers saved_gdt: .long 0,0 saved_idt: .long 0,0 saved_ldt: .long 0 saved_tss: .long 0