xref: /haiku/src/add-ons/kernel/cpu/x86/generic_x86.cpp (revision 3904a8dba0df1065db019e58a491c712cdf9cd83)
1 /*
2  * Copyright 2005-2009, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Axel Dörfler, axeld@pinc-software.de
7  *		Ingo Weinhold, ingo_weinhold@gmx.de
8  */
9 
10 
11 #include "generic_x86.h"
12 #include "intel.h"
13 #include "amd.h"
14 #include "via.h"
15 
16 #include <KernelExport.h>
17 #include <arch_system_info.h>
18 #include <arch/x86/arch_cpu.h>
19 #include <smp.h>
20 
21 
22 //#define TRACE_MTRR
23 #ifdef TRACE_MTRR
24 #	define TRACE(x...)	dprintf("mtrr: "x)
25 #else
26 #	define TRACE(x...)	/* nothing */
27 #endif
28 
29 
30 #define IA32_MTRR_ENABLE				(1UL << 11)
31 #define IA32_MTRR_ENABLE_FIXED			(1UL << 10)
32 #define IA32_MTRR_VALID_RANGE			(1UL << 11)
33 
34 
35 struct mtrr_capabilities {
36 	mtrr_capabilities(uint64 value) { *(uint64 *)this = value; }
37 
38 	uint64	variable_ranges : 8;
39 	uint64	supports_fixed : 1;
40 	uint64	_reserved0 : 1;
41 	uint64	supports_write_combined : 1;
42 	uint64	_reserved1 : 53;
43 };
44 
45 
46 uint64 gPhysicalMask = 0;
47 
48 
49 #ifdef TRACE_MTRR
50 static const char *
51 mtrr_type_to_string(uint8 type)
52 {
53 	switch (type) {
54 		case 0:
55 			return "uncacheable";
56 		case 1:
57 			return "write combining";
58 		case 4:
59 			return "write-through";
60 		case 5:
61 			return "write-protected";
62 		case 6:
63 			return "write-back";
64 		default:
65 			return "reserved";
66 	}
67 }
68 #endif // TRACE_MTRR
69 
70 
71 static void
72 set_mtrr(uint32 index, uint64 base, uint64 length, uint8 type)
73 {
74 	uint64 mask = length - 1;
75 	mask = ~mask & gPhysicalMask;
76 
77 	TRACE("MTRR %lu: new mask %Lx\n", index, mask);
78 	TRACE("  mask test base: %Lx\n", mask & base);
79 	TRACE("  mask test middle: %Lx\n", mask & (base + length / 2));
80 	TRACE("  mask test end: %Lx\n", mask & (base + length));
81 
82 	index *= 2;
83 		// there are two registers per slot
84 
85 	// First, disable MTRR
86 
87 	x86_write_msr(IA32_MSR_MTRR_PHYSICAL_MASK_0 + index, 0);
88 
89 	if (base != 0 || mask != 0 || type != 0) {
90 		// then fill in the new values, and enable it again
91 
92 		x86_write_msr(IA32_MSR_MTRR_PHYSICAL_BASE_0 + index,
93 			(base & ~(B_PAGE_SIZE - 1)) | type);
94 		x86_write_msr(IA32_MSR_MTRR_PHYSICAL_MASK_0 + index,
95 			mask | IA32_MTRR_VALID_RANGE);
96 	} else {
97 		// reset base as well
98 		x86_write_msr(IA32_MSR_MTRR_PHYSICAL_BASE_0 + index, 0);
99 	}
100 }
101 
102 
103 // #pragma mark -
104 
105 
106 uint32
107 generic_count_mtrrs(void)
108 {
109 	if (!x86_check_feature(IA32_FEATURE_MTRR, FEATURE_COMMON)
110 		|| !x86_check_feature(IA32_FEATURE_MSR, FEATURE_COMMON))
111 		return 0;
112 
113 	mtrr_capabilities capabilities(x86_read_msr(IA32_MSR_MTRR_CAPABILITIES));
114 	TRACE("CPU %ld has %u variable range MTRRs.\n", smp_get_current_cpu(),
115 		(uint8)capabilities.variable_ranges);
116 	return capabilities.variable_ranges;
117 }
118 
119 
120 void
121 generic_init_mtrrs(uint32 count)
122 {
123 	if (count == 0)
124 		return;
125 
126 	// If MTRRs are enabled, we leave everything as is (save for, possibly, the
127 	// default, which we set below), so that we can benefit from the BIOS's
128 	// setup until we've installed our own. If MTRRs are disabled, we clear
129 	// all registers and enable MTRRs.
130 	// (we leave the fixed MTRRs as is)
131 	// TODO: check if the fixed MTRRs are set on all CPUs identically?
132 	TRACE("generic_init_mtrrs(count = %ld)\n", count);
133 
134 	uint64 defaultType = x86_read_msr(IA32_MSR_MTRR_DEFAULT_TYPE);
135 	if ((defaultType & IA32_MTRR_ENABLE) == 0) {
136 		for (uint32 i = 0; i < count; i++)
137 			set_mtrr(i, 0, 0, 0);
138 	}
139 
140 	// Turn on variable MTRR functionality.
141 	// We need to ensure that the default type is uncacheable, otherwise
142 	// clearing the mtrrs could result in ranges that aren't supposed to be
143 	// cacheable to become cacheable due to the default type.
144 	x86_write_msr(IA32_MSR_MTRR_DEFAULT_TYPE,
145 		(defaultType & ~0xff) | IA32_MTRR_ENABLE);
146 }
147 
148 
149 void
150 generic_set_mtrr(uint32 index, uint64 base, uint64 length, uint8 type)
151 {
152 	set_mtrr(index, base, length, type);
153 	TRACE("[cpu %ld] mtrrs now:\n", smp_get_current_cpu());
154 	generic_dump_mtrrs(generic_count_mtrrs());
155 }
156 
157 
158 status_t
159 generic_get_mtrr(uint32 index, uint64 *_base, uint64 *_length, uint8 *_type)
160 {
161 	uint64 mask = x86_read_msr(IA32_MSR_MTRR_PHYSICAL_MASK_0 + index * 2);
162 	if ((mask & IA32_MTRR_VALID_RANGE) == 0)
163 		return B_ERROR;
164 
165 	uint64 base = x86_read_msr(IA32_MSR_MTRR_PHYSICAL_BASE_0 + index * 2);
166 
167 	*_base = base & ~(B_PAGE_SIZE - 1);
168 	*_length = (~mask & gPhysicalMask) + B_PAGE_SIZE;
169 	*_type = base & 0xff;
170 
171 	return B_OK;
172 }
173 
174 
175 void
176 generic_set_mtrrs(uint8 newDefaultType, const x86_mtrr_info* infos,
177 	uint32 count, uint32 maxCount)
178 {
179 	// check count
180 	if (maxCount == 0)
181 		return;
182 
183 	if (count > maxCount)
184 		count = maxCount;
185 
186 	// disable MTTRs
187 	uint64 defaultType = x86_read_msr(IA32_MSR_MTRR_DEFAULT_TYPE)
188 		& ~IA32_MTRR_ENABLE;
189 	x86_write_msr(IA32_MSR_MTRR_DEFAULT_TYPE, defaultType);
190 
191 	// set the given MTRRs
192 	for (uint32 i = 0; i < count; i++)
193 		set_mtrr(i, infos[i].base, infos[i].size, infos[i].type);
194 
195 	// clear the other MTRRs
196 	for (uint32 i = count; i < maxCount; i++)
197 		set_mtrr(i, 0, 0, 0);
198 
199 	// re-enable MTTRs and set the new default type
200 	defaultType = (defaultType & ~(uint64)0xff) | newDefaultType;
201 	x86_write_msr(IA32_MSR_MTRR_DEFAULT_TYPE, defaultType | IA32_MTRR_ENABLE);
202 }
203 
204 
205 status_t
206 generic_mtrr_compute_physical_mask(void)
207 {
208 	uint32 bits = 36;
209 
210 	cpuid_info cpuInfo;
211 	if (get_current_cpuid(&cpuInfo, 0x80000000) == B_OK
212 		&& (cpuInfo.eax_0.max_eax & 0xff) >= 8) {
213 		get_current_cpuid(&cpuInfo, 0x80000008);
214 		bits = cpuInfo.regs.eax & 0xff;
215 
216 		// Obviously, the bits are not always reported correctly
217 		if (bits < 36)
218 			bits = 36;
219 	}
220 
221 	gPhysicalMask = ((1ULL << bits) - 1) & ~(B_PAGE_SIZE - 1);
222 
223 	TRACE("CPU %ld has %ld physical address bits, physical mask is %016Lx\n",
224 		smp_get_current_cpu(), bits, gPhysicalMask);
225 
226 	return B_OK;
227 }
228 
229 
230 void
231 generic_dump_mtrrs(uint32 count)
232 {
233 #ifdef TRACE_MTRR
234 	if (count == 0)
235 		return;
236 
237 	int cpu = smp_get_current_cpu();
238 	uint64 defaultType = x86_read_msr(IA32_MSR_MTRR_DEFAULT_TYPE);
239 	TRACE("[cpu %d] MTRRs are %sabled\n", cpu,
240 		(defaultType & IA32_MTRR_ENABLE) != 0 ? "en" : "dis");
241 	TRACE("[cpu %d] default type is %u %s\n", cpu,
242 		(uint8)defaultType, mtrr_type_to_string(defaultType));
243 	TRACE("[cpu %d] fixed range MTRRs are %sabled\n", cpu,
244 		(defaultType & IA32_MTRR_ENABLE_FIXED) != 0 ? "en" : "dis");
245 
246 	for (uint32 i = 0; i < count; i++) {
247 		uint64 base;
248 		uint64 length;
249 		uint8 type;
250 		if (generic_get_mtrr(i, &base, &length, &type) == B_OK) {
251 			TRACE("[cpu %d] %lu: base: 0x%Lx; length: 0x%Lx; type: %u %s\n",
252 				cpu, i, base, length, type, mtrr_type_to_string(type));
253 		} else
254 			TRACE("[cpu %d] %lu: empty\n", cpu, i);
255 	}
256 #endif // TRACE_MTRR
257 }
258 
259 
260 module_info *modules[] = {
261 	(module_info *)&gIntelModule,
262 	(module_info *)&gAMDModule,
263 	(module_info *)&gVIAModule,
264 	NULL
265 };
266