aboutsummaryrefslogtreecommitdiffstats
path: root/xen/arch/x86/x86_64/compat_kexec.S
blob: fc92af9c5e1d32f72b9d607b1dcffca35b9cae2d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/*
 * Compatibility kexec handler.
 */

/*
 * NOTE: We rely on Xen not relocating itself above the 4G boundary. This is
 * currently true but if it ever changes then compat_pg_table will
 * need to be moved back below 4G at run time.
 */

#include <xen/config.h>

#include <asm/asm_defns.h>
#include <asm/msr.h>
#include <asm/page.h>

/* The unrelocated physical address of a symbol. */
#define SYM_PHYS(sym)          ((sym) - __XEN_VIRT_START)

/* Load physical address of symbol into register and relocate it. */
#define RELOCATE_SYM(sym,reg)  mov $SYM_PHYS(sym), reg ; \
                               add xen_phys_start(%rip), reg

/*
 * Relocate a physical address in memory. Size of temporary register
 * determines size of the value to relocate.
 */
#define RELOCATE_MEM(addr,reg) mov addr(%rip), reg ; \
                               add xen_phys_start(%rip), reg ; \
                               mov reg, addr(%rip)

        .text

        .code64

ENTRY(compat_machine_kexec)
        /* x86/64                        x86/32  */
        /* %rdi - relocate_new_kernel_t  CALL    */
        /* %rsi - indirection page       4(%esp) */
        /* %rdx - page_list              8(%esp) */
        /* %rcx - start address         12(%esp) */
        /*        cpu has pae           16(%esp) */

        /* Shim the 64 bit page_list into a 32 bit page_list. */
        mov $12,%r9
        lea compat_page_list(%rip), %rbx
1:      dec %r9
        movl (%rdx,%r9,8),%eax
        movl %eax,(%rbx,%r9,4)
        test %r9,%r9
        jnz 1b

        RELOCATE_SYM(compat_page_list,%rdx)

        /* Relocate compatibility mode entry point address. */
        RELOCATE_MEM(compatibility_mode_far,%eax)

        /* Relocate compat_pg_table. */
        RELOCATE_MEM(compat_pg_table,     %rax)
        RELOCATE_MEM(compat_pg_table+0x8, %rax)
        RELOCATE_MEM(compat_pg_table+0x10,%rax)
        RELOCATE_MEM(compat_pg_table+0x18,%rax)

        /*
         * Setup an identity mapped region in PML4[0] of idle page
         * table.
         */
        RELOCATE_SYM(l3_identmap,%rax)
        or  $0x63,%rax
        mov %rax, idle_pg_table(%rip)

        /* Switch to idle page table. */
        RELOCATE_SYM(idle_pg_table,%rax)
        movq %rax, %cr3

        /* Switch to identity mapped compatibility stack. */
        RELOCATE_SYM(compat_stack,%rax)
        movq %rax, %rsp

        /* Save xen_phys_start for 32 bit code. */
        movq xen_phys_start(%rip), %rbx

        /* Jump to low identity mapping in compatibility mode. */
        ljmp *compatibility_mode_far(%rip)
        ud2

compatibility_mode_far:
        .long SYM_PHYS(compatibility_mode)
        .long __HYPERVISOR_CS32

        /*
         * We use 5 words of stack for the arguments passed to the kernel. The
         * kernel only uses 1 word before switching to its own stack. Allocate
         * 16 words to give "plenty" of room.
         */
        .fill 16,4,0
compat_stack:

        .code32

#undef RELOCATE_SYM
#undef RELOCATE_MEM

/*
 * Load physical address of symbol into register and relocate it. %rbx
 * contains xen_phys_start(%rip) saved before jump to compatibility
 * mode.
 */
#define RELOCATE_SYM(sym,reg) mov $SYM_PHYS(sym), reg ; \
                              add %ebx, reg

compatibility_mode:
        /* Setup some sane segments. */
        movl $__HYPERVISOR_DS32, %eax
        movl %eax, %ds
        movl %eax, %es
        movl %eax, %fs
        movl %eax, %gs
        movl %eax, %ss

        /* Push arguments onto stack. */
        pushl $0   /* 20(%esp) - preserve context */
        pushl $1   /* 16(%esp) - cpu has pae */
        pushl %ecx /* 12(%esp) - start address */
        pushl %edx /*  8(%esp) - page list */
        pushl %esi /*  4(%esp) - indirection page */
        pushl %edi /*  0(%esp) - CALL */

        /* Disable paging and therefore leave 64 bit mode. */
        movl %cr0, %eax
        andl $~X86_CR0_PG, %eax
        movl %eax, %cr0

        /* Switch to 32 bit page table. */
        RELOCATE_SYM(compat_pg_table, %eax)
        movl  %eax, %cr3

        /* Clear MSR_EFER[LME], disabling long mode */
        movl    $MSR_EFER,%ecx
        rdmsr
        btcl    $_EFER_LME,%eax
        wrmsr

        /* Re-enable paging, but only 32 bit mode now. */
        movl %cr0, %eax
        orl $X86_CR0_PG, %eax
        movl %eax, %cr0
        jmp 1f
1:

        popl %eax
        call *%eax
        ud2

        .data
        .align 4
compat_page_list:
        .fill 12,4,0

        .align 32,0

        /*
         * These compat page tables contain an identity mapping of the
         * first 4G of the physical address space.
         */
compat_pg_table:
        .long SYM_PHYS(compat_pg_table_l2) + 0*PAGE_SIZE + 0x01, 0
        .long SYM_PHYS(compat_pg_table_l2) + 1*PAGE_SIZE + 0x01, 0
        .long SYM_PHYS(compat_pg_table_l2) + 2*PAGE_SIZE + 0x01, 0
        .long SYM_PHYS(compat_pg_table_l2) + 3*PAGE_SIZE + 0x01, 0

        .section .data.page_aligned, "aw", @progbits
        .align PAGE_SIZE,0
compat_pg_table_l2:
        .macro identmap from=0, count=512
        .if \count-1
        identmap "(\from+0)","(\count/2)"
        identmap "(\from+(0x200000*(\count/2)))","(\count/2)"
        .else
        .quad 0x00000000000000e3 + \from
        .endif
        .endm

        identmap 0x00000000
        identmap 0x40000000
        identmap 0x80000000
        identmap 0xc0000000