entry.asm

Commit: 7f340f67 Author: jokerz Raw Copy
1
; ============================================================================
2
; Pactor64 Kernel Entry — x86-64 Long Mode via Multiboot2
3
;
4
; GRUB2 loads us in 32-bit protected mode. We transition to 64-bit long mode.
5
; Supports ring 3 (userspace) via GDT, TSS, SYSCALL/SYSRET.
6
; ============================================================================
7
8
; === Multiboot2 Constants ===
9
MB2_MAGIC    equ 0xE85250D6
10
MB2_ARCH     equ 0            ; i386
11
MB2_HEADER_LEN equ mb2_header_end - mb2_header
12
MB2_CHECKSUM equ -(MB2_MAGIC + MB2_ARCH + MB2_HEADER_LEN)
13
14
; === Page Table Addresses (must be 4KB aligned) ===
15
PML4_ADDR    equ 0x1000
16
PDPT_ADDR    equ 0x2000
17
PD_ADDR      equ 0x3000
18
19
; === Page Table Flags ===
20
PAGE_PRESENT equ 0x01
21
PAGE_WRITE   equ 0x02
22
PAGE_SIZE    equ 0x80         ; 2MB page (PS bit)
23
24
; === 64-bit GDT selectors ===
25
CODE64_SEL   equ 0x08
26
DATA64_SEL   equ 0x10
27
USER_CS_SEL  equ 0x18         ; GDT index 3 (DPL=3 → selector 0x18|3 = 0x1B)
28
USER_DS_SEL  equ 0x20         ; GDT index 4 (DPL=3 → selector 0x20|3 = 0x23)
29
TSS_SEL      equ 0x28         ; GDT index 5
30
31
; === MSRs ===
32
IA32_EFER    equ 0xC0000080
33
EFER_LME     equ 0x100        ; Long Mode Enable
34
EFER_SCE     equ 0x001        ; SYSCALL Enable
35
36
; === CR Bits ===
37
CR0_PE       equ 0x00000001
38
CR0_PG       equ 0x80000000
39
CR4_PAE      equ 0x00000020
40
41
; === VGA ===
42
VGA_BUFFER   equ 0xB8000
43
44
; === Serial ===
45
COM1         equ 0x3F8
46
47
section .multiboot
48
align 8
49
mb2_header:
50
    dd MB2_MAGIC
51
    dd MB2_ARCH
52
    dd MB2_HEADER_LEN
53
    dd MB2_CHECKSUM
54
55
    ; End tag (required)
56
    dw 0    ; type
57
    dw 0    ; flags
58
    dd 8    ; size
59
mb2_header_end:
60
61
; === 32-bit Entry Point (called by GRUB) ===
62
section .text
63
bits 32
64
global _start
65
extern kernel_main_64
66
67
_start:
68
    ; Save multiboot info pointer
69
    mov [mb2_info_ptr], ebx
70
    mov [mb2_magic_ptr], eax
71
72
    ; Disable interrupts
73
    cli
74
75
    ; Print 'P' to serial to confirm we got here
76
    mov al, 'P'
77
    call serial_out_32
78
79
    ; === Step 1: Check CPUID for long mode support ===
80
    mov eax, 0x80000000
81
    cpuid
82
    cmp eax, 0x80000001
83
    jb .no_long_mode
84
85
    mov eax, 0x80000001
86
    cpuid
87
    test edx, (1 << 29)      ; LM bit
88
    jz .no_long_mode
89
90
    mov al, 'L'
91
    call serial_out_32
92
93
    ; === Step 2: Set up page tables for long mode ===
94
    ; Identity-map first 1GB using 2MB pages
95
    ; PML4[0] → PDPT, PDPT[0] → PD, PD[0..511] → 2MB pages
96
97
    ; Clear page table area (4KB each)
98
    mov edi, PML4_ADDR
99
    mov ecx, 4096 * 4 / 4    ; 4 pages = 16KB
100
    xor eax, eax
101
    rep stosd
102
103
    ; PML4[0] = PDPT_ADDR | PRESENT | WRITE
104
    mov dword [PML4_ADDR], PDPT_ADDR | PAGE_PRESENT | PAGE_WRITE
105
106
    ; PDPT[0] = PD_ADDR | PRESENT | WRITE
107
    mov dword [PDPT_ADDR], PD_ADDR | PAGE_PRESENT | PAGE_WRITE
