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.
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.
18 .align 8 # The config variables are passed here
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.
45 gdt64
: .short 0 # Temporary 64-bit GDT. Again just two segments.
58 .short 23 # Limit. We use only first three descriptors here.
60 gdtr64
: # 64-bit GDTR.
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.
75 mov
%cs
, %ax
# First set up data segment
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)
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
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.
101 boot32
: movw $
0x10, %ax
# Setup the 32-bit data selectors
105 lgdt gdtr64
# Load 64-bit gdt
106 movl $
0x000000A0, %eax
# Enable PAE
108 movl pml4
, %ebx
# Enable pages
110 movl $
0xC0000080, %ecx
# EFER MSR number
112 orl $
0x00000100, %eax
# Enable long mode
114 movl $
0x80000001, %eax
# Enable paging and activate long mode
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.
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.