Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame^] | 1 | .text |
| 2 | #include <linux/linkage.h> |
| 3 | #include <asm/segment.h> |
| 4 | #include <asm/page.h> |
| 5 | |
| 6 | # |
| 7 | # wakeup_code runs in real mode, and at unknown address (determined at run-time). |
| 8 | # Therefore it must only use relative jumps/calls. |
| 9 | # |
| 10 | # Do we need to deal with A20? It is okay: ACPI specs says A20 must be enabled |
| 11 | # |
| 12 | # If physical address of wakeup_code is 0x12345, BIOS should call us with |
| 13 | # cs = 0x1234, eip = 0x05 |
| 14 | # |
| 15 | |
| 16 | ALIGN |
| 17 | .align 4096 |
| 18 | ENTRY(wakeup_start) |
| 19 | wakeup_code: |
| 20 | wakeup_code_start = . |
| 21 | .code16 |
| 22 | |
| 23 | movw $0xb800, %ax |
| 24 | movw %ax,%fs |
| 25 | movw $0x0e00 + 'L', %fs:(0x10) |
| 26 | |
| 27 | cli |
| 28 | cld |
| 29 | |
| 30 | # setup data segment |
| 31 | movw %cs, %ax |
| 32 | movw %ax, %ds # Make ds:0 point to wakeup_start |
| 33 | movw %ax, %ss |
| 34 | mov $(wakeup_stack - wakeup_code), %sp # Private stack is needed for ASUS board |
| 35 | movw $0x0e00 + 'S', %fs:(0x12) |
| 36 | |
| 37 | pushl $0 # Kill any dangerous flags |
| 38 | popfl |
| 39 | |
| 40 | movl real_magic - wakeup_code, %eax |
| 41 | cmpl $0x12345678, %eax |
| 42 | jne bogus_real_magic |
| 43 | |
| 44 | testl $1, video_flags - wakeup_code |
| 45 | jz 1f |
| 46 | lcall $0xc000,$3 |
| 47 | movw %cs, %ax |
| 48 | movw %ax, %ds # Bios might have played with that |
| 49 | movw %ax, %ss |
| 50 | 1: |
| 51 | |
| 52 | testl $2, video_flags - wakeup_code |
| 53 | jz 1f |
| 54 | mov video_mode - wakeup_code, %ax |
| 55 | call mode_set |
| 56 | 1: |
| 57 | |
| 58 | # set up page table |
| 59 | movl $swapper_pg_dir-__PAGE_OFFSET, %eax |
| 60 | movl %eax, %cr3 |
| 61 | |
| 62 | testl $1, real_efer_save_restore - wakeup_code |
| 63 | jz 4f |
| 64 | # restore efer setting |
| 65 | movl real_save_efer_edx - wakeup_code, %edx |
| 66 | movl real_save_efer_eax - wakeup_code, %eax |
| 67 | mov $0xc0000080, %ecx |
| 68 | wrmsr |
| 69 | 4: |
| 70 | # make sure %cr4 is set correctly (features, etc) |
| 71 | movl real_save_cr4 - wakeup_code, %eax |
| 72 | movl %eax, %cr4 |
| 73 | movw $0xb800, %ax |
| 74 | movw %ax,%fs |
| 75 | movw $0x0e00 + 'i', %fs:(0x12) |
| 76 | |
| 77 | # need a gdt |
| 78 | lgdt real_save_gdt - wakeup_code |
| 79 | |
| 80 | movl real_save_cr0 - wakeup_code, %eax |
| 81 | movl %eax, %cr0 |
| 82 | jmp 1f |
| 83 | 1: |
| 84 | movw $0x0e00 + 'n', %fs:(0x14) |
| 85 | |
| 86 | movl real_magic - wakeup_code, %eax |
| 87 | cmpl $0x12345678, %eax |
| 88 | jne bogus_real_magic |
| 89 | |
| 90 | ljmpl $__KERNEL_CS,$wakeup_pmode_return |
| 91 | |
| 92 | real_save_gdt: .word 0 |
| 93 | .long 0 |
| 94 | real_save_cr0: .long 0 |
| 95 | real_save_cr3: .long 0 |
| 96 | real_save_cr4: .long 0 |
| 97 | real_magic: .long 0 |
| 98 | video_mode: .long 0 |
| 99 | video_flags: .long 0 |
| 100 | real_efer_save_restore: .long 0 |
| 101 | real_save_efer_edx: .long 0 |
| 102 | real_save_efer_eax: .long 0 |
| 103 | |
| 104 | bogus_real_magic: |
| 105 | movw $0x0e00 + 'B', %fs:(0x12) |
| 106 | jmp bogus_real_magic |
| 107 | |
| 108 | /* This code uses an extended set of video mode numbers. These include: |
| 109 | * Aliases for standard modes |
| 110 | * NORMAL_VGA (-1) |
| 111 | * EXTENDED_VGA (-2) |
| 112 | * ASK_VGA (-3) |
| 113 | * Video modes numbered by menu position -- NOT RECOMMENDED because of lack |
| 114 | * of compatibility when extending the table. These are between 0x00 and 0xff. |
| 115 | */ |
| 116 | #define VIDEO_FIRST_MENU 0x0000 |
| 117 | |
| 118 | /* Standard BIOS video modes (BIOS number + 0x0100) */ |
| 119 | #define VIDEO_FIRST_BIOS 0x0100 |
| 120 | |
| 121 | /* VESA BIOS video modes (VESA number + 0x0200) */ |
| 122 | #define VIDEO_FIRST_VESA 0x0200 |
| 123 | |
| 124 | /* Video7 special modes (BIOS number + 0x0900) */ |
| 125 | #define VIDEO_FIRST_V7 0x0900 |
| 126 | |
| 127 | # Setting of user mode (AX=mode ID) => CF=success |
| 128 | mode_set: |
| 129 | movw %ax, %bx |
| 130 | #if 0 |
| 131 | cmpb $0xff, %ah |
| 132 | jz setalias |
| 133 | |
| 134 | testb $VIDEO_RECALC>>8, %ah |
| 135 | jnz _setrec |
| 136 | |
| 137 | cmpb $VIDEO_FIRST_RESOLUTION>>8, %ah |
| 138 | jnc setres |
| 139 | |
| 140 | cmpb $VIDEO_FIRST_SPECIAL>>8, %ah |
| 141 | jz setspc |
| 142 | |
| 143 | cmpb $VIDEO_FIRST_V7>>8, %ah |
| 144 | jz setv7 |
| 145 | #endif |
| 146 | |
| 147 | cmpb $VIDEO_FIRST_VESA>>8, %ah |
| 148 | jnc check_vesa |
| 149 | #if 0 |
| 150 | orb %ah, %ah |
| 151 | jz setmenu |
| 152 | #endif |
| 153 | |
| 154 | decb %ah |
| 155 | # jz setbios Add bios modes later |
| 156 | |
| 157 | setbad: clc |
| 158 | ret |
| 159 | |
| 160 | check_vesa: |
| 161 | subb $VIDEO_FIRST_VESA>>8, %bh |
| 162 | orw $0x4000, %bx # Use linear frame buffer |
| 163 | movw $0x4f02, %ax # VESA BIOS mode set call |
| 164 | int $0x10 |
| 165 | cmpw $0x004f, %ax # AL=4f if implemented |
| 166 | jnz _setbad # AH=0 if OK |
| 167 | |
| 168 | stc |
| 169 | ret |
| 170 | |
| 171 | _setbad: jmp setbad |
| 172 | |
| 173 | .code32 |
| 174 | ALIGN |
| 175 | |
| 176 | .org 0x800 |
| 177 | wakeup_stack_begin: # Stack grows down |
| 178 | |
| 179 | .org 0xff0 # Just below end of page |
| 180 | wakeup_stack: |
| 181 | ENTRY(wakeup_end) |
| 182 | |
| 183 | .org 0x1000 |
| 184 | |
| 185 | wakeup_pmode_return: |
| 186 | movw $__KERNEL_DS, %ax |
| 187 | movw %ax, %ss |
| 188 | movw %ax, %ds |
| 189 | movw %ax, %es |
| 190 | movw %ax, %fs |
| 191 | movw %ax, %gs |
| 192 | movw $0x0e00 + 'u', 0xb8016 |
| 193 | |
| 194 | # reload the gdt, as we need the full 32 bit address |
| 195 | lgdt saved_gdt |
| 196 | lidt saved_idt |
| 197 | lldt saved_ldt |
| 198 | ljmp $(__KERNEL_CS),$1f |
| 199 | 1: |
| 200 | movl %cr3, %eax |
| 201 | movl %eax, %cr3 |
| 202 | wbinvd |
| 203 | |
| 204 | # and restore the stack ... but you need gdt for this to work |
| 205 | movl saved_context_esp, %esp |
| 206 | |
| 207 | movl %cs:saved_magic, %eax |
| 208 | cmpl $0x12345678, %eax |
| 209 | jne bogus_magic |
| 210 | |
| 211 | # jump to place where we left off |
| 212 | movl saved_eip,%eax |
| 213 | jmp *%eax |
| 214 | |
| 215 | bogus_magic: |
| 216 | movw $0x0e00 + 'B', 0xb8018 |
| 217 | jmp bogus_magic |
| 218 | |
| 219 | |
| 220 | ## |
| 221 | # acpi_copy_wakeup_routine |
| 222 | # |
| 223 | # Copy the above routine to low memory. |
| 224 | # |
| 225 | # Parameters: |
| 226 | # %eax: place to copy wakeup routine to |
| 227 | # |
| 228 | # Returned address is location of code in low memory (past data and stack) |
| 229 | # |
| 230 | ENTRY(acpi_copy_wakeup_routine) |
| 231 | |
| 232 | sgdt saved_gdt |
| 233 | sidt saved_idt |
| 234 | sldt saved_ldt |
| 235 | str saved_tss |
| 236 | |
| 237 | movl nx_enabled, %edx |
| 238 | movl %edx, real_efer_save_restore - wakeup_start (%eax) |
| 239 | testl $1, real_efer_save_restore - wakeup_start (%eax) |
| 240 | jz 2f |
| 241 | # save efer setting |
| 242 | pushl %eax |
| 243 | movl %eax, %ebx |
| 244 | mov $0xc0000080, %ecx |
| 245 | rdmsr |
| 246 | movl %edx, real_save_efer_edx - wakeup_start (%ebx) |
| 247 | movl %eax, real_save_efer_eax - wakeup_start (%ebx) |
| 248 | popl %eax |
| 249 | 2: |
| 250 | |
| 251 | movl %cr3, %edx |
| 252 | movl %edx, real_save_cr3 - wakeup_start (%eax) |
| 253 | movl %cr4, %edx |
| 254 | movl %edx, real_save_cr4 - wakeup_start (%eax) |
| 255 | movl %cr0, %edx |
| 256 | movl %edx, real_save_cr0 - wakeup_start (%eax) |
| 257 | sgdt real_save_gdt - wakeup_start (%eax) |
| 258 | |
| 259 | movl saved_videomode, %edx |
| 260 | movl %edx, video_mode - wakeup_start (%eax) |
| 261 | movl acpi_video_flags, %edx |
| 262 | movl %edx, video_flags - wakeup_start (%eax) |
| 263 | movl $0x12345678, real_magic - wakeup_start (%eax) |
| 264 | movl $0x12345678, saved_magic |
| 265 | ret |
| 266 | |
| 267 | .data |
| 268 | ALIGN |
| 269 | ENTRY(saved_magic) .long 0 |
| 270 | ENTRY(saved_eip) .long 0 |
| 271 | |
| 272 | save_registers: |
| 273 | leal 4(%esp), %eax |
| 274 | movl %eax, saved_context_esp |
| 275 | movl %ebx, saved_context_ebx |
| 276 | movl %ebp, saved_context_ebp |
| 277 | movl %esi, saved_context_esi |
| 278 | movl %edi, saved_context_edi |
| 279 | pushfl ; popl saved_context_eflags |
| 280 | |
| 281 | movl $ret_point, saved_eip |
| 282 | ret |
| 283 | |
| 284 | |
| 285 | restore_registers: |
| 286 | movl saved_context_ebp, %ebp |
| 287 | movl saved_context_ebx, %ebx |
| 288 | movl saved_context_esi, %esi |
| 289 | movl saved_context_edi, %edi |
| 290 | pushl saved_context_eflags ; popfl |
| 291 | ret |
| 292 | |
| 293 | ENTRY(do_suspend_lowlevel) |
| 294 | call save_processor_state |
| 295 | call save_registers |
| 296 | pushl $3 |
| 297 | call acpi_enter_sleep_state |
| 298 | addl $4, %esp |
| 299 | ret |
| 300 | .p2align 4,,7 |
| 301 | ret_point: |
| 302 | call restore_registers |
| 303 | call restore_processor_state |
| 304 | ret |
| 305 | |
| 306 | ENTRY(do_suspend_lowlevel_s4bios) |
| 307 | call save_processor_state |
| 308 | call save_registers |
| 309 | call acpi_enter_sleep_state_s4bios |
| 310 | ret |
| 311 | |
| 312 | ALIGN |
| 313 | # saved registers |
| 314 | saved_gdt: .long 0,0 |
| 315 | saved_idt: .long 0,0 |
| 316 | saved_ldt: .long 0 |
| 317 | saved_tss: .long 0 |
| 318 | |