~eliasnaur/unik

ref: cd0f505f7c2c06bac9c3655ab38b7bbe2d1761f3 unik/kernel/interrupt_amd64.go -rw-r--r-- 4.4 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
// SPDX-License-Identifier: Unlicense OR MIT

package kernel

import (
	"sync/atomic"
	"unsafe"
)

const (
	_IA32_APIC_BASE = 0x1b
)

const (
	intDivideError            intVector = 0x0
	intGeneralProtectionFault intVector = 0xd
	intPageFault              intVector = 0xe
	intSSE                    intVector = 0x13

	apic
)

const (
	firstAvailableInterrupt intVector = 0x20 + iota
	intAPICError
	intTimer
	intFirstUser

	intLastUser           = intFirstUser + 10
	intSpurious intVector = 0xff
)

var pendingInterrupts [intLastUser - intFirstUser]bool

var (
	apicBase virtualAddress
	// Interrupt handlers write 0 to apicEOI to
	// signal end of interrupt handling.
	apicEOI *uint32
)

//go:nosplit
func initAPIC() error {
	_, _, _, edx := cpuid(0x1, 0)
	if edx&(1<<9) == 0 {
		return kernError("initAPIC: no APIC available")
	}
	maskPIC()
	apicBaseMSR := rdmsr(_IA32_APIC_BASE)
	if apicBaseMSR&(1<<8) == 0 {
		return kernError("initAPIC: not running on the boot CPU")
	}
	apicBase = virtualAddress(apicBaseMSR &^ 0xfff)
	// Enable APIC.
	wrmsr(_IA32_APIC_BASE, apicBaseMSR|1<<11)

	apicEOI = (*uint32)(unsafe.Pointer(apicBase + 0xb0))

	// Map the APIC page.
	flags := pageFlagWritable | pageFlagNX | pageFlagNoCache
	globalMap.mustAddRange(apicBase, apicBase+pageSize, flags)
	if err := mmapAligned(&globalMem, globalPT, apicBase, apicBase+pageSize, physicalAddress(apicBase), flags); err != nil {
		return err
	}

	globalIDT.install(intDivideError, ring0, istGeneric, divFault)
	globalIDT.install(intGeneralProtectionFault, ring0, istGeneric, generalProtectionFaultTrampoline)
	globalIDT.install(intSSE, ring0, istGeneric, sseException)
	globalIDT.install(intPageFault, ring0, istPageFault, pageFaultTrampoline)

	globalIDT.install(intAPICError, ring0, istGeneric, unknownInterruptTrampoline)
	globalIDT.install(intSpurious, ring0, istGeneric, unknownInterruptTrampoline)
	installUserHandlers()

	reloadIDT()
	// Mask LINT0-1.
	const apicIntMasked = 1 << 17
	apicWrite(0x350 /* LVT LINT0*/, apicIntMasked)
	apicWrite(0x360 /* LVT LINT1*/, apicIntMasked)
	// Setup error interrupt.
	apicWrite(0x370 /* LVT Error*/, uint32(intAPICError))
	// Setup spurious interrupt handler and enable interrupts.
	apicWrite(0x0f0, 0x100|uint32(intSpurious))

	return nil
}

//go:nosplit
func divFault() {
	fatal("division by 0")
}

//go:nosplit
func gpFault(addr uint64) {
	outputString("fault address: ")
	outputUint64(addr)
	outputString("\n")
	fatal("general protection fault")
}

//go:nosplit
func sseException() {
	fatal("SSE exception")
}

//go:nosplit
func installUserHandlers() {
	vector := intFirstUser
	globalIDT.install(vector, ring0, istGeneric, userInterruptTrampoline0)
	vector++
	globalIDT.install(vector, ring0, istGeneric, userInterruptTrampoline1)
	vector++
	globalIDT.install(vector, ring0, istGeneric, userInterruptTrampoline2)
	vector++
	globalIDT.install(vector, ring0, istGeneric, userInterruptTrampoline3)
	vector++
	globalIDT.install(vector, ring0, istGeneric, userInterruptTrampoline4)
	vector++
	globalIDT.install(vector, ring0, istGeneric, userInterruptTrampoline5)
	vector++
	globalIDT.install(vector, ring0, istGeneric, userInterruptTrampoline6)
	vector++
	globalIDT.install(vector, ring0, istGeneric, userInterruptTrampoline7)
	vector++
	globalIDT.install(vector, ring0, istGeneric, userInterruptTrampoline8)
	vector++
	globalIDT.install(vector, ring0, istGeneric, userInterruptTrampoline9)
	vector++
	if vector != intLastUser {
		fatal("not enough users interrupt handlers declared")
	}
}

//go:nosplit
func apicWrite(reg int, val uint32) {
	atomic.StoreUint32((*uint32)(unsafe.Pointer(apicBase+virtualAddress(reg))), val)
}

//go:nosplit
func apicRead(reg int) uint32 {
	return atomic.LoadUint32((*uint32)(unsafe.Pointer(apicBase + virtualAddress(reg))))
}

//go:nosplit
func maskPIC() {
	const (
		PIC1_DATA = 0x21
		PIC2_DATA = 0xa1
	)
	outb(PIC1_DATA, 0xff)
	outb(PIC2_DATA, 0xff)
}

//go:nosplit
func userInterrupt(vector uint64) {
	pendingInterrupts[vector] = true
}

//go:nosplit
func unknownInterrupt() {
	fatal("unexpected interrupt")
}

func unknownInterruptTrampoline()

func userInterruptTrampoline0()
func userInterruptTrampoline1()
func userInterruptTrampoline2()
func userInterruptTrampoline3()
func userInterruptTrampoline4()
func userInterruptTrampoline5()
func userInterruptTrampoline6()
func userInterruptTrampoline7()
func userInterruptTrampoline8()
func userInterruptTrampoline9()

func generalProtectionFaultTrampoline()