win-pvdrivers

view xenpci/xenpci_patch_kernel.c @ 1099:27bd2a5a4704

License change from GPL to BSD
author James Harper <james.harper@bendigoit.com.au>
date Thu Mar 13 13:38:31 2014 +1100 (2014-03-13)
parents 24fae56a87bf
children
line source
1 /*
2 PV Drivers for Windows Xen HVM Domains
4 Copyright (c) 2014, James Harper
5 All rights reserved.
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 * Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 * Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in the
13 documentation and/or other materials provided with the distribution.
14 * Neither the name of James Harper nor the
15 names of its contributors may be used to endorse or promote products
16 derived from this software without specific prior written permission.
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 DISCLAIMED. IN NO EVENT SHALL JAMES HARPER BE LIABLE FOR ANY
22 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
30 #include "xenpci.h"
32 #if defined(_X86_)
34 /* Is LOCK MOV CR0 available? */
35 #define CPUID_ALTMOVCR8 (1UL << 4)
36 /* Task priority register address */
37 #define LAPIC_TASKPRI 0xFFFE0080
38 #define TPR_BYTES 0x80, 0x00, 0xfe, 0xff
40 extern VOID MoveTprToEax(VOID);
41 extern VOID MoveTprToEcx(VOID);
42 extern VOID MoveTprToEdx(VOID);
43 extern VOID MoveTprToEsi(VOID);
44 extern VOID PushTpr(VOID);
45 extern VOID MoveEaxToTpr(VOID);
46 extern VOID MoveEbxToTpr(VOID);
47 extern VOID MoveEcxToTpr(VOID);
48 extern VOID MoveEdxToTpr(VOID);
49 extern VOID MoveEsiToTpr(VOID);
50 extern VOID MoveConstToTpr(ULONG new_tpr_value);
51 extern VOID MoveZeroToTpr(VOID);
53 static PHYSICAL_ADDRESS lapic_page[MAX_VIRT_CPUS];
54 static volatile PVOID lapic[MAX_VIRT_CPUS];
55 static ULONG tpr_cache[MAX_VIRT_CPUS];
56 #define PATCH_METHOD_LOCK_MOVE_CR0 1
57 #define PATCH_METHOD_CACHED_TPR 2
58 static ULONG patch_method;
60 static ULONG
61 SaveTprProcValue(ULONG cpu, ULONG value) {
62 switch (patch_method) {
63 case PATCH_METHOD_LOCK_MOVE_CR0:
64 case PATCH_METHOD_CACHED_TPR:
65 tpr_cache[cpu] = value;
66 break;
67 }
68 return value;
69 }
71 static ULONG
72 SaveTpr() {
73 ULONG cpu = KeGetCurrentProcessorNumber() & 0xff;
75 switch (patch_method) {
76 case PATCH_METHOD_LOCK_MOVE_CR0:
77 case PATCH_METHOD_CACHED_TPR:
78 return SaveTprProcValue(cpu, *(PULONG)LAPIC_TASKPRI);
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 int i;
87 ULONG bit;
88 ULONG value;
89 for (i = 0; i < 8; i++) {
90 value = bitmap[(7 - i) * 4];
91 if (value) {
92 _BitScanReverse(&bit, value);
93 return ((7 - i) << 5) | bit;
94 }
95 }
96 return -1;
97 }
99 /* called with interrupts disabled (via CLI) from an arbitrary location inside HAL.DLL */
100 VOID
101 WriteTpr(ULONG new_tpr_value) {
102 ULONG cpu = KeGetCurrentProcessorNumber() & 0xff;
104 switch (patch_method) {
105 case PATCH_METHOD_LOCK_MOVE_CR0:
106 tpr_cache[cpu] = new_tpr_value;
107 __asm {
108 mov eax, new_tpr_value;
109 shr eax, 4;
110 lock mov cr0, eax; /* this is actually mov cr8, eax */
111 }
112 break;
113 case PATCH_METHOD_CACHED_TPR:
114 if (new_tpr_value != tpr_cache[cpu]) {
115 *(PULONG)LAPIC_TASKPRI = new_tpr_value;
116 tpr_cache[cpu] = new_tpr_value;
117 }
118 break;
119 }
120 }
122 /* called with interrupts disabled (via CLI) from an arbitrary location inside HAL.DLL */
123 ULONG
124 ReadTpr() {
125 ULONG cpu = KeGetCurrentProcessorNumber() & 0xff;
127 switch (patch_method) {
128 case PATCH_METHOD_LOCK_MOVE_CR0:
129 case PATCH_METHOD_CACHED_TPR:
130 return tpr_cache[cpu];
131 default:
132 return 0;
133 }
134 }
136 static __inline VOID
137 InsertCallRel32(PUCHAR address, ULONG target) {
138 *address = 0xE8; /* call near */
139 *(PULONG)(address + 1) = (ULONG)target - ((ULONG)address + 5);
140 }
142 #define PATCH_SIZE 10
144 typedef struct {
145 ULONG patch_type;
146 ULONG match_size;
147 ULONG function;
148 UCHAR bytes[PATCH_SIZE];
149 } patch_t;
151 #define PATCH_NONE 0
152 #define PATCH_1B4 1 /* 1 byte opcode with 4 bytes of data - replace with call function */
153 #define PATCH_2B4 2 /* 2 byte opcode with 4 bytes of data - replace with nop + call function*/
154 #define PATCH_2B5 3 /* 2 byte opcode with 1 + 4 bytes of data - replace with nop + nop + call function */
155 #define PATCH_2B8 4 /* 2 byte opcode with 4 + 4 bytes of data - replace with push const + call function*/
157 static patch_t patches[] = {
158 { PATCH_1B4, 5, (ULONG)MoveTprToEax, { 0xa1, TPR_BYTES } },
159 { PATCH_2B4, 6, (ULONG)MoveTprToEcx, { 0x8b, 0x0d, TPR_BYTES } },
160 { PATCH_2B4, 6, (ULONG)MoveTprToEdx, { 0x8b, 0x15, TPR_BYTES } },
161 { PATCH_2B4, 6, (ULONG)MoveTprToEsi, { 0x8b, 0x35, TPR_BYTES } },
162 { PATCH_2B4, 6, (ULONG)PushTpr, { 0xff, 0x35, TPR_BYTES } },
163 { PATCH_1B4, 5, (ULONG)MoveEaxToTpr, { 0xa3, TPR_BYTES } },
164 { PATCH_2B4, 6, (ULONG)MoveEbxToTpr, { 0x89, 0x1D, TPR_BYTES } },
165 { PATCH_2B4, 6, (ULONG)MoveEcxToTpr, { 0x89, 0x0D, TPR_BYTES } },
166 { PATCH_2B4, 6, (ULONG)MoveEdxToTpr, { 0x89, 0x15, TPR_BYTES } },
167 { PATCH_2B4, 6, (ULONG)MoveEsiToTpr, { 0x89, 0x35, TPR_BYTES } },
168 { PATCH_2B8, 6, (ULONG)MoveConstToTpr, { 0xC7, 0x05, TPR_BYTES } }, /* + another 4 bytes of const */
169 { PATCH_2B5, 7, (ULONG)MoveZeroToTpr, { 0x83, 0x25, TPR_BYTES, 0 } },
170 { PATCH_NONE, 0, 0, { 0 } }
171 };
173 static BOOLEAN
174 XenPci_TestAndPatchInstruction(PVOID address) {
175 PUCHAR instruction = address;
176 ULONG i;
177 /* don't declare patches[] on the stack - windows gets grumpy if we allocate too much space on the stack at HIGH_LEVEL */
179 for (i = 0; patches[i].patch_type != PATCH_NONE; i++) {
180 if (memcmp(address, patches[i].bytes, patches[i].match_size) == 0)
181 break;
182 }
184 switch (patches[i].patch_type) {
185 case PATCH_1B4:
186 InsertCallRel32(instruction + 0, patches[i].function);
187 break;
188 case PATCH_2B4:
189 *(instruction + 0) = 0x90; /* nop */
190 InsertCallRel32(instruction + 1, patches[i].function);
191 break;
192 case PATCH_2B8:
193 *(instruction + 0) = 0x68; /* push value */
194 *(PULONG)(instruction + 1) = *(PULONG)(instruction + 6);
195 InsertCallRel32(instruction + 5, patches[i].function);
196 break;
197 case PATCH_2B5:
198 *(instruction + 0) = 0x90; /* nop */
199 *(instruction + 1) = 0x90; /* nop */
200 InsertCallRel32(instruction + 2, patches[i].function);
201 break;
202 default:
203 return FALSE;
204 }
205 return TRUE;
206 }
208 typedef struct {
209 PVOID base;
210 ULONG length;
211 } patch_info_t;
213 static PVOID patch_positions[256];
214 static PVOID potential_patch_positions[256];
216 static VOID
217 XenPci_DoPatchKernel0(PVOID context) {
218 patch_info_t *pi = context;
219 ULONG i;
220 ULONG high_level_tpr;
221 ULONG patch_position_index = 0;
222 ULONG potential_patch_position_index = 0;
224 FUNCTION_ENTER();
226 high_level_tpr = SaveTpr();
227 /* we know all the other CPUs are at HIGH_LEVEL so set them all to the same as cpu 0 */
228 for (i = 1; i < MAX_VIRT_CPUS; i++)
229 SaveTprProcValue(i, high_level_tpr);
231 /* we can't use KdPrint while patching as it may involve the TPR while we are patching it */
232 for (i = 0; i < pi->length; i++) {
233 if (XenPci_TestAndPatchInstruction((PUCHAR)pi->base + i)) {
234 patch_positions[patch_position_index++] = (PUCHAR)pi->base + i;
235 } else if (*(PULONG)((PUCHAR)pi->base + i) == LAPIC_TASKPRI) {
236 potential_patch_positions[potential_patch_position_index++] = (PUCHAR)pi->base + i;
237 }
238 }
240 for (i = 0; i < patch_position_index; i++)
241 FUNCTION_MSG("Patch added at %p\n", patch_positions[i]);
243 for (i = 0; i < potential_patch_position_index; i++)
244 FUNCTION_MSG("Unpatch TPR address found at %p\n", potential_patch_positions[i]);
246 FUNCTION_EXIT();
247 }
249 static VOID
250 XenPci_DoPatchKernelN(PVOID context) {
251 UNREFERENCED_PARAMETER(context);
253 FUNCTION_ENTER();
255 FUNCTION_EXIT();
256 }
258 static BOOLEAN
259 IsMoveCr8Supported() {
260 DWORD32 cpuid_output[4];
262 __cpuid(cpuid_output, 0x80000001UL);
263 if (cpuid_output[2] & CPUID_ALTMOVCR8)
264 return TRUE;
265 else
266 return FALSE;
267 }
269 VOID
270 XenPci_PatchKernel(PXENPCI_DEVICE_DATA xpdd, PVOID base, ULONG length) {
271 patch_info_t patch_info;
272 #if (NTDDI_VERSION >= NTDDI_WINXP)
273 RTL_OSVERSIONINFOEXW version_info;
274 #endif
276 FUNCTION_ENTER();
278 /* if we're compiled for 2000 then assume we need patching */
279 #if (NTDDI_VERSION >= NTDDI_WINXP)
280 version_info.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
282 RtlGetVersion((PRTL_OSVERSIONINFOW)&version_info);
283 if (version_info.dwMajorVersion >= 6) {
284 FUNCTION_MSG("Vista or newer - no need for patch\n");
285 return;
286 }
287 if (version_info.dwMajorVersion == 5
288 && version_info.dwMinorVersion > 2) {
289 FUNCTION_MSG("Windows 2003 sp2 or newer - no need for patch\n");
290 return;
291 }
292 if (version_info.dwMajorVersion == 5
293 && version_info.dwMinorVersion == 2
294 && version_info.wServicePackMajor >= 2) {
295 FUNCTION_MSG("Windows 2003 sp2 or newer - no need for patch\n");
296 return;
297 }
298 #endif
299 if (IsMoveCr8Supported()) {
300 FUNCTION_MSG("Using LOCK MOVE CR0 TPR patch\n");
301 patch_method = PATCH_METHOD_LOCK_MOVE_CR0;
302 } else {
303 FUNCTION_MSG("Using cached TPR patch\n");
304 patch_method = PATCH_METHOD_CACHED_TPR;
305 }
306 patch_info.base = base;
307 patch_info.length = length;
309 XenPci_HighSync(XenPci_DoPatchKernel0, XenPci_DoPatchKernelN, &patch_info);
311 xpdd->removable = FALSE;
313 FUNCTION_EXIT();
314 }
316 #else
318 VOID
319 XenPci_PatchKernel(PXENPCI_DEVICE_DATA xpdd, PVOID base, ULONG length) {
320 UNREFERENCED_PARAMETER(xpdd);
321 UNREFERENCED_PARAMETER(base);
322 UNREFERENCED_PARAMETER(length);
323 }
325 #endif