~eliasnaur/unik

ref: cd0f505f7c2c06bac9c3655ab38b7bbe2d1761f3 unik/kernel/kernel_amd64.go -rw-r--r-- 5.2 KiB
cd0f505fElias Naur initial import 6 months ago
                                                                                
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
// SPDX-License-Identifier: Unlicense OR MIT

package kernel

import (
	"encoding/binary"
	"unsafe"
)

// kernError is an error type usable in kernel code.
type kernError string

const (
	_MSR_IA32_EFER = 0xc0000080
	_MSR_FS_BASE   = 0xc0000100

	_EFER_SCE = 1 << 0  // Enable SYSCALL.
	_EFER_NXE = 1 << 11 // Enable no-execute page bit.

	_XCR0_FPU = 1 << 0
	_XCR0_SSE = 1 << 1
	_XCR0_AVX = 1 << 2

	_CR4_DE         = 1 << 3
	_CR4_PSE        = 1 << 4
	_CR4_PAE        = 1 << 5
	_CR4_FXSTOR     = 1 << 9
	_CR4_OSXMMEXCPT = 1 << 10
	_CR4_FSGSBASE   = 1 << 16
	_CR4_OSXSAVE    = 1 << 18
)

type stack [10 * pageSize]byte

var (
	// Kernel stack.
	kstack stack

	fpuContextSize uint64
	kstackTop      uint64
)

//go:nosplit
func kernelStackTop() uint64 {
	return uint64(kstack.top())
}

//go:nosplit
func runKernel(mmapSize, descSize, kernelImageSize uint64, mmapAddr, kernelImage *byte) {
	mmap := (*(*[1 << 30]byte)(unsafe.Pointer(mmapAddr)))[:mmapSize:mmapSize]
	img := (*(*[1 << 30]byte)(unsafe.Pointer(kernelImage)))[:kernelImageSize:kernelImageSize]
	if err := initKernel(descSize, mmap, img); err != nil {
		fatalError(err)
	}
	if err := runGo(); err != nil {
		fatalError(err)
	}
	fatal("runKernel: runGo returned")
}

//go:nosplit
func initKernel(descSize uint64, mmap, kernelImage []byte) error {
	kstackTop = kernelStackTop()
	efiMap := efiMemoryMap{mmap: mmap, stride: int(descSize)}
	setCR4Reg(_CR4_PAE | _CR4_PSE | _CR4_DE | _CR4_FXSTOR | _CR4_OSXMMEXCPT)
	loadGDT()
	kernelThread.self = &kernelThread
	thread0.self = &thread0
	thread0.makeCurrent()
	if err := initMemory(efiMap, kernelImage); err != nil {
		return err
	}
	if err := initAPIC(); err != nil {
		return err
	}
	initSYSCALL()
	if err := initVDSO(); err != nil {
		return err
	}
	if err := initThreads(); err != nil {
		return err
	}
	if err := initClock(); err != nil {
		return err
	}
	return nil
}

//go:nosplit
func runGo() error {
	// Allocate initial stack.
	ssize := uint64(unsafe.Sizeof(stack{}))
	addr, err := globalMap.mmap(0, ssize, pageFlagNX|pageFlagWritable|pageFlagUserAccess)
	if err != nil {
		return err
	}
	stack := (*stack)(unsafe.Pointer(addr))

	// Allocate initial thread.
	t, err := globalThreads.newThread()
	if err != nil {
		return err
	}
	t.sp = uint64(stack.top())
	t.makeCurrent()

	// Set up sane initial state, in particular the MXCSR flags.
	saveThread()
	// Prepare program environment on stack.
	const envSize = 256
	setupEnv(stack[len(stack)-envSize:])
	t.sp -= envSize
	t.flags = _FLAG_RESERVED | _FLAG_IF
	// Jump to Go runtime start.
	t.ip = uint64(funcPC(jumpToGo))
	resumeThread()
	return nil
}

// setupEnv sets up the argv, auxv and env on the stack, mimicing
// the Linux kernel.
//go:nosplit
func setupEnv(stack []byte) {
	args := stack
	bo := binary.LittleEndian
	bo.PutUint64(args, 1) // 1 argument, the process name.
	args = args[8:]
	// First argument, address of binary name.
	binAddr := args[:8]
	args = args[8:]
	bo.PutUint64(args, 0) // NULL separator.
	args = args[8:]
	bo.PutUint64(args, 0) // No envp.
	args = args[8:]
	// Build auxillary vector.
	// Page size.
	bo.PutUint64(args, _AT_PAGESZ)
	args = args[8:]
	bo.PutUint64(args, pageSize)
	args = args[8:]
	// End of auxv.
	bo.PutUint64(args, _AT_NULL)
	args = args[8:]
	bo.PutUint64(args, 0)
	// Binary name.
	bo.PutUint64(binAddr, uint64(uintptr(unsafe.Pointer(&args[0]))))
	n := copy(args, []byte("kernel\x00"))
	args = args[n:]
}

//go:nosplit
func funcPC(f func()) uintptr {
	return **(**uintptr)(unsafe.Pointer(&f))
}

//go:nosplit
func wrmsr(register uint32, value uint64) {
	wrmsr0(register, uint32(value), uint32(value>>32))
}

//go:nosplit
func rdmsr(register uint32) uint64 {
	lo, hi := rdmsr0(register)
	return uint64(hi)<<32 | uint64(lo)
}

//go:nosplit
func fatalError(err error) {
	// The only error type supported is kernError,
	// but we can't call its Error method directly,
	// because the compiler wrapper is not nosplit.
	switch err := err.(type) {
	case kernError:
		fatal(err.Error())
	default:
		fatal("unsupported error")
	}
}

//go:nosplit
func fatal(msg string) {
	outputString("fatal error: ")
	outputString(msg)
	outputString("\n")
	halt()
}

// cpuidMaxExt returns the highest supported CPUID extended
// function number.
//go:nosplit
func cpuidMaxExt() uint32 {
	eax, _, _, _ := cpuid(0x80000000, 0)
	return eax
}

//go:nosplit
func hasInvariantTSC() bool {
	maxExt := cpuidMaxExt()
	if maxExt < 0x80000007 {
		return false
	}
	_, _, _, edx := cpuid(0x80000007, 0)
	return edx&(1<<8) != 0
}

//go:nosplit
func (s *stack) slice() []byte {
	stackTop := uintptr(unsafe.Pointer(&s[0])) + unsafe.Sizeof(*s)
	// Align to 16 bytes.
	alignment := int(stackTop & 0xf)
	return s[:len(s)-alignment]
}

//go:nosplit
func (s *stack) top() virtualAddress {
	stackTop := uintptr(unsafe.Pointer(&s[0])) + unsafe.Sizeof(*s)
	// Align to 16 bytes.
	stackTop = stackTop &^ 0xf
	return virtualAddress(stackTop)
}

func wrmsr0(register, lo, hi uint32)
func rdmsr0(register uint32) (lo, hi uint32)
func halt()
func cpuid(function, sub uint32) (eax, ebx, ecx, edx uint32)
func jumpToGo()
func fninit()
func setCR4Reg(flags uint64)
func outb(port uint16, b uint8)
func inb(port uint16) uint8
func outl(port uint16, b uint32)
func inl(port uint16) uint32

//go:nosplit
func (k kernError) Error() string {
	return string(k)
}