win-pvdrivers

view xenpci/xenpci_patch_kernel.c @ 1054:471c94d04d8a

Refactoring to support xencache (tmem)
author James Harper <james.harper@bendigoit.com.au>
date Sun Jun 02 16:37:21 2013 +1000 (2013-06-02)
parents 4e6f162a054c
children 6f69b45af0fb
line source
1 /*
2 PV Drivers for Windows Xen HVM Domains
3 Copyright (C) 2007 James Harper
4 Inspired by amdvopt by Travis Betak
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
21 #include "xenpci.h"
23 #if defined(_X86_)
25 /* Is LOCK MOV CR0 available? */
26 #define CPUID_ALTMOVCR8 (1UL << 4)
27 /* Task priority register address */
28 #define LAPIC_TASKPRI 0xFFFE0080
29 #define TPR_BYTES 0x80, 0x00, 0xfe, 0xff
31 extern VOID MoveTprToEax(VOID);
32 extern VOID MoveTprToEcx(VOID);
33 extern VOID MoveTprToEdx(VOID);
34 extern VOID MoveTprToEsi(VOID);
35 extern VOID PushTpr(VOID);
36 extern VOID MoveEaxToTpr(VOID);
37 extern VOID MoveEbxToTpr(VOID);
38 extern VOID MoveEcxToTpr(VOID);
39 extern VOID MoveEdxToTpr(VOID);
40 extern VOID MoveEsiToTpr(VOID);
41 extern VOID MoveConstToTpr(ULONG new_tpr_value);
42 extern VOID MoveZeroToTpr(VOID);
44 static PHYSICAL_ADDRESS lapic_page[MAX_VIRT_CPUS];
45 static volatile PVOID lapic[MAX_VIRT_CPUS];
46 static ULONG tpr_cache[MAX_VIRT_CPUS];
47 #define PATCH_METHOD_LOCK_MOVE_CR0 1
48 #define PATCH_METHOD_MAPPED_VLAPIC 2
49 #define PATCH_METHOD_CACHED_TPR 3
50 static ULONG patch_method;
52 static ULONG
53 SaveTprProcValue(ULONG cpu, ULONG value)
54 {
55 switch (patch_method)
56 {
57 case PATCH_METHOD_LOCK_MOVE_CR0:
58 case PATCH_METHOD_CACHED_TPR:
59 tpr_cache[cpu] = value;
60 break;
61 case PATCH_METHOD_MAPPED_VLAPIC:
62 /* no need to save here */
63 break;
64 }
65 return value;
66 }
68 static ULONG
69 SaveTpr()
70 {
71 switch (patch_method)
72 {
73 case PATCH_METHOD_LOCK_MOVE_CR0:
74 case PATCH_METHOD_CACHED_TPR:
75 return SaveTprProcValue(KeGetCurrentProcessorNumber(), *(PULONG)LAPIC_TASKPRI);
76 case PATCH_METHOD_MAPPED_VLAPIC:
77 /* no need to save here */
78 break;
79 }
80 return 0;
81 }
83 /* called with interrupts disabled (via CLI) from an arbitrary location inside HAL.DLL */
84 static __inline LONG
85 ApicHighestVector(PULONG bitmap)
86 {
87 int i;
88 ULONG bit;
89 ULONG value;
90 for (i = 0; i < 8; i++)
91 {
92 value = bitmap[(7 - i) * 4];
93 if (value)
94 {
95 _BitScanReverse(&bit, value);
96 return ((7 - i) << 5) | bit;
97 }
98 }
99 return -1;
100 }
102 /* called with interrupts disabled (via CLI) from an arbitrary location inside HAL.DLL */
103 VOID
104 WriteTpr(ULONG new_tpr_value)
105 {
106 LONG ISR;
107 LONG IRR;
108 ULONG cpu = KeGetCurrentProcessorNumber();
110 switch (patch_method)
111 {
112 case PATCH_METHOD_LOCK_MOVE_CR0:
113 tpr_cache[cpu] = new_tpr_value;
114 __asm {
115 mov eax, new_tpr_value;
116 shr eax, 4;
117 lock mov cr0, eax; /* this is actually mov cr8, eax */
118 }
119 break;
120 case PATCH_METHOD_CACHED_TPR:
121 if (new_tpr_value != tpr_cache[cpu])
122 {
123 *(PULONG)LAPIC_TASKPRI = new_tpr_value;
124 tpr_cache[cpu] = new_tpr_value;
125 }
126 break;
127 case PATCH_METHOD_MAPPED_VLAPIC:
128 /* need to set the new tpr value and then check for pending interrupts to avoid a race */
129 *(PULONG)((PUCHAR)lapic[cpu] + 0x80) = new_tpr_value & 0xff;
130 KeMemoryBarrier();
131 IRR = ApicHighestVector((PULONG)((PUCHAR)lapic[cpu] + 0x200));
132 if (IRR == -1)
133 return;
134 ISR = ApicHighestVector((PULONG)((PUCHAR)lapic[cpu] + 0x100));
135 if (ISR == -1)
136 ISR = 0;
137 if ((ULONG)(IRR >> 4) > max((ULONG)(ISR >> 4), ((new_tpr_value & 0xf0) >> 4)))
138 *(PULONG)LAPIC_TASKPRI = new_tpr_value;
139 break;
140 }
141 }
143 /* called with interrupts disabled (via CLI) from an arbitrary location inside HAL.DLL */
144 ULONG
145 ReadTpr()
146 {
147 switch (patch_method)
148 {
149 case PATCH_METHOD_LOCK_MOVE_CR0:
150 case PATCH_METHOD_CACHED_TPR:
151 return tpr_cache[KeGetCurrentProcessorNumber()];
152 case PATCH_METHOD_MAPPED_VLAPIC:
153 return *(PULONG)((PUCHAR)lapic[KeGetCurrentProcessorNumber()] + 0x80);
154 default:
155 return 0;
156 }
157 }
159 static __inline VOID
160 InsertCallRel32(PUCHAR address, ULONG target)
161 {
162 *address = 0xE8; /* call near */
163 *(PULONG)(address + 1) = (ULONG)target - ((ULONG)address + 5);
164 }
166 #define PATCH_SIZE 10
168 typedef struct {
169 ULONG patch_type;
170 ULONG match_size;
171 ULONG function;
172 UCHAR bytes[PATCH_SIZE];
173 } patch_t;
175 #define PATCH_NONE 0
176 #define PATCH_1B4 1 /* 1 byte opcode with 4 bytes of data - replace with call function */
177 #define PATCH_2B4 2 /* 2 byte opcode with 4 bytes of data - replace with nop + call function*/
178 #define PATCH_2B5 3 /* 2 byte opcode with 1 + 4 bytes of data - replace with nop + nop + call function */
179 #define PATCH_2B8 4 /* 2 byte opcode with 4 + 4 bytes of data - replace with push const + call function*/
181 static patch_t patches[] =
182 {
183 { PATCH_1B4, 5, (ULONG)MoveTprToEax, { 0xa1, TPR_BYTES } },
184 { PATCH_2B4, 6, (ULONG)MoveTprToEcx, { 0x8b, 0x0d, TPR_BYTES } },
185 { PATCH_2B4, 6, (ULONG)MoveTprToEdx, { 0x8b, 0x15, TPR_BYTES } },
186 { PATCH_2B4, 6, (ULONG)MoveTprToEsi, { 0x8b, 0x35, TPR_BYTES } },
187 { PATCH_2B4, 6, (ULONG)PushTpr, { 0xff, 0x35, TPR_BYTES } },
188 { PATCH_1B4, 5, (ULONG)MoveEaxToTpr, { 0xa3, TPR_BYTES } },
189 { PATCH_2B4, 6, (ULONG)MoveEbxToTpr, { 0x89, 0x1D, TPR_BYTES } },
190 { PATCH_2B4, 6, (ULONG)MoveEcxToTpr, { 0x89, 0x0D, TPR_BYTES } },
191 { PATCH_2B4, 6, (ULONG)MoveEdxToTpr, { 0x89, 0x15, TPR_BYTES } },
192 { PATCH_2B4, 6, (ULONG)MoveEsiToTpr, { 0x89, 0x35, TPR_BYTES } },
193 { PATCH_2B8, 6, (ULONG)MoveConstToTpr, { 0xC7, 0x05, TPR_BYTES } }, /* + another 4 bytes of const */
194 { PATCH_2B5, 7, (ULONG)MoveZeroToTpr, { 0x83, 0x25, TPR_BYTES, 0 } },
195 { PATCH_NONE, 0, 0, { 0 } }
196 };
198 static BOOLEAN
199 XenPci_TestAndPatchInstruction(PVOID address)
200 {
201 PUCHAR instruction = address;
202 ULONG i;
203 /* don't declare patches[] on the stack - windows gets grumpy if we allocate too much space on the stack at HIGH_LEVEL */
205 for (i = 0; patches[i].patch_type != PATCH_NONE; i++)
206 {
207 if (memcmp(address, patches[i].bytes, patches[i].match_size) == 0)
208 break;
209 }
211 switch (patches[i].patch_type)
212 {
213 case PATCH_1B4:
214 InsertCallRel32(instruction + 0, patches[i].function);
215 break;
216 case PATCH_2B4:
217 *(instruction + 0) = 0x90; /* nop */
218 InsertCallRel32(instruction + 1, patches[i].function);
219 break;
220 case PATCH_2B8:
221 *(instruction + 0) = 0x68; /* push value */
222 *(PULONG)(instruction + 1) = *(PULONG)(instruction + 6);
223 InsertCallRel32(instruction + 5, patches[i].function);
224 break;
225 case PATCH_2B5:
226 *(instruction + 0) = 0x90; /* nop */
227 *(instruction + 1) = 0x90; /* nop */
228 InsertCallRel32(instruction + 2, patches[i].function);
229 break;
230 default:
231 return FALSE;
232 }
233 return TRUE;
234 }
236 typedef struct {
237 PVOID base;
238 ULONG length;
239 } patch_info_t;
241 static PVOID patch_positions[256];
242 static PVOID potential_patch_positions[256];
244 static VOID
245 XenPci_DoPatchKernel0(PVOID context)
246 {
247 patch_info_t *pi = context;
248 ULONG i;
249 ULONG high_level_tpr;
250 ULONG patch_position_index = 0;
251 ULONG potential_patch_position_index = 0;
253 FUNCTION_ENTER();
255 high_level_tpr = SaveTpr();
256 /* we know all the other CPUs are at HIGH_LEVEL so set them all to the same as cpu 0 */
257 for (i = 1; i < MAX_VIRT_CPUS; i++)
258 SaveTprProcValue(i, high_level_tpr);
260 /* we can't use KdPrint while patching as it may involve the TPR while we are patching it */
261 for (i = 0; i < pi->length; i++)
262 {
263 if (XenPci_TestAndPatchInstruction((PUCHAR)pi->base + i))
264 {
265 patch_positions[patch_position_index++] = (PUCHAR)pi->base + i;
266 }
267 else if (*(PULONG)((PUCHAR)pi->base + i) == LAPIC_TASKPRI)
268 {
269 potential_patch_positions[potential_patch_position_index++] = (PUCHAR)pi->base + i;
270 }
271 }
273 for (i = 0; i < patch_position_index; i++)
274 KdPrint((__DRIVER_NAME " Patch added at %p\n", patch_positions[i]));
276 for (i = 0; i < potential_patch_position_index; i++)
277 KdPrint((__DRIVER_NAME " Unpatch TPR address found at %p\n", potential_patch_positions[i]));
279 FUNCTION_EXIT();
280 }
282 static VOID
283 XenPci_DoPatchKernelN(PVOID context)
284 {
285 UNREFERENCED_PARAMETER(context);
287 FUNCTION_ENTER();
289 FUNCTION_EXIT();
290 }
292 static BOOLEAN
293 IsMoveCr8Supported()
294 {
295 DWORD32 cpuid_output[4];
297 __cpuid(cpuid_output, 0x80000001UL);
298 if (cpuid_output[2] & CPUID_ALTMOVCR8)
299 return TRUE;
300 else
301 return FALSE;
302 }
304 static ULONG
305 MapVlapic(PXENPCI_DEVICE_DATA xpdd)
306 {
307 struct xen_add_to_physmap xatp;
308 ULONG rc = EINVAL;
309 ULONG ActiveProcessorCount;
310 int i;
312 FUNCTION_ENTER();
314 #if (NTDDI_VERSION >= NTDDI_WINXP)
315 ActiveProcessorCount = (ULONG)KeNumberProcessors;
316 #else
317 ActiveProcessorCount = (ULONG)*KeNumberProcessors;
318 #endif
320 for (i = 0; i < (int)ActiveProcessorCount; i++)
321 {
322 KdPrint((__DRIVER_NAME " mapping lapic for cpu = %d\n", i));
324 lapic_page[i] = XenPci_AllocMMIO(xpdd, PAGE_SIZE);
325 lapic[i] = MmMapIoSpace(lapic_page[i], PAGE_SIZE, MmCached);
327 xatp.domid = DOMID_SELF;
328 xatp.idx = i;
329 xatp.space = XENMAPSPACE_vlapic;
330 xatp.gpfn = (xen_pfn_t)(lapic_page[i].QuadPart >> PAGE_SHIFT);
331 KdPrint((__DRIVER_NAME " gpfn = %x\n", xatp.gpfn));
332 rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp);
333 KdPrint((__DRIVER_NAME " hypervisor memory op (XENMAPSPACE_vlapic_regs) ret = %d\n", rc));
334 if (rc != 0)
335 {
336 FUNCTION_EXIT();
337 return rc;
338 }
339 }
340 FUNCTION_EXIT();
342 return rc;
343 }
345 VOID
346 XenPci_PatchKernel(PXENPCI_DEVICE_DATA xpdd, PVOID base, ULONG length)
347 {
348 patch_info_t patch_info;
349 ULONG rc;
350 #if (NTDDI_VERSION >= NTDDI_WINXP)
351 RTL_OSVERSIONINFOEXW version_info;
352 #endif
354 FUNCTION_ENTER();
356 /* if we're compiled for 2000 then assume we need patching */
357 #if (NTDDI_VERSION >= NTDDI_WINXP)
358 version_info.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
360 RtlGetVersion((PRTL_OSVERSIONINFOW)&version_info);
361 if (version_info.dwMajorVersion >= 6)
362 {
363 KdPrint((__DRIVER_NAME " Vista or newer - no need for patch\n"));
364 return;
365 }
366 if (version_info.dwMajorVersion == 5
367 && version_info.dwMinorVersion > 2)
368 {
369 KdPrint((__DRIVER_NAME " Windows 2003 sp2 or newer - no need for patch\n"));
370 return;
371 }
372 if (version_info.dwMajorVersion == 5
373 && version_info.dwMinorVersion == 2
374 && version_info.wServicePackMajor >= 2)
375 {
376 KdPrint((__DRIVER_NAME " Windows 2003 sp2 or newer - no need for patch\n"));
377 return;
378 }
379 #endif
380 if (IsMoveCr8Supported())
381 {
382 KdPrint((__DRIVER_NAME " Using LOCK MOVE CR0 TPR patch\n"));
383 patch_method = PATCH_METHOD_LOCK_MOVE_CR0;
384 }
385 else
386 {
387 rc = MapVlapic(xpdd);
388 if (rc == EACCES)
389 {
390 KdPrint((__DRIVER_NAME " Xen already using VMX LAPIC acceleration. No patch required\n"));
391 return;
392 }
393 if (!rc)
394 {
395 KdPrint((__DRIVER_NAME " Using mapped vLAPIC TPR patch\n"));
396 patch_method = PATCH_METHOD_MAPPED_VLAPIC;
397 }
398 else
399 {
400 KdPrint((__DRIVER_NAME " Using cached TPR patch\n"));
401 patch_method = PATCH_METHOD_CACHED_TPR;
402 }
403 }
404 patch_info.base = base;
405 patch_info.length = length;
407 XenPci_HighSync(XenPci_DoPatchKernel0, XenPci_DoPatchKernelN, &patch_info);
409 xpdd->removable = FALSE;
411 FUNCTION_EXIT();
412 }
414 #else
416 VOID
417 XenPci_PatchKernel(PXENPCI_DEVICE_DATA xpdd, PVOID base, ULONG length)
418 {
419 UNREFERENCED_PARAMETER(xpdd);
420 UNREFERENCED_PARAMETER(base);
421 UNREFERENCED_PARAMETER(length);
422 }
424 #endif