summaryrefslogtreecommitdiff
path: root/src/include/cpu/x86/lapic.h
blob: 0e1384fe7ed82ecef81d8721e497838bc820d346 (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
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
/* SPDX-License-Identifier: GPL-2.0-only */

#ifndef CPU_X86_LAPIC_H
#define CPU_X86_LAPIC_H

#include <arch/cpu.h>
#include <cpu/x86/lapic_def.h>
#include <cpu/x86/msr.h>
#include <device/mmio.h>
#include <halt.h>
#include <stdint.h>

static __always_inline uint32_t xapic_read(unsigned int reg)
{
	return read32((volatile void *)(uintptr_t)(LAPIC_DEFAULT_BASE + reg));
}

static __always_inline void xapic_write(unsigned int reg, uint32_t v)
{
	write32((volatile void *)(uintptr_t)(LAPIC_DEFAULT_BASE + reg), v);
}

static __always_inline void xapic_send_ipi(uint32_t icrlow, uint32_t icrhi)
{
	xapic_write(LAPIC_ICR2, icrhi);
	xapic_write(LAPIC_ICR, icrlow);
}

static __always_inline int xapic_busy(void)
{
	return xapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY;
}

static __always_inline uint32_t x2apic_read(unsigned int reg)
{
	uint32_t value, index;
	msr_t msr;

	index = X2APIC_MSR_BASE_ADDRESS + (uint32_t)(reg >> 4);
	msr = rdmsr(index);
	value = msr.lo;
	return value;
}

static __always_inline void x2apic_write(unsigned int reg, uint32_t v)
{
	uint32_t index;
	msr_t msr;

	index = X2APIC_MSR_BASE_ADDRESS + (uint32_t)(reg >> 4);
	msr.hi = 0x0;
	msr.lo = v;
	wrmsr(index, msr);
}

static __always_inline void x2apic_send_ipi(uint32_t icrlow, uint32_t icrhi)
{
	msr_t icr;
	icr.hi = icrhi;
	icr.lo = icrlow;
	wrmsr(X2APIC_MSR_ICR_ADDRESS, icr);
}

static __always_inline bool is_x2apic_mode(void)
{
	if (CONFIG(XAPIC_ONLY))
		return false;

	if (CONFIG(X2APIC_ONLY))
		return true;

	msr_t msr;
	msr = rdmsr(LAPIC_BASE_MSR);
	return ((msr.lo & LAPIC_BASE_X2APIC_ENABLED) == LAPIC_BASE_X2APIC_ENABLED);
}

static __always_inline uint32_t lapic_read(unsigned int reg)
{
	if (is_x2apic_mode())
		return x2apic_read(reg);
	else
		return xapic_read(reg);
}

static __always_inline void lapic_write(unsigned int reg, uint32_t v)
{
	if (is_x2apic_mode())
		x2apic_write(reg, v);
	else
		xapic_write(reg, v);
}

static __always_inline void lapic_update32(unsigned int reg, uint32_t mask, uint32_t or)
{
	if (is_x2apic_mode()) {
		uint32_t index;
		msr_t msr;
		index = X2APIC_MSR_BASE_ADDRESS + (uint32_t)(reg >> 4);
		msr = rdmsr(index);
		msr.lo &= mask;
		msr.lo |= or;
		wrmsr(index, msr);
	} else {
		uint32_t value;
		value = xapic_read(reg);
		value &= mask;
		value |= or;
		xapic_write(reg, value);
	}
}

static __always_inline void lapic_send_ipi(uint32_t icrlow, uint32_t apicid)
{
	if (is_x2apic_mode())
		x2apic_send_ipi(icrlow, apicid);
	else
		xapic_send_ipi(icrlow, SET_LAPIC_DEST_FIELD(apicid));
}

static __always_inline int lapic_busy(void)
{
	if (is_x2apic_mode())
		return 0;
	else
		return xapic_busy();
}

static __always_inline unsigned int initial_lapicid(void)
{
	uint32_t lapicid;
	if (is_x2apic_mode() && cpuid_get_max_func() >= 0xb)
		lapicid = cpuid_ext(0xb, 0).edx;
	else
		lapicid = cpuid_ebx(1) >> 24;
	return lapicid;
}

static __always_inline unsigned int lapicid(void)
{
	uint32_t lapicid = lapic_read(LAPIC_ID);

	/* check x2apic mode and return accordingly */
	if (!is_x2apic_mode())
		lapicid >>= 24;
	return lapicid;
}

static __always_inline void lapic_send_ipi_self(uint32_t icrlow)
{
	int i = 1000;

	/* LAPIC_DEST_SELF does not support all delivery mode -fields. */
	lapic_send_ipi(icrlow, lapicid());

	/* In case of X2APIC force a short delay, to prevent deadlock in a case
	 * the immediately following code acquires some lock, like with printk().
	 */
	const bool x2apic = is_x2apic_mode();

	while (x2apic && i--)
		cpu_relax();
}

static __always_inline void lapic_send_ipi_others(uint32_t icrlow)
{
	lapic_send_ipi(LAPIC_DEST_ALLBUT | icrlow, 0);
}

#if !CONFIG(AP_IN_SIPI_WAIT)
/* If we need to go back to sipi wait, we use the long non-inlined version of
 * this function in lapic_cpu_stop.c
 */
static __always_inline void stop_this_cpu(void)
{
	/* Called by an AP when it is ready to halt and wait for a new task */
	halt();
}
#else
void stop_this_cpu(void);
#endif

void enable_lapic(void);
void enable_lapic_mode(bool try_set_x2apic);
void setup_lapic_interrupts(void);

static inline unsigned int early_lapicid(void)
{
	if (!CONFIG(SMP))
		return 0;

	if (!ENV_RAMSTAGE)
		return 0;

	enable_lapic();
	return lapicid();
}

#endif /* CPU_X86_LAPIC_H */