summaryrefslogtreecommitdiff
path: root/src/kernel/arch/amd64/32/boot.S
blob: 3f5798feb05064143e5d413a8d8409939551969c (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
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
.section .text
.global _start
.type _start, @function
_start:
	cli
	mov $_stack_top, %esp
	push %ebx // save the address of the multiboot struct

	mov $0x80000000, %eax // check CPUID extended functions
	cpuid
	cmp $0x80000001, %eax
	jb panic_early

	mov $0x80000001, %eax
	cpuid
	test $(1<<29), %edx // check long mode support
	jz panic_early

	mov %cr4, %eax
	or $(1<<5 | 1<<9 | 1<<10), %eax // PAE | SSE | SSE
	mov %eax, %cr4

	call pml4_identity_init
	mov $pml4_identity, %eax
	mov %eax, %cr3

	mov $0xC0000080, %ecx // EFER MSR
	rdmsr
	or $(1 | 1<<8 | 1<<11), %eax // syscall/ret | long mode | NX
	wrmsr

	mov %cr0, %eax
	or $0x80000002, %eax // enable paging, coprocessor monitoring
	and $(~4), %eax // disable coprocessor emulation
	mov %eax, %cr0

	/* fill out the TSS's address */
	movw $TSS, (GdtTss + 2)
	lgdt (GdtPointer)

	pop %edi

	mov $(2 << 3), %eax // SEG_r0data
	mov %eax, %ds
	mov %eax, %ss
	mov %eax, %es
	mov %eax, %fs
	mov %eax, %gs

	ljmp $(1 << 3), $boot64 // SEG_r0code

panic_early:
	// output a vga Fuck
	movl $0x4F754F46, 0xB872A
	movl $0x4F6B4F63, 0xB872E
	jmp cpu_halt

.global cpu_shutdown
.type cpu_shutdown, @function
cpu_shutdown:
/* This quits QEMU. While I couldn't find this officially documented anywhere,
 * it is used by QEMU in tests/tcg/i386/system/boot.S (as of commit 40d6ee), so
 * I assume that this is safe-ish to use */
	mov $0x604, %edx
	mov $0x2000, %eax
	outw %ax, %dx

.global cpu_halt
.type cpu_halt, @function
cpu_halt:
	cli
1:	hlt
	jmp 1b


.global cpu_pause
.type cpu_pause, @function
cpu_pause:
	sti
	hlt
	cli
	ret

.section .shared
.global GDT
.align 8
GDT:
#define GdtLimit(p)   (p & 0xFFFF) | ((p >> 16) << 48) /* doesn't check if p fits in 20 bits */
#define GdtAccessed   (1<<40)
#define GdtReadWrite  (1<<41)
#define GdtCode       (1<<43)
#define GdtCodeOrData (1<<44)
#define GdtRing(r)    (r<<45)
#define GdtPresent    (1<<47)
#define GdtAvailable  (1<<52)
#define GdtLongMode   (1<<53)
#define GdtPageGran   (1<<55) /* limit is in pages */
#define GdtCommon     (GdtLimit(0xFFFFF) | GdtPageGran | GdtAccessed | \
                       GdtReadWrite | GdtCodeOrData | GdtPresent | GdtAvailable)
.8byte 0                                              /* [0] = SEG_null */
.8byte GdtCommon | GdtRing(0) | GdtLongMode | GdtCode /* [1] = SEG_r0code */
.8byte GdtCommon | GdtRing(0) | GdtLongMode           /* [2] = SEG_r0data */
.8byte GdtCommon | GdtRing(3)               | GdtCode /* [3] = SEG_r3code32 */
.8byte GdtCommon | GdtRing(3) | GdtLongMode           /* [4] = SEG_r3data */
.8byte GdtCommon | GdtRing(3) | GdtLongMode | GdtCode /* [5] = SEG_r3code */
GdtTss:
.8byte GdtLimit(104) | (9<<40) | GdtPresent | GdtAvailable /* [6] = SEG_TSS */
.8byte 0                                                   /* [7] = SEG_TSS2 */

.section .data
.global GdtPointer
GdtPointer:
.2byte 63  /* size of the GDT - 1 */
.4byte GDT /* address of the GDT */