win-pvdrivers

view xenpci/xenpci_patch_kernel.c @ 1066:24fae56a87bf

Fix build errors
author James Harper <james.harper@bendigoit.com.au>
date Tue Oct 29 19:38:31 2013 +1100 (2013-10-29)
parents 1d6eae98e552
children 27bd2a5a4704
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_CACHED_TPR 2
49 static ULONG patch_method;
51 static ULONG
52 SaveTprProcValue(ULONG cpu, ULONG value) {
53 switch (patch_method) {
54 case PATCH_METHOD_LOCK_MOVE_CR0:
55 case PATCH_METHOD_CACHED_TPR:
56 tpr_cache[cpu] = value;
57 break;
58 }
59 return value;
60 }
62 static ULONG
63 SaveTpr() {
64 ULONG cpu = KeGetCurrentProcessorNumber() & 0xff;
66 switch (patch_method) {
67 case PATCH_METHOD_LOCK_MOVE_CR0:
68 case PATCH_METHOD_CACHED_TPR:
69 return SaveTprProcValue(cpu, *(PULONG)LAPIC_TASKPRI);
70 }
71 return 0;
72 }
74 /* called with interrupts disabled (via CLI) from an arbitrary location inside HAL.DLL */
75 static __inline LONG
76 ApicHighestVector(PULONG bitmap) {
77 int i;
78 ULONG bit;
79 ULONG value;
80 for (i = 0; i < 8; i++) {
81 value = bitmap[(7 - i) * 4];
82 if (value) {
83 _BitScanReverse(&bit, value);
84 return ((7 - i) << 5) | bit;
85 }
86 }
87 return -1;
88 }
90 /* called with interrupts disabled (via CLI) from an arbitrary location inside HAL.DLL */
91 VOID
92 WriteTpr(ULONG new_tpr_value) {
93 ULONG cpu = KeGetCurrentProcessorNumber() & 0xff;
95 switch (patch_method) {
96 case PATCH_METHOD_LOCK_MOVE_CR0:
97 tpr_cache[cpu] = new_tpr_value;
98 __asm {
99 mov eax, new_tpr_value;
100 shr eax, 4;
101 lock mov cr0, eax; /* this is actually mov cr8, eax */
102 }
103 break;
104 case PATCH_METHOD_CACHED_TPR:
105 if (new_tpr_value != tpr_cache[cpu]) {
106 *(PULONG)LAPIC_TASKPRI = new_tpr_value;
107 tpr_cache[cpu] = new_tpr_value;
108 }
109 break;
110 }
111 }
113 /* called with interrupts disabled (via CLI) from an arbitrary location inside HAL.DLL */
114 ULONG
115 ReadTpr() {
116 ULONG cpu = KeGetCurrentProcessorNumber() & 0xff;
118 switch (patch_method) {
119 case PATCH_METHOD_LOCK_MOVE_CR0:
120 case PATCH_METHOD_CACHED_TPR:
121 return tpr_cache[cpu];
122 default:
123 return 0;
124 }
125 }
127 static __inline VOID
128 InsertCallRel32(PUCHAR address, ULONG target) {
129 *address = 0xE8; /* call near */
130 *(PULONG)(address + 1) = (ULONG)target - ((ULONG)address + 5);
131 }
133 #define PATCH_SIZE 10
135 typedef struct {
136 ULONG patch_type;
137 ULONG match_size;
138 ULONG function;
139 UCHAR bytes[PATCH_SIZE];
140 } patch_t;
142 #define PATCH_NONE 0
143 #define PATCH_1B4 1 /* 1 byte opcode with 4 bytes of data - replace with call function */
144 #define PATCH_2B4 2 /* 2 byte opcode with 4 bytes of data - replace with nop + call function*/
145 #define PATCH_2B5 3 /* 2 byte opcode with 1 + 4 bytes of data - replace with nop + nop + call function */
146 #define PATCH_2B8 4 /* 2 byte opcode with 4 + 4 bytes of data - replace with push const + call function*/
148 static patch_t patches[] = {
149 { PATCH_1B4, 5, (ULONG)MoveTprToEax, { 0xa1, TPR_BYTES } },
150 { PATCH_2B4, 6, (ULONG)MoveTprToEcx, { 0x8b, 0x0d, TPR_BYTES } },
151 { PATCH_2B4, 6, (ULONG)MoveTprToEdx, { 0x8b, 0x15, TPR_BYTES } },
152 { PATCH_2B4, 6, (ULONG)MoveTprToEsi, { 0x8b, 0x35, TPR_BYTES } },
153 { PATCH_2B4, 6, (ULONG)PushTpr, { 0xff, 0x35, TPR_BYTES } },
154 { PATCH_1B4, 5, (ULONG)MoveEaxToTpr, { 0xa3, TPR_BYTES } },
155 { PATCH_2B4, 6, (ULONG)MoveEbxToTpr, { 0x89, 0x1D, TPR_BYTES } },
156 { PATCH_2B4, 6, (ULONG)MoveEcxToTpr, { 0x89, 0x0D, TPR_BYTES } },
157 { PATCH_2B4, 6, (ULONG)MoveEdxToTpr, { 0x89, 0x15, TPR_BYTES } },
158 { PATCH_2B4, 6, (ULONG)MoveEsiToTpr, { 0x89, 0x35, TPR_BYTES } },
159 { PATCH_2B8, 6, (ULONG)MoveConstToTpr, { 0xC7, 0x05, TPR_BYTES } }, /* + another 4 bytes of const */
160 { PATCH_2B5, 7, (ULONG)MoveZeroToTpr, { 0x83, 0x25, TPR_BYTES, 0 } },
161 { PATCH_NONE, 0, 0, { 0 } }
162 };
164 static BOOLEAN
165 XenPci_TestAndPatchInstruction(PVOID address) {
166 PUCHAR instruction = address;
167 ULONG i;
168 /* don't declare patches[] on the stack - windows gets grumpy if we allocate too much space on the stack at HIGH_LEVEL */
170 for (i = 0; patches[i].patch_type != PATCH_NONE; i++) {
171 if (memcmp(address, patches[i].bytes, patches[i].match_size) == 0)
172 break;
173 }
175 switch (patches[i].patch_type) {
176 case PATCH_1B4:
177 InsertCallRel32(instruction + 0, patches[i].function);
178 break;
179 case PATCH_2B4:
180 *(instruction + 0) = 0x90; /* nop */
181 InsertCallRel32(instruction + 1, patches[i].function);
182 break;
183 case PATCH_2B8:
184 *(instruction + 0) = 0x68; /* push value */
185 *(PULONG)(instruction + 1) = *(PULONG)(instruction + 6);
186 InsertCallRel32(instruction + 5, patches[i].function);
187 break;
188 case PATCH_2B5:
189 *(instruction + 0) = 0x90; /* nop */
190 *(instruction + 1) = 0x90; /* nop */
191 InsertCallRel32(instruction + 2, patches[i].function);
192 break;
193 default:
194 return FALSE;
195 }
196 return TRUE;
197 }
199 typedef struct {
200 PVOID base;
201 ULONG length;
202 } patch_info_t;
204 static PVOID patch_positions[256];
205 static PVOID potential_patch_positions[256];
207 static VOID
208 XenPci_DoPatchKernel0(PVOID context) {
209 patch_info_t *pi = context;
210 ULONG i;
211 ULONG high_level_tpr;
212 ULONG patch_position_index = 0;
213 ULONG potential_patch_position_index = 0;
215 FUNCTION_ENTER();
217 high_level_tpr = SaveTpr();
218 /* we know all the other CPUs are at HIGH_LEVEL so set them all to the same as cpu 0 */
219 for (i = 1; i < MAX_VIRT_CPUS; i++)
220 SaveTprProcValue(i, high_level_tpr);
222 /* we can't use KdPrint while patching as it may involve the TPR while we are patching it */
223 for (i = 0; i < pi->length; i++) {
224 if (XenPci_TestAndPatchInstruction((PUCHAR)pi->base + i)) {
225 patch_positions[patch_position_index++] = (PUCHAR)pi->base + i;
226 } else if (*(PULONG)((PUCHAR)pi->base + i) == LAPIC_TASKPRI) {
227 potential_patch_positions[potential_patch_position_index++] = (PUCHAR)pi->base + i;
228 }
229 }
231 for (i = 0; i < patch_position_index; i++)
232 FUNCTION_MSG("Patch added at %p\n", patch_positions[i]);
234 for (i = 0; i < potential_patch_position_index; i++)
235 FUNCTION_MSG("Unpatch TPR address found at %p\n", potential_patch_positions[i]);
237 FUNCTION_EXIT();
238 }
240 static VOID
241 XenPci_DoPatchKernelN(PVOID context) {
242 UNREFERENCED_PARAMETER(context);
244 FUNCTION_ENTER();
246 FUNCTION_EXIT();
247 }
249 static BOOLEAN
250 IsMoveCr8Supported() {
251 DWORD32 cpuid_output[4];
253 __cpuid(cpuid_output, 0x80000001UL);
254 if (cpuid_output[2] & CPUID_ALTMOVCR8)
255 return TRUE;
256 else
257 return FALSE;
258 }
260 VOID
261 XenPci_PatchKernel(PXENPCI_DEVICE_DATA xpdd, PVOID base, ULONG length) {
262 patch_info_t patch_info;
263 #if (NTDDI_VERSION >= NTDDI_WINXP)
264 RTL_OSVERSIONINFOEXW version_info;
265 #endif
267 FUNCTION_ENTER();
269 /* if we're compiled for 2000 then assume we need patching */
270 #if (NTDDI_VERSION >= NTDDI_WINXP)
271 version_info.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
273 RtlGetVersion((PRTL_OSVERSIONINFOW)&version_info);
274 if (version_info.dwMajorVersion >= 6) {
275 FUNCTION_MSG("Vista or newer - no need for patch\n");
276 return;
277 }
278 if (version_info.dwMajorVersion == 5
279 && version_info.dwMinorVersion > 2) {
280 FUNCTION_MSG("Windows 2003 sp2 or newer - no need for patch\n");
281 return;
282 }
283 if (version_info.dwMajorVersion == 5
284 && version_info.dwMinorVersion == 2
285 && version_info.wServicePackMajor >= 2) {
286 FUNCTION_MSG("Windows 2003 sp2 or newer - no need for patch\n");
287 return;
288 }
289 #endif
290 if (IsMoveCr8Supported()) {
291 FUNCTION_MSG("Using LOCK MOVE CR0 TPR patch\n");
292 patch_method = PATCH_METHOD_LOCK_MOVE_CR0;
293 } else {
294 FUNCTION_MSG("Using cached TPR patch\n");
295 patch_method = PATCH_METHOD_CACHED_TPR;
296 }
297 patch_info.base = base;
298 patch_info.length = length;
300 XenPci_HighSync(XenPci_DoPatchKernel0, XenPci_DoPatchKernelN, &patch_info);
302 xpdd->removable = FALSE;
304 FUNCTION_EXIT();
305 }
307 #else
309 VOID
310 XenPci_PatchKernel(PXENPCI_DEVICE_DATA xpdd, PVOID base, ULONG length) {
311 UNREFERENCED_PARAMETER(xpdd);
312 UNREFERENCED_PARAMETER(base);
313 UNREFERENCED_PARAMETER(length);
314 }
316 #endif