win-pvdrivers

view xenpci/xenpci_patch_kernel.c @ 547:ac614e49832c

Fixed another typo
author James Harper <james.harper@bendigoit.com.au>
date Sun Mar 08 14:37:45 2009 +1100 (2009-03-08)
parents 2a74ac2f43bb
children 70c3a7839b4e
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 }
210 if (patches[i].patch_type == PATCH_NONE)
211 return FALSE;
213 switch (patches[i].patch_type)
214 {
215 case PATCH_1B4:
216 InsertCallRel32(instruction + 0, patches[i].function);
217 break;
218 case PATCH_2B4:
219 *(instruction + 0) = 0x90; /* nop */
220 InsertCallRel32(instruction + 1, patches[i].function);
221 break;
222 case PATCH_2B8:
223 *(instruction + 0) = 0x68; /* push value */
224 *(PULONG)(instruction + 1) = *(PULONG)(instruction + 6);
225 InsertCallRel32(instruction + 5, patches[i].function);
226 break;
227 case PATCH_2B5:
228 *(instruction + 0) = 0x90; /* nop */
229 *(instruction + 1) = 0x90; /* nop */
230 InsertCallRel32(instruction + 2, patches[i].function);
231 break;
232 default:
233 /* wtf? */
234 break;
235 }
236 return TRUE;
237 }
239 typedef struct {
240 PVOID base;
241 ULONG length;
242 } patch_info_t;
244 static PVOID patch_positions[256];
245 static PVOID potential_patch_positions[256];
247 static VOID
248 XenPci_DoPatchKernel0(PVOID context)
249 {
250 patch_info_t *pi = context;
251 ULONG i;
252 ULONG high_level_tpr;
253 ULONG patch_position_index = 0;
254 ULONG potential_patch_position_index = 0;
256 FUNCTION_ENTER();
258 high_level_tpr = SaveTpr();
260 /* we know all the other CPUs are at HIGH_LEVEL so set them all to the same as cpu 0 */
261 for (i = 1; i < MAX_VIRT_CPUS; i++)
262 SaveTprProcValue(i, high_level_tpr);
264 /* we can't use KdPrint while patching as it may involve the TPR while we are patching it */
265 for (i = 0; i < pi->length; i++)
266 {
267 if (XenPci_TestAndPatchInstruction((PUCHAR)pi->base + i))
268 {
269 patch_positions[patch_position_index++] = (PUCHAR)pi->base + i;
270 }
271 else if (*(PULONG)((PUCHAR)pi->base + i) == LAPIC_TASKPRI)
272 {
273 potential_patch_positions[potential_patch_position_index++] = (PUCHAR)pi->base + i;
274 }
275 }
276 for (i = 0; i < patch_position_index; i++)
277 KdPrint((__DRIVER_NAME " Patch added at %p\n", patch_positions[i]));
279 for (i = 0; i < potential_patch_position_index; i++)
280 KdPrint((__DRIVER_NAME " Unpatch TPR address found at %p\n", potential_patch_positions[i]));
282 FUNCTION_EXIT();
283 }
285 static VOID
286 XenPci_DoPatchKernelN(PVOID context)
287 {
288 UNREFERENCED_PARAMETER(context);
290 FUNCTION_ENTER();
292 FUNCTION_EXIT();
293 }
295 static BOOLEAN
296 IsMoveCr8Supported()
297 {
298 DWORD32 cpuid_output[4];
300 __cpuid(cpuid_output, 0x80000001UL);
301 if (cpuid_output[2] & CPUID_ALTMOVCR8)
302 return TRUE;
303 else
304 return FALSE;
305 }
307 static ULONG
308 MapVlapic(PXENPCI_DEVICE_DATA xpdd)
309 {
310 struct xen_add_to_physmap xatp;
311 ULONG rc = EINVAL;
312 int i;
314 FUNCTION_ENTER();
316 for (i = 0; i < KeNumberProcessors; i++)
317 {
318 KdPrint((__DRIVER_NAME " mapping lapic for cpu = %d\n", i));
320 lapic_page[i] = XenPci_AllocMMIO(xpdd, PAGE_SIZE);
321 lapic[i] = MmMapIoSpace(lapic_page[i], PAGE_SIZE, MmCached);
323 xatp.domid = DOMID_SELF;
324 xatp.idx = i;
325 xatp.space = XENMAPSPACE_vlapic;
326 xatp.gpfn = (xen_pfn_t)(lapic_page[i].QuadPart >> PAGE_SHIFT);
327 KdPrint((__DRIVER_NAME " gpfn = %x\n", xatp.gpfn));
328 rc = HYPERVISOR_memory_op(xpdd, XENMEM_add_to_physmap, &xatp);
329 KdPrint((__DRIVER_NAME " hypervisor memory op (XENMAPSPACE_vlapic_regs) ret = %d\n", rc));
330 if (rc != 0)
331 {
332 FUNCTION_EXIT();
333 return rc;
334 }
335 }
336 FUNCTION_EXIT();
338 return rc;
339 }
341 VOID
342 XenPci_PatchKernel(PXENPCI_DEVICE_DATA xpdd, PVOID base, ULONG length)
343 {
344 patch_info_t patch_info;
345 ULONG rc;
346 RTL_OSVERSIONINFOEXW version_info;
348 FUNCTION_ENTER();
350 version_info.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
351 RtlGetVersion((PRTL_OSVERSIONINFOW)&version_info);
352 if (version_info.dwMajorVersion >= 6)
353 {
354 KdPrint((__DRIVER_NAME " Vista or newer - no need for patch\n"));
355 return;
356 }
357 if (version_info.dwMajorVersion == 5
358 && version_info.dwMinorVersion > 2)
359 {
360 KdPrint((__DRIVER_NAME " Windows 2003 sp2 or newer - no need for patch\n"));
361 return;
362 }
363 if (version_info.dwMajorVersion == 5
364 && version_info.dwMinorVersion == 2
365 && version_info.wServicePackMajor >= 2)
366 {
367 KdPrint((__DRIVER_NAME " Windows 2003 sp2 or newer - no need for patch\n"));
368 return;
369 }
371 if (IsMoveCr8Supported())
372 {
373 KdPrint((__DRIVER_NAME " Using LOCK MOVE CR0 TPR patch\n"));
374 patch_method = PATCH_METHOD_LOCK_MOVE_CR0;
375 }
376 else
377 {
378 rc = MapVlapic(xpdd);
379 if (rc == EACCES)
380 {
381 KdPrint((__DRIVER_NAME " Xen already using VMX LAPIC acceleration. No patch required\n"));
382 return;
383 }
384 if (!rc)
385 {
386 KdPrint((__DRIVER_NAME " Using mapped vLAPIC TPR patch\n"));
387 patch_method = PATCH_METHOD_MAPPED_VLAPIC;
388 }
389 else
390 {
391 KdPrint((__DRIVER_NAME " Using cached TPR patch\n"));
392 patch_method = PATCH_METHOD_CACHED_TPR;
393 }
394 }
395 patch_info.base = base;
396 patch_info.length = length;
398 XenPci_HighSync(XenPci_DoPatchKernel0, XenPci_DoPatchKernelN, &patch_info);
400 xpdd->removable = FALSE;
402 FUNCTION_EXIT();
403 }
405 #else
407 VOID
408 XenPci_PatchKernel(PXENPCI_DEVICE_DATA xpdd, PVOID base, ULONG length)
409 {
410 UNREFERENCED_PARAMETER(xpdd);
411 UNREFERENCED_PARAMETER(base);
412 UNREFERENCED_PARAMETER(length);
413 }
415 #endif