aboutsummaryrefslogtreecommitdiffstats
path: root/.gitignore
blob: 1bef86e97137eaf11c234ac6c08f8008536ca3a2 (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
> 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