summaryrefslogtreecommitdiffstats
path: root/sys/i386/acpica/acpi_wakecode.S
diff options
context:
space:
mode:
Diffstat (limited to 'sys/i386/acpica/acpi_wakecode.S')
-rw-r--r--sys/i386/acpica/acpi_wakecode.S322
1 files changed, 134 insertions, 188 deletions
diff --git a/sys/i386/acpica/acpi_wakecode.S b/sys/i386/acpica/acpi_wakecode.S
index e23b138..27520f3 100644
--- a/sys/i386/acpica/acpi_wakecode.S
+++ b/sys/i386/acpica/acpi_wakecode.S
@@ -1,6 +1,8 @@
/*-
* Copyright (c) 2001 Takanori Watanabe <takawata@jp.freebsd.org>
- * Copyright (c) 2001 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
+ * Copyright (c) 2001-2012 Mitsuru IWASAKI <iwasaki@jp.freebsd.org>
+ * Copyright (c) 2003 Peter Wemm
+ * Copyright (c) 2008-2012 Jung-uk Kim <jkim@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -28,7 +30,9 @@
*/
#include <machine/asmacros.h>
+#include <machine/ppireg.h>
#include <machine/specialreg.h>
+#include <machine/timerreg.h>
#include "assym.s"
@@ -39,221 +43,163 @@
* Depending on the previous sleep state, we may need to initialize more
* of the system (i.e., S3 suspend-to-RAM vs. S4 suspend-to-disk).
*/
- .align 4
- .code16
-wakeup_16:
- nop
- cli
- cld
+ .data /* So we can modify it */
+
+ ALIGN_TEXT
+ .code16
+wakeup_start:
/*
* Set up segment registers for real mode, a small stack for
* any calls we make, and clear any flags.
*/
- movw %cs,%ax
- movw %ax,%ds
- movw %ax,%ss
- movw $PAGE_SIZE,%sp
- pushl $0
- popfl
+ cli /* make sure no interrupts */
+ mov %cs, %ax /* copy %cs to %ds. Remember these */
+ mov %ax, %ds /* are offsets rather than selectors */
+ mov %ax, %ss
+ movw $PAGE_SIZE, %sp
+ xorw %ax, %ax
+ pushw %ax
+ popfw
/* To debug resume hangs, beep the speaker if the user requested. */
- cmpl $1,resume_beep
- jne nobeep
- movb $0xc0,%al
- outb %al,$0x42
- movb $0x04,%al
- outb %al,$0x42
- inb $0x61,%al
- orb $0x3,%al
- outb %al,$0x61
-nobeep:
+ testb $~0, resume_beep - wakeup_start
+ jz 1f
+ movb $0, resume_beep - wakeup_start
+
+ /* Set PIC timer2 to beep. */
+ movb $(TIMER_SEL2 | TIMER_SQWAVE | TIMER_16BIT), %al
+ outb %al, $TIMER_MODE
+
+ /* Turn on speaker. */
+ inb $IO_PPI, %al
+ orb $PIT_SPKR, %al
+ outb %al, $IO_PPI
+
+ /* Set frequency. */
+ movw $0x4c0, %ax
+ outb %al, $TIMER_CNTR2
+ shrw $8, %ax
+ outb %al, $TIMER_CNTR2
+1:
/* Re-initialize video BIOS if the reset_video tunable is set. */
- cmpl $1,reset_video
- jne nobiosreset
- lcall $0xc000,$3
+ testb $~0, reset_video - wakeup_start
+ jz 1f
+ movb $0, reset_video - wakeup_start
+ lcall $0xc000, $3
+
+ /* When we reach here, int 0x10 should be ready. Hide cursor. */
+ movb $0x01, %ah
+ movb $0x20, %ch
+ int $0x10
+
+ /* Re-start in case the previous BIOS call clobbers them. */
+ jmp wakeup_start
+1:
/*
- * Set up segment registers for real mode again in case the
- * previous BIOS call clobbers them.
+ * Find relocation base and patch the gdt descript and ljmp targets
*/
- movw %cs,%ax
- movw %ax,%ds
- movw %ax,%ss
-nobiosreset:
-
- /* Load GDT for real mode. Use 32 bit prefix for addresses >16 MB. */
- lgdtl physical_gdt
-
- /* Restore CR2, CR3 and CR4 */
- movl previous_cr2,%eax
- movl %eax,%cr2
- movl previous_cr3,%eax
- movl %eax,%cr3
- movl previous_cr4,%eax
- movl %eax,%cr4
+ xorl %ebx, %ebx
+ mov %cs, %bx
+ sall $4, %ebx /* %ebx is now our relocation base */
- /* Transfer some values to protected mode with an inline stack */
-#define NVALUES 9
-#define TRANSFER_STACK32(val, idx) \
- movl val,%eax; \
- movl %eax,wakeup_32stack+(idx+1)+(idx*4)
-
- TRANSFER_STACK32(previous_ss, (NVALUES - 9))
- TRANSFER_STACK32(previous_fs, (NVALUES - 8))
- TRANSFER_STACK32(previous_ds, (NVALUES - 7))
- TRANSFER_STACK32(physical_gdt+2, (NVALUES - 6))
- TRANSFER_STACK32(where_to_recover, (NVALUES - 5))
- TRANSFER_STACK32(previous_idt+2, (NVALUES - 4))
- TRANSFER_STACK32(previous_ldt, (NVALUES - 3))
- TRANSFER_STACK32(previous_gdt+2, (NVALUES - 2))
- TRANSFER_STACK32(previous_tr, (NVALUES - 1))
- TRANSFER_STACK32(previous_cr0, (NVALUES - 0))
-
- mov physical_esp,%esi /* to be used in 32bit code */
+ /*
+ * Load the descriptor table pointer. We'll need it when running
+ * in 16-bit protected mode.
+ */
+ lgdtl bootgdtdesc - wakeup_start
/* Enable protected mode */
- movl %cr0,%eax
- orl $(CR0_PE),%eax
- movl %eax,%cr0
+ movl $CR0_PE, %eax
+ mov %eax, %cr0
+ /*
+ * Now execute a far jump to turn on protected mode. This
+ * causes the segment registers to turn into selectors and causes
+ * %cs to be loaded from the gdt.
+ *
+ * The following instruction is:
+ * ljmpl $bootcode32 - bootgdt, $wakeup_32 - wakeup_start
+ * but gas cannot assemble that. And besides, we patch the targets
+ * in early startup and its a little clearer what we are patching.
+ */
wakeup_sw32:
- /* Switch to protected mode by intersegmental jump */
- ljmpl $KCSEL,$0x12345678 /* Code location, to be replaced */
+ .byte 0x66 /* size override to 32 bits */
+ .byte 0xea /* opcode for far jump */
+ .long wakeup_32 - wakeup_start /* offset in segment */
+ .word bootcode32 - bootgdt /* index in gdt for 32 bit code */
/*
- * Now switched to protected mode without paging enabled.
- * %esi: KERNEL stack pointer (physical address)
+ * At this point, we are running in 32 bit legacy protected mode.
*/
+ ALIGN_TEXT
.code32
wakeup_32:
- nop
-
- /* Set up segment registers for protected mode */
- movw $KDSEL,%ax /* KDSEL to segment registers */
- movw %ax,%ds
- movw %ax,%es
- movw %ax,%gs
- movw %ax,%ss
- movw $KPSEL,%ax /* KPSEL to %fs */
- movw %ax,%fs
- movl %esi,%esp /* physical address stack pointer */
-wakeup_32stack:
- /* Operands are overwritten in 16 bit code by TRANSFER_STACK32 macro */
- pushl $0xabcdef09 /* ss + dummy */
- pushl $0xabcdef08 /* fs + gs */
- pushl $0xabcdef07 /* ds + es */
- pushl $0xabcdef06 /* gdt:base (physical address) */
- pushl $0xabcdef05 /* recover address */
- pushl $0xabcdef04 /* idt:base */
- pushl $0xabcdef03 /* ldt + idt:limit */
- pushl $0xabcdef02 /* gdt:base */
- pushl $0xabcdef01 /* TR + gdt:limit */
- pushl $0xabcdef00 /* CR0 */
+ mov $bootdata32 - bootgdt, %eax
+ mov %ax, %ds
- movl %esp,%ebp
-#define CR0_REGISTER 0(%ebp)
-#define TASK_REGISTER 4(%ebp)
-#define PREVIOUS_GDT 6(%ebp)
-#define PREVIOUS_LDT 12(%ebp)
-#define PREVIOUS_IDT 14(%ebp)
-#define RECOVER_ADDR 20(%ebp)
-#define PHYSICAL_GDT_BASE 24(%ebp)
-#define PREVIOUS_DS 28(%ebp)
-#define PREVIOUS_ES 30(%ebp)
-#define PREVIOUS_FS 32(%ebp)
-#define PREVIOUS_GS 34(%ebp)
-#define PREVIOUS_SS 36(%ebp)
+ /* Get PCB and return address. */
+ movl wakeup_pcb - wakeup_start(%ebx), %esi
+ movl wakeup_ret - wakeup_start(%ebx), %edi
- /* Fixup TSS type field */
-#define TSS_TYPEFIX_MASK 0xf9
- xorl %esi,%esi
- movl PHYSICAL_GDT_BASE,%ebx
- movw TASK_REGISTER,%si
- leal (%ebx,%esi),%eax /* get TSS segment descriptor */
- andb $TSS_TYPEFIX_MASK,5(%eax)
-
- /* Prepare to return to sleep/wakeup code point */
- lgdtl PREVIOUS_GDT
- lidtl PREVIOUS_IDT
-
- /* Pack values from the GDT to be loaded into segment registers. */
- movl PREVIOUS_DS,%ebx
- movl PREVIOUS_FS,%ecx
- movl PREVIOUS_SS,%edx
- movw TASK_REGISTER,%si
- shll $16,%esi
- movw PREVIOUS_LDT,%si
- movl RECOVER_ADDR,%edi
-
- /* Enable paging and etc. */
- movl CR0_REGISTER,%eax
- movl %eax,%cr0
-
- /* Flush the prefetch queue */
- jmp 1f
-1: jmp 1f
-1:
+ /* Restore CR4 and CR3. */
+ movl wakeup_cr4 - wakeup_start(%ebx), %eax
+ mov %eax, %cr4
+ movl wakeup_cr3 - wakeup_start(%ebx), %eax
+ mov %eax, %cr3
/*
- * Now we are in kernel virtual memory addressing with the following
- * original register values:
- * %ebx: ds + es
- * %ecx: fs + gs
- * %edx: ss + dummy
- * %esi: LDTR + TR
- * %edi: recover address
- * We'll load these back into the segment registers now.
+ * Finally, switch to long bit mode by enabling paging. We have
+ * to be very careful here because all the segmentation disappears
+ * out from underneath us. The spec says we can depend on the
+ * subsequent pipelined branch to execute, but *only if* everthing
+ * is still identity mapped. If any mappings change, the pipeline
+ * will flush.
*/
- nop
-
- movl %esi,%eax /* LDTR + TR */
- lldt %ax /* load LDT register */
- shrl $16,%eax
- ltr %ax /* load task register */
+ mov %cr0, %eax
+ orl $CR0_PG, %eax
+ mov %eax, %cr0
- /* Restore segment registers */
- movl %ebx,%eax /* ds + es */
- movw %ax,%ds
- shrl $16,%eax
- movw %ax,%es
- movl %ecx,%eax /* fs + gs */
- movw %ax,%fs
- shrl $16,%eax
- movw %ax,%gs
- movl %edx,%eax /* ss */
- movw %ax,%ss
-
- /* Jump to acpi_restorecpu() */
+ jmp 1f
+1:
+ /* Jump to return address. */
jmp *%edi
-/* used in real mode */
-physical_gdt: .word 0
- .long 0
-physical_esp: .long 0
-previous_cr2: .long 0
-previous_cr3: .long 0
-previous_cr4: .long 0
-resume_beep: .long 0
-reset_video: .long 0
-
-/*
- * Transfer from real mode to protected mode. The order of these variables
- * is very important, DO NOT INSERT OR CHANGE unless you know why.
- */
-previous_cr0: .long 0
-previous_tr: .word 0
-previous_gdt: .word 0
- .long 0
-previous_ldt: .word 0
-previous_idt: .word 0
- .long 0
-where_to_recover: .long 0
-previous_ds: .word 0
-previous_es: .word 0
-previous_fs: .word 0
-previous_gs: .word 0
-previous_ss: .word 0
-dummy: .word 0
+ .data
+
+resume_beep:
+ .byte 0
+reset_video:
+ .byte 0
+
+ ALIGN_DATA
+bootgdt:
+ .long 0x00000000
+ .long 0x00000000
+
+bootcode32:
+ .long 0x0000ffff
+ .long 0x00cf9b00
+
+bootdata32:
+ .long 0x0000ffff
+ .long 0x00cf9300
+bootgdtend:
+
+bootgdtdesc:
+ .word bootgdtend - bootgdt /* Length */
+ .long bootgdt - wakeup_start /* Offset plus %ds << 4 */
+
+ ALIGN_DATA
+wakeup_cr4:
+ .long 0
+wakeup_cr3:
+ .long 0
+wakeup_pcb:
+ .long 0
+wakeup_ret:
+ .long 0
+dummy:
OpenPOWER on IntegriCloud