| 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 |  | 
| Nickolai Zeldovich | 9d94377 | 2005-04-08 23:37:34 -0400 | [diff] [blame] | 77 | # need a gdt -- use lgdtl to force 32-bit operands, in case | 
|  | 78 | # the GDT is located past 16 megabytes. | 
|  | 79 | lgdtl	real_save_gdt - wakeup_code | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 80 |  | 
|  | 81 | movl	real_save_cr0 - wakeup_code, %eax | 
|  | 82 | movl	%eax, %cr0 | 
|  | 83 | jmp 1f | 
|  | 84 | 1: | 
|  | 85 | movw	$0x0e00 + 'n', %fs:(0x14) | 
|  | 86 |  | 
|  | 87 | movl	real_magic - wakeup_code, %eax | 
|  | 88 | cmpl	$0x12345678, %eax | 
|  | 89 | jne	bogus_real_magic | 
|  | 90 |  | 
|  | 91 | ljmpl	$__KERNEL_CS,$wakeup_pmode_return | 
|  | 92 |  | 
|  | 93 | real_save_gdt:	.word 0 | 
|  | 94 | .long 0 | 
|  | 95 | real_save_cr0:	.long 0 | 
|  | 96 | real_save_cr3:	.long 0 | 
|  | 97 | real_save_cr4:	.long 0 | 
|  | 98 | real_magic:	.long 0 | 
|  | 99 | video_mode:	.long 0 | 
|  | 100 | video_flags:	.long 0 | 
|  | 101 | real_efer_save_restore:	.long 0 | 
|  | 102 | real_save_efer_edx: 	.long 0 | 
|  | 103 | real_save_efer_eax: 	.long 0 | 
|  | 104 |  | 
|  | 105 | bogus_real_magic: | 
|  | 106 | movw	$0x0e00 + 'B', %fs:(0x12) | 
|  | 107 | jmp bogus_real_magic | 
|  | 108 |  | 
|  | 109 | /* This code uses an extended set of video mode numbers. These include: | 
|  | 110 | * Aliases for standard modes | 
|  | 111 | *	NORMAL_VGA (-1) | 
|  | 112 | *	EXTENDED_VGA (-2) | 
|  | 113 | *	ASK_VGA (-3) | 
|  | 114 | * Video modes numbered by menu position -- NOT RECOMMENDED because of lack | 
|  | 115 | * of compatibility when extending the table. These are between 0x00 and 0xff. | 
|  | 116 | */ | 
|  | 117 | #define VIDEO_FIRST_MENU 0x0000 | 
|  | 118 |  | 
|  | 119 | /* Standard BIOS video modes (BIOS number + 0x0100) */ | 
|  | 120 | #define VIDEO_FIRST_BIOS 0x0100 | 
|  | 121 |  | 
|  | 122 | /* VESA BIOS video modes (VESA number + 0x0200) */ | 
|  | 123 | #define VIDEO_FIRST_VESA 0x0200 | 
|  | 124 |  | 
|  | 125 | /* Video7 special modes (BIOS number + 0x0900) */ | 
|  | 126 | #define VIDEO_FIRST_V7 0x0900 | 
|  | 127 |  | 
|  | 128 | # Setting of user mode (AX=mode ID) => CF=success | 
|  | 129 | mode_set: | 
|  | 130 | movw	%ax, %bx | 
|  | 131 | #if 0 | 
|  | 132 | cmpb	$0xff, %ah | 
|  | 133 | jz	setalias | 
|  | 134 |  | 
|  | 135 | testb	$VIDEO_RECALC>>8, %ah | 
|  | 136 | jnz	_setrec | 
|  | 137 |  | 
|  | 138 | cmpb	$VIDEO_FIRST_RESOLUTION>>8, %ah | 
|  | 139 | jnc	setres | 
|  | 140 |  | 
|  | 141 | cmpb	$VIDEO_FIRST_SPECIAL>>8, %ah | 
|  | 142 | jz	setspc | 
|  | 143 |  | 
|  | 144 | cmpb	$VIDEO_FIRST_V7>>8, %ah | 
|  | 145 | jz	setv7 | 
|  | 146 | #endif | 
|  | 147 |  | 
|  | 148 | cmpb	$VIDEO_FIRST_VESA>>8, %ah | 
|  | 149 | jnc	check_vesa | 
|  | 150 | #if 0 | 
|  | 151 | orb	%ah, %ah | 
|  | 152 | jz	setmenu | 
|  | 153 | #endif | 
|  | 154 |  | 
|  | 155 | decb	%ah | 
|  | 156 | #	jz	setbios				  Add bios modes later | 
|  | 157 |  | 
|  | 158 | setbad:	clc | 
|  | 159 | ret | 
|  | 160 |  | 
|  | 161 | check_vesa: | 
|  | 162 | subb	$VIDEO_FIRST_VESA>>8, %bh | 
|  | 163 | orw	$0x4000, %bx			# Use linear frame buffer | 
|  | 164 | movw	$0x4f02, %ax			# VESA BIOS mode set call | 
|  | 165 | int	$0x10 | 
|  | 166 | cmpw	$0x004f, %ax			# AL=4f if implemented | 
|  | 167 | jnz	_setbad				# AH=0 if OK | 
|  | 168 |  | 
|  | 169 | stc | 
|  | 170 | ret | 
|  | 171 |  | 
|  | 172 | _setbad: jmp setbad | 
|  | 173 |  | 
|  | 174 | .code32 | 
|  | 175 | ALIGN | 
|  | 176 |  | 
|  | 177 | .org	0x800 | 
|  | 178 | wakeup_stack_begin:	# Stack grows down | 
|  | 179 |  | 
|  | 180 | .org	0xff0		# Just below end of page | 
|  | 181 | wakeup_stack: | 
|  | 182 | ENTRY(wakeup_end) | 
|  | 183 |  | 
|  | 184 | .org	0x1000 | 
|  | 185 |  | 
|  | 186 | wakeup_pmode_return: | 
|  | 187 | movw	$__KERNEL_DS, %ax | 
|  | 188 | movw	%ax, %ss | 
|  | 189 | movw	%ax, %ds | 
|  | 190 | movw	%ax, %es | 
|  | 191 | movw	%ax, %fs | 
|  | 192 | movw	%ax, %gs | 
|  | 193 | movw	$0x0e00 + 'u', 0xb8016 | 
|  | 194 |  | 
|  | 195 | # reload the gdt, as we need the full 32 bit address | 
|  | 196 | lgdt	saved_gdt | 
|  | 197 | lidt	saved_idt | 
|  | 198 | lldt	saved_ldt | 
|  | 199 | ljmp	$(__KERNEL_CS),$1f | 
|  | 200 | 1: | 
|  | 201 | movl	%cr3, %eax | 
|  | 202 | movl	%eax, %cr3 | 
|  | 203 | wbinvd | 
|  | 204 |  | 
|  | 205 | # and restore the stack ... but you need gdt for this to work | 
|  | 206 | movl	saved_context_esp, %esp | 
|  | 207 |  | 
|  | 208 | movl	%cs:saved_magic, %eax | 
|  | 209 | cmpl	$0x12345678, %eax | 
|  | 210 | jne	bogus_magic | 
|  | 211 |  | 
|  | 212 | # jump to place where we left off | 
|  | 213 | movl	saved_eip,%eax | 
|  | 214 | jmp	*%eax | 
|  | 215 |  | 
|  | 216 | bogus_magic: | 
|  | 217 | movw	$0x0e00 + 'B', 0xb8018 | 
|  | 218 | jmp	bogus_magic | 
|  | 219 |  | 
|  | 220 |  | 
|  | 221 | ## | 
|  | 222 | # acpi_copy_wakeup_routine | 
|  | 223 | # | 
|  | 224 | # Copy the above routine to low memory. | 
|  | 225 | # | 
|  | 226 | # Parameters: | 
|  | 227 | # %eax:	place to copy wakeup routine to | 
|  | 228 | # | 
|  | 229 | # Returned address is location of code in low memory (past data and stack) | 
|  | 230 | # | 
|  | 231 | ENTRY(acpi_copy_wakeup_routine) | 
|  | 232 |  | 
|  | 233 | sgdt	saved_gdt | 
|  | 234 | sidt	saved_idt | 
|  | 235 | sldt	saved_ldt | 
|  | 236 | str	saved_tss | 
|  | 237 |  | 
|  | 238 | movl	nx_enabled, %edx | 
|  | 239 | movl	%edx, real_efer_save_restore - wakeup_start (%eax) | 
|  | 240 | testl	$1, real_efer_save_restore - wakeup_start (%eax) | 
|  | 241 | jz	2f | 
|  | 242 | # save efer setting | 
|  | 243 | pushl	%eax | 
|  | 244 | movl	%eax, %ebx | 
|  | 245 | mov     $0xc0000080, %ecx | 
|  | 246 | rdmsr | 
|  | 247 | movl	%edx, real_save_efer_edx - wakeup_start (%ebx) | 
|  | 248 | movl	%eax, real_save_efer_eax - wakeup_start (%ebx) | 
|  | 249 | popl	%eax | 
|  | 250 | 2: | 
|  | 251 |  | 
|  | 252 | movl    %cr3, %edx | 
|  | 253 | movl    %edx, real_save_cr3 - wakeup_start (%eax) | 
|  | 254 | movl    %cr4, %edx | 
|  | 255 | movl    %edx, real_save_cr4 - wakeup_start (%eax) | 
|  | 256 | movl	%cr0, %edx | 
|  | 257 | movl	%edx, real_save_cr0 - wakeup_start (%eax) | 
|  | 258 | sgdt    real_save_gdt - wakeup_start (%eax) | 
|  | 259 |  | 
|  | 260 | movl	saved_videomode, %edx | 
|  | 261 | movl	%edx, video_mode - wakeup_start (%eax) | 
|  | 262 | movl	acpi_video_flags, %edx | 
|  | 263 | movl	%edx, video_flags - wakeup_start (%eax) | 
|  | 264 | movl	$0x12345678, real_magic - wakeup_start (%eax) | 
|  | 265 | movl	$0x12345678, saved_magic | 
|  | 266 | ret | 
|  | 267 |  | 
|  | 268 | .data | 
|  | 269 | ALIGN | 
|  | 270 | ENTRY(saved_magic)	.long	0 | 
|  | 271 | ENTRY(saved_eip)	.long	0 | 
|  | 272 |  | 
|  | 273 | save_registers: | 
|  | 274 | leal	4(%esp), %eax | 
|  | 275 | movl	%eax, saved_context_esp | 
|  | 276 | movl %ebx, saved_context_ebx | 
|  | 277 | movl %ebp, saved_context_ebp | 
|  | 278 | movl %esi, saved_context_esi | 
|  | 279 | movl %edi, saved_context_edi | 
|  | 280 | pushfl ; popl saved_context_eflags | 
|  | 281 |  | 
|  | 282 | movl $ret_point, saved_eip | 
|  | 283 | ret | 
|  | 284 |  | 
|  | 285 |  | 
|  | 286 | restore_registers: | 
|  | 287 | movl saved_context_ebp, %ebp | 
|  | 288 | movl saved_context_ebx, %ebx | 
|  | 289 | movl saved_context_esi, %esi | 
|  | 290 | movl saved_context_edi, %edi | 
|  | 291 | pushl saved_context_eflags ; popfl | 
|  | 292 | ret | 
|  | 293 |  | 
|  | 294 | ENTRY(do_suspend_lowlevel) | 
|  | 295 | call	save_processor_state | 
|  | 296 | call	save_registers | 
|  | 297 | pushl	$3 | 
|  | 298 | call	acpi_enter_sleep_state | 
|  | 299 | addl	$4, %esp | 
|  | 300 | ret | 
|  | 301 | .p2align 4,,7 | 
|  | 302 | ret_point: | 
|  | 303 | call	restore_registers | 
|  | 304 | call	restore_processor_state | 
|  | 305 | ret | 
|  | 306 |  | 
|  | 307 | ENTRY(do_suspend_lowlevel_s4bios) | 
|  | 308 | call save_processor_state | 
|  | 309 | call save_registers | 
|  | 310 | call acpi_enter_sleep_state_s4bios | 
|  | 311 | ret | 
|  | 312 |  | 
|  | 313 | ALIGN | 
|  | 314 | # saved registers | 
|  | 315 | saved_gdt:	.long	0,0 | 
|  | 316 | saved_idt:	.long	0,0 | 
|  | 317 | saved_ldt:	.long	0 | 
|  | 318 | saved_tss:	.long	0 | 
|  | 319 |  |