revert between 56095 -> 55830 in arch
[AROS.git] / arch / x86_64-pc / kernel / smpbootstrap.s
blob952cb7ae99931ee1c00657ae09bfd37834a41aa5
1 # This is the bootstrap code for secondary cores (or APs - Application Processors)
2 # Every AP starts ip in real mode from the address we specify. The address is page-
3 # aligned, and it is converted by hardware to segment:offset notation and loaded
4 # into CS:IP. IP is effectively zero and CS is the address divided by 16.
5 # This code is statically linked at address 0. But segmentation makes if effectively
6 # position-indepentent. The only thing which is true, is that the code starts at
7 # zero offset in the segment. This is why we can safely use absolute addressing here.
8 #
9 # This code doesn not use main AROS 64-bit GDT, but uses a temporary one instead.
10 # This is done because theoretically AROS GDT can be placed anywhere in 64-bit memory,
11 # but our GDT must be reachable at least from within 32-bit mode, to be able to
12 # load its address. AROS replaces this GDT with own one in core_CPUSetup() function.
14 .code16
15 cli
16 jmp boot16
18 .align 8 # The config variables are passed here
19 arg1: .long 0
20 .long 0
21 arg2: .long 0
22 .long 0
23 arg3: .long 0
24 .long 0
25 arg4: .long 0
26 .long 0
27 pml4: .long 0
28 .long 0
29 sp: .long 0
30 .long 0
31 ip: .long 0
32 .long 0
33 gdt32: .short 0 # Temporary GDT for 32-bit mode.
34 .short 0 # Just two supervisor-mode segments of the same length,
35 .short 0 # one for code and one for data.
36 .short 0 # Zero descriptor is never used.
37 .short 0x1000
38 .short 0x0000
39 .short 0x9a00
40 .short 0x0040
41 .short 0x1000
42 .short 0x0000
43 .short 0x9200
44 .short 0x0040
45 gdt64: .short 0 # Temporary 64-bit GDT. Again just two segments.
46 .short 0
47 .short 0
48 .short 0
49 .short 0xffff
50 .short 0x0000
51 .short 0x9a00
52 .short 0x00af
53 .short 0xffff
54 .short 0x0000
55 .short 0x9200
56 .short 0x00af
57 gdtr32: # 32-bit GDTR
58 .short 23 # Limit. We use only first three descriptors here.
59 .long 0 # Base
60 gdtr64: # 64-bit GDTR.
61 .short 23 # Limit
62 .long 0 # Base (32-bit notation)
63 target: # Far jump destination
64 .long 0 # Address (32-bit notation)
65 .short 8 # Segment selector
67 # Well, here we are in real mode. We want to reach long mode, but we cannot do that directly.
68 # We need to jump to 32-bit protected mode first. We already have a GDT for this. But in order
69 # not to lose ourselves, we need to adjust base addresses of 32-bit segments to be equal to those
70 # of 16-bit segments, so that our code stays at logical address zero.
71 # The only truly absolute (physical) addresses here are segment starts in GDT and
72 # 64-bit code address. We fix them up in this part, using initial CS value to get our physical location.
74 boot16: mov $0, %eax
75 mov %cs, %ax # First set up data segment
76 mov %ax, %ds
77 shl $4, %eax # Determine our location in 32-bit form (EAX)
78 leal gdt32(%eax), %ebx # Load physical address of 32-bit gdt
79 movl %ebx, gdtr32+2 # Set up 32-bit gdt address
80 leal gdt64(%eax), %ebx # Load physical address of 64-bit gdt
81 movl %ebx, gdtr64+2 # Set up 64-bit gdt address
82 leal boot64(%eax), %ebx # Load physical address of 64-bit code
83 movl %ebx, target # Set up 64-bit entry address
84 movw %ax, gdt32+10 # Set base address of 32-bit code segment (bits 0:15)
85 movw %ax, gdt32+18 # Set base address of 32-bit data segment (bits 0:15)
86 shr $16, %eax
87 movb %al, gdt32+12 # Set base address of 32-bit code segment (bits 16:23)
88 movb %al, gdt32+20 # Set base address of 32-bit data segment (bits 16:23)
89 ADDR32 DATA32 lgdt gdtr32 # Load 32-bit gdt
90 movl %cr0, %eax # Enter protected mode
91 orb $1, %al
92 movl %eax, %cr0
93 rel1: ljmp $0x08, $boot32 # Jump into 32-bit mode, CS = 8.
95 # Here we are in 32-bit mode and can proceed to 64-bit one.
96 # In 32-bit mode segmentation is still active, and base addresses of our segments
97 # have been adjusted to match 16-bit ones. So we still start at logical address zero
98 # and still can use absolute addressing.
100 .code32
101 boot32: movw $0x10, %ax # Setup the 32-bit data selectors
102 movw %ax, %ds
103 movw %ax, %es
104 movw %ax, %ss
105 lgdt gdtr64 # Load 64-bit gdt
106 movl $0x000000A0, %eax # Enable PAE
107 movl %eax, %cr4
108 movl pml4, %ebx # Enable pages
109 movl %ebx, %cr3
110 movl $0xC0000080, %ecx # EFER MSR number
111 rdmsr
112 orl $0x00000100, %eax # Enable long mode
113 wrmsr
114 movl $0x80000001, %eax # Enable paging and activate long mode
115 movl %eax, %cr0
116 rel2: ljmp *target
118 # Phew, we are in 64-bit mode at last!
119 # In 64-bit mode there is effectively no segmentation. Offsets and limits are ignored.
120 # Segment descriptors are used only to specify CPU privilege level. This is why we
121 # needed to find an an absolute physical address of this point.
122 # The simplest way to achieve position independency here is to use RIP-relative addressing.
123 # Here we effectively do the same as core_Kick() function does, just with no .bss clearing.
125 .code64
126 boot64: movq sp(%rip), %rsp # Set stack pointer
127 movq ip(%rip), %rax # Get address to jump to
128 movq arg1(%rip), %rdi # Load arguments into registers
129 movq arg2(%rip), %rsi
130 movq arg3(%rip), %rdx
131 movq arg4(%rip), %rcx
132 movq $0, %rbp # Mark end of stack backtrace
133 call *%rax # GO!!! This must never return.