108
109
    ; PD[0..511] = 2MB pages, identity mapped
110
    mov edi, PD_ADDR
111
    mov eax, PAGE_PRESENT | PAGE_WRITE | PAGE_SIZE  ; First 2MB page at 0
112
    mov ecx, 512
113
.fill_pd:
114
    stosd
115
    push eax
116
    mov eax, 0
117
    stosd                   ; High 32 bits = 0
118
    pop eax
119
    add eax, 0x200000       ; Next 2MB page
120
    loop .fill_pd
121
122
    mov al, 'T'
123
    call serial_out_32
124
125
    ; === Step 3: Load PML4 into CR3 ===
126
    mov eax, PML4_ADDR
127
    mov cr3, eax
128
129
    ; === Step 4: Enable PAE (CR4.PAE) ===
130
    mov eax, cr4
131
    or eax, CR4_PAE
132
    mov cr4, eax
133
134
    ; === Step 5: Enable Long Mode (IA32_EFER.LME) ===
135
    mov ecx, IA32_EFER
136
    rdmsr
137
    or eax, EFER_LME | EFER_SCE
138
    wrmsr
139
140
    ; === Step 6: Enable Paging (CR0.PG) ===
141
    mov eax, cr0
142
    or eax, CR0_PG
143
    mov cr0, eax
144
145
    mov al, 'M'
146
    call serial_out_32
147
148
    ; === Step 7: Load initial 64-bit GDT and far jump ===
149
    lgdt [gdt64_boot_ptr]
150
    jmp CODE64_SEL:long_mode_entry
151
152
.no_long_mode:
153
    mov al, '!'
154
    call serial_out_32
155
    cli
156
    hlt
157
158
; === 32-bit Serial Output (for boot debug) ===
159
serial_out_32:
160
    push edx
161
    push eax
162
    mov dx, COM1 + 5
163
.wait:
164
    in al, dx
165
    test al, 0x20
166
    jz .wait
167
    pop eax
168
    mov dx, COM1
169
    out dx, al
170
    pop edx
171
    ret
172
173
; === Boot GDT (minimal, used only for 32→64 mode switch) ===
174
align 8
175
gdt64_boot:
176
    dq 0                             ; Null descriptor
177
    dq 0x00AF9A000000FFFF            ; 64-bit code: L=1, D=0, P=1, S=1, E=1, R=1
178
    dq 0x00CF92000000FFFF            ; 64-bit data: P=1, S=1, W=1
179
gdt64_boot_ptr:
180
    dw gdt64_boot_ptr - gdt64_boot - 1  ; Limit
181
    dq gdt64_boot                        ; Base
182
183
; === Multiboot info (saved from GRUB) ===
184
section .data
185
align 8
186
mb2_info_ptr: dq 0
187
mb2_magic_ptr: dq 0
188
189
; ============================================================================
190
; Full 64-bit GDT with ring 3 segments and TSS
191
; Loaded after entering long mode. Index layout:
192
;   0: Null       (selector 0x00)
193
;   1: Kern code   (selector 0x08)  — DPL=0, L=1
194
;   2: Kern data   (selector 0x10)  — DPL=0
195
;   3: User code   (selector 0x18)  — DPL=3, L=1
196
;   4: User data   (selector 0x20)  — DPL=3
197
;   5: TSS         (selector 0x28)  — 16 bytes, filled at runtime
198
; ============================================================================
199
align 8
200
global gdt64_full
201
gdt64_full:
202
    dq 0                             ; Null (index 0)
203
    dq 0x00AF9A000000FFFF            ; Kernel code (index 1, sel 0x08)
204
    dq 0x00CF92000000FFFF            ; Kernel data (index 2, sel 0x10)
205
    dq 0x00AFFA000000FFFF            ; User code (index 3, sel 0x18)
206
    dq 0x00CFF2000000FFFF            ; User data (index 4, sel 0x20)
207
    ; TSS descriptor (index 5, sel 0x28) — 16 bytes, filled at runtime
208
    dq 0
209
    dq 0
210
gdt64_full_ptr:
211
    dw gdt64_full_ptr - gdt64_full - 1  ; Limit = 55
212
    dq gdt64_full                        ; Base
213
214
; ============================================================================
215
; TSS (Task State Segment) for 64-bit mode
216
; Used for RSP0 (kernel stack on SYSCALL entry from ring 3)
217
; ============================================================================
218
align 16
219
global tss_struct
220
tss_struct:
221
    dd 0            ; Reserved (offset 0)
222
    dq 0            ; RSP0 (offset 4) — filled at runtime
223
    dq 0            ; RSP1 (offset 12)
224
    dq 0            ; RSP2 (offset 20)
225
    dq 0            ; Reserved (offset 28)
226
    dq 0            ; IST1 (offset 36)
227
    dq 0            ; IST2 (offset 44)
228
    dq 0            ; IST3 (offset 52)
229
    dq 0            ; IST4 (offset 60)
230
    dq 0            ; IST5 (offset 68)
231
    dq 0            ; IST6 (offset 76)
232
    dq 0            ; IST7 (offset 84)
233
    dq 0            ; Reserved (offset 92)
234
    dw 0            ; Reserved (offset 100)
235
    dw 0            ; I/O Map Base (offset 102) — no I/O bitmap
236
tss_struct_end:
237
238
; ============================================================================
239
; 64-bit Long Mode Entry
240
; ============================================================================
241
section .text
242
bits 64
243
long_mode_entry:
244
    ; === Fill TSS descriptor in the full GDT ===
245
    ; TSS descriptor is at GDT index 5, offset 40 (5*8)
246
    ; Format: Limit[15:0], Base[15:0], Base[23:16], Access, Flags|Limit[19:16],
247
    ;         Base[31:24], Base[63:32], Reserved
248
249
    lea rax, [rel tss_struct]
250
    mov word [rel gdt64_full + 40], (tss_struct_end - tss_struct - 1) ; Limit
251
    mov word [rel gdt64_full + 42], ax                                 ; Base[15:0]
252
    shr rax, 16
253
    mov byte [rel gdt64_full + 44], al                                 ; Base[23:16]
254
    mov byte [rel gdt64_full + 45], 0x89                               ; Access: P=1, DPL=0, Type=9
255
    mov byte [rel gdt64_full + 46], 0x00                               ; Flags + Limit[19:16]
256
    shr rax, 8
257
    mov byte [rel gdt64_full + 47], al                                 ; Base[31:24]
258
    lea rax, [rel tss_struct]
259
    shr rax, 32
260
    mov dword [rel gdt64_full + 48], eax                               ; Base[63:32]
261
    mov dword [rel gdt64_full + 52], 0                                 ; Reserved
262
263
    ; === Load the full GDT ===
264
    lgdt [rel gdt64_full_ptr]
265
266
    ; === Reload CS with a far return ===
267
    push qword CODE64_SEL
268
    lea rax, [rel .cs_reload]
269
    push rax
270
    o64 retf
271
.cs_reload:
272
273
    ; === Set up 64-bit data segments ===
274
    mov ax, DATA64_SEL
275
    mov ds, ax
276
    mov es, ax
277
    mov fs, ax
278
    mov gs, ax
279
    mov ss, ax
280
281
    ; === Set up kernel stack (16KB) ===
282
    mov rsp, kernel_stack_top
283
284
    ; === Set RSP0 in TSS (kernel stack for SYSCALL entry from ring 3) ===
285
    mov qword [rel tss_struct + 4], kernel_stack_top
286
287
    ; === Load TSS ===
288
    mov ax, TSS_SEL
289
    ltr ax
290
291
    ; Zero all general-purpose registers
292
    xor rax, rax
293
    xor rbx, rbx
294
    xor rcx, rcx
295
    xor rdx, rdx
296
    xor rsi, rsi
297
    xor rdi, rdi
298
    xor rbp, rbp
299
    xor r8, r8
300
    xor r9, r9
301
    xor r10, r10
302
    xor r11, r11
303
    xor r12, r12
304
    xor r13, r13
305
    xor r14, r14
306
    xor r15, r15
307
308
    ; Print '6' to serial to confirm 64-bit mode
309
    mov al, '6'
310
    mov dx, COM1
311
    out dx, al
312
313
    ; Pass multiboot info to kernel
314
    mov rdi, [mb2_info_ptr]
315
    mov rsi, [mb2_magic_ptr]
316
    call kernel_main_64
317
318
    ; If kernel returns, halt
319
    cli
320
    hlt
321
    jmp $
322
323
; === Kernel Stack (16KB) ===
324
section .bss
325
align 16
326
global kernel_stack_top
327
kernel_stack_bottom:
328
    resb 16384              ; 16KB stack
329
kernel_stack_top:
330
331