win-pvdrivers

view xenpci/xenpci_patch_kernel.c @ 651:8b3dae86a7f6

nothing
author James Harper <james.harper@bendigoit.com.au>
date Mon Sep 07 13:31:22 2009 +1000 (2009-09-07)
parents 70c3a7839b4e
children 4e6f162a054c
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 int i;
311 FUNCTION_ENTER();
313 for (i = 0; i < KeNumberProcessors; i++)
314 {
315 KdPrint((__DRIVER_NAME " mapping lapic for cpu = %d\n", i));
317 lapic_page[i] = XenPci_AllocMMIO(xpdd, PAGE_SIZE);
318 lapic[i] = MmMapIoSpace(lapic_page[i], PAGE_SIZE, MmCached);
320 xatp.domid = DOMID_SELF;
321 xatp.idx = i;
322 xatp.space = XENMAPSPACE_vlapic;
323 xatp.gpfn = (xen_pfn_t)(lapic_page[i].QuadPart >> PAGE_SHIFT);
324 KdPrint((__DRIVER_NAME " gpfn = %x\n", xatp.gpfn));
325 rc = HYPERVISOR_memory_op(xpdd, XENMEM_add_to_physmap, &xatp);
326 KdPrint((__DRIVER_NAME " hypervisor memory op (XENMAPSPACE_vlapic_regs) ret = %d\n", rc));
327 if (rc != 0)
328 {
329 FUNCTION_EXIT();
330 return rc;
331 }
332 }
333 FUNCTION_EXIT();
335 return rc;
336 }
338 VOID
339 XenPci_PatchKernel(PXENPCI_DEVICE_DATA xpdd, PVOID base, ULONG length)
340 {
341 patch_info_t patch_info;
342 ULONG rc;
343 RTL_OSVERSIONINFOEXW version_info;
345 FUNCTION_ENTER();
347 version_info.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
348 RtlGetVersion((PRTL_OSVERSIONINFOW)&version_info);
349 if (version_info.dwMajorVersion >= 6)
350 {
351 KdPrint((__DRIVER_NAME " Vista or newer - no need for patch\n"));
352 return;
353 }
354 if (version_info.dwMajorVersion == 5
355 && version_info.dwMinorVersion > 2)
356 {
357 KdPrint((__DRIVER_NAME " Windows 2003 sp2 or newer - no need for patch\n"));
358 return;
359 }
360 if (version_info.dwMajorVersion == 5
361 && version_info.dwMinorVersion == 2
362 && version_info.wServicePackMajor >= 2)
363 {
364 KdPrint((__DRIVER_NAME " Windows 2003 sp2 or newer - no need for patch\n"));
365 return;
366 }
368 if (IsMoveCr8Supported())
369 {
370 KdPrint((__DRIVER_NAME " Using LOCK MOVE CR0 TPR patch\n"));
371 patch_method = PATCH_METHOD_LOCK_MOVE_CR0;
372 }
373 else
374 {
375 rc = MapVlapic(xpdd);
376 if (rc == EACCES)
377 {
378 KdPrint((__DRIVER_NAME " Xen already using VMX LAPIC acceleration. No patch required\n"));
379 return;
380 }
381 if (!rc)
382 {
383 KdPrint((__DRIVER_NAME " Using mapped vLAPIC TPR patch\n"));
384 patch_method = PATCH_METHOD_MAPPED_VLAPIC;
385 }
386 else
387 {
388 KdPrint((__DRIVER_NAME " Using cached TPR patch\n"));
389 patch_method = PATCH_METHOD_CACHED_TPR;
390 }
391 }
392 patch_info.base = base;
393 patch_info.length = length;
395 XenPci_HighSync(XenPci_DoPatchKernel0, XenPci_DoPatchKernelN, &patch_info);
397 xpdd->removable = FALSE;
399 FUNCTION_EXIT();
400 }
402 #else
404 VOID
405 XenPci_PatchKernel(PXENPCI_DEVICE_DATA xpdd, PVOID base, ULONG length)
406 {
407 UNREFERENCED_PARAMETER(xpdd);
408 UNREFERENCED_PARAMETER(base);
409 UNREFERENCED_PARAMETER(length);
410 }
412 #endif