1// Copyright 2017 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7#include <arch/defines.h>
8#include <arch/x86/asm.h>
9#include <arch/x86/descriptor.h>
10#include <arch/x86/mmu.h>
11#include <arch/x86/registers.h>
12#include <asm.h>
13#include <zircon/tls.h>
14#include <mexec.h>
15
16#define CODE_SEGMENT_SELECTOR (1 << 3)
17#define DATA_SEGMENT_SELECTOR (2 << 3)
18
19.text
20FUNCTION_LABEL(mexec_asm)
21    // Make sure interrupts are disabled.
22    cli
23
24    /* Stash all the arguments passed in registers R8 - R13 */
25    mov %r9,  %r13   /* Unused Arg */
26    mov %r8,  %r12   /* Memmove Ops */
27    mov %rcx, %r11   /* Unused Arg */
28    mov %rdx, %r10   /* ENTRY64_ADDR */
29    mov %rsi, %r9    /* CR3 for Safe page tables */
30    mov %rdi, %r8    /* Bootimage Address */
31
32    // The old SP is in the old kernel virtual address space, so don't use it.
33    xor %esp, %esp
34
35    // Make sure old PGE mappings from the kernel address space are not
36    // still in the TLB.  Having them there masked the previous bug wherein
37    // this code relied on using the incoming stack pointer.
38    mov %cr4, %rax
39    and $~X86_CR4_PGE, %rax
40    mov %rax, %cr4
41
42    // Switch to the safe identity mapped page tables.
43    mov  %r9, %cr3
44
45    // Load our little GDT defined below.  The current GDT is somewhere
46    // that might be overwritten when we copy in the new kernel below.
47    lea mexec_gdt(%rip), %rax
48    mov %rax, mexec_gdt_pointer(%rip)
49    lgdt mexec_gdt_descriptor(%rip)
50
51    // Switch to the new data segment.
52    mov $DATA_SEGMENT_SELECTOR, %ax
53    mov %ax, %ds
54    mov %ax, %es
55    mov %ax, %ss
56
57    // Switch to the new code segment.
58    // Note that ljmp accepts only a 32-bit address (aka "offset").
59    // That's fine here, since we know we're running in the low 4G here.
60    leal .Lnew_cs(%rip), %eax
61    movl %eax, mexec_ljmp_descriptor(%rip)
62    ljmp *mexec_ljmp_descriptor(%rip)
63.Lnew_cs:
64
65    /* Load the kernel relocation op into ram */
66    mov MEMMOV_OPS_DST_OFFSET (%r12), %rdi
67    mov MEMMOV_OPS_SRC_OFFSET (%r12), %rsi
68    mov MEMMOV_OPS_LEN_OFFSET (%r12), %rcx
69
70    /* Move RCX bytes from RSI to RDI */
71    cld               /* Clear the direction flag so that we're copying forward */
72                      /* by default when we start */
73
74    cmp %rsi, %rdi    /* Compare the src and dst registers to see if we need to */
75                      /* copy forwards or backwards */
76
77    jbe .Ldo_copy      /* if dst is greater than src, go ahead and do the copy */
78                      /* forwards */
79
80    mov %rcx, %rax    /* rcx and rax contain the number of bytes to be copied */
81    sub $1,   %rax    /* Move rsi and rdi to the end of their respective buffers */
82    add %rax, %rdi
83    add %rax, %rsi
84
85    std               /* Set the direction flag to 1. This will ensure that the */
86                      /* copy happens from the back of the buffers to the front */
87
88.Ldo_copy:
89
90    rep movsb         /* copy RCX bytes from RSI to RDI */
91
92    cld               /* Clear the direction flag since we may have polluted it */
93                      /* if we did a copy backwards */
94
95    /* Move the address of the bootdata into the appropriate register */
96    mov %r8, %rsi
97
98    /* Zero out some registers */
99    xor %ebx, %ebx
100    xor %edi, %edi
101    xor %ebp, %ebp
102
103    /* Grab 64bit entrypoint from provided location */
104    mov (%r10), %rax
105
106    /* See you on the other side! */
107    jmp *%rax
108
109    /* Crash, we should never reach here */
110    ud2
111
112END_DATA(mexec_asm)
113
114.balign 8
115LOCAL_DATA(mexec_gdt)
116    // Null entry.
117    .int 0
118    .int 0
119
120    // 64-bit code segment.
121    .short 0xffff           // limit 15:00
122    .short 0x0000           // base 15:00
123    .byte  0x00             // base 23:16
124    .byte  0b10011010       // P(1) DPL(00) S(1) 1 C(0) R(1) A(0)
125    .byte  0b10101111       // G(1) D(0) L(1) AVL(0) limit 19:16
126    .byte  0x0              // base 31:24
127
128    // Data segment.
129    .short 0xffff           // limit 15:00
130    .short 0x0000           // base 15:00
131    .byte  0x00             // base 23:16
132    .byte  0b10010010       // P(1) DPL(00) S(1) 0 E(0) W(1) A(0)
133    .byte  0b11001111       // G(1) B(1) 0 0 limit 19:16
134    .byte  0x0              // base 31:24
135END_DATA(mexec_gdt)
136DATA(mexec_gdt_end)
137
138.balign 8
139LOCAL_DATA(mexec_gdt_descriptor)
140    .short mexec_gdt_end - mexec_gdt - 1
141LOCAL_DATA(mexec_gdt_pointer)
142    .quad 0 // Filled in at runtime.
143END_DATA(mexec_gdt_descriptor)
144
145.balign 8
146LOCAL_DATA(mexec_ljmp_descriptor)
147    .long 0 // Filled in at runtime.
148    .short CODE_SEGMENT_SELECTOR
149END_DATA(mexec_ljmp_descriptor)
150
151DATA(mexec_asm_end)
152