Chameleon

Chameleon Svn Source Tree

Root/branches/meklort/i386/boot2/kernel_patcher.c

Source at commit 154 created 13 years 11 months ago.
By meklort, Added a pacth_lapic_init routine. determines if a patch is required based on cpu model. Todo: make cpuid patch configurable
1/*
2 * Copyright (c) 2009 Evan Lojewski. All rights reserved.
3 *
4 */
5
6#include "libsaio.h"
7#include "kernel_patcher.h"
8#include "platform.h"
9
10extern PlatformInfo_t Platform;
11
12
13#define SYMBOL_CPUID_SET_INFO0
14#define SYMBOL_PANIC1
15#define SYMBOL_PMCPUEXITHALTTOOFF2
16#define SYMBOL_LAPIC_INIT3
17#define NUM_SYMBOLS4
18
19#define SYMBOL_CPUID_SET_INFO_STRING"_cpuid_set_info"
20#define SYMBOL_PANIC_STRING"_panic"
21#define SYMBOL_PMCPUEXITHALTTOOFF_STRING"_pmCPUExitHaltToOff"
22#define SYMBOL_LAPIC_INIT_STRING"_lapic_init"
23
24char* kernelSymbols[NUM_SYMBOLS] = {
25SYMBOL_CPUID_SET_INFO_STRING,
26SYMBOL_PANIC_STRING,
27SYMBOL_PMCPUEXITHALTTOOFF_STRING,
28SYMBOL_LAPIC_INIT_STRING
29};
30
31UInt32 kernelSymbolAddresses[NUM_SYMBOLS] = {
320,
330,
340,
350
36};
37
38
39UInt32 textSection = 0;
40UInt32 textAddress = 0;
41
42
43void patch_kernel(void* kernelData)
44{
45switch (locate_symbols((void*)kernelData)) {
46case KERNEL_32:
47patch_kernel_32((void*)kernelData);
48break;
49
50case KERNEL_64:
51default:
52patch_kernel_64((void*)kernelData);
53break;
54}
55}
56
57// patches a 64bit kernel.
58void patch_kernel_64(void* kernelData)
59{
60// At the moment, the kernel patching code fails when used
61// in 64bit mode, so we don't patch it. This is due to 32bit vs 64bit
62// pointers as well as changes in structure sizes
63printf("Unable to patch 64bit kernel. Please use arch=i386.\n");
64}
65
66
67/**
68 ** patch_kernel_32
69 **patches kernel based on cpu info determined earlier in the boot process.
70 **It the machine is vmware, remove the artificial lapic panic
71 **If the CPU is not supported, remove the cpuid_set_info panic
72 **If the CPU is and Intel Atom, inject the penryn cpuid info.
73 **/
74void patch_kernel_32(void* kernelData)
75{
76//patch_pmCPUExitHaltToOff(kernelData);// Not working as intended, disabled for now
77
78//if(vmware_detected)
79{
80patch_lapic_init(kernelData);
81}
82
83switch( Platform.CPU.Vendor )
84{
85case 0x06:
86switch(Platform.CPU.Model)
87{
88// Known good CPU's, no reason to patch kernel
89case 13:
90case CPUID_MODEL_YONAH:
91case CPUID_MODEL_MEROM:
92case CPUID_MODEL_PENRYN:
93case CPUID_MODEL_NEHALEM:
94case CPUID_MODEL_FIELDS:
95case CPUID_MODEL_DALES:
96case CPUID_MODEL_NEHALEM_EX:
97break;
98
99// Known unsuported CPU's
100case CPUID_MODEL_ATOM:
101// TODO: Impersonate CPU based on user selection
102patch_cpuid_set_info(kernelData, CPUFAMILY_INTEL_PENRYN, CPUID_MODEL_PENRYN);// Impersonate Penryn CPU
103break;
104
105// Unknown CPU's
106default:
107// TODO: Impersonate CPU based on user selection
108patch_cpuid_set_info(kernelData, 0, 0);// Remove Panic Call
109
110break;
111}
112break;
113
114default:
115break;
116}
117}
118
119
120/**
121 **This functions located the kernelSymbols[i] symbols in the macho header.
122 **as well as determines the start of the __TEXT segment and __TEXT,__text sections
123 **/
124int locate_symbols(void* kernelData)
125{
126UInt16 symbolIndexes[NUM_SYMBOLS];
127
128struct load_command *loadCommand;
129struct symtab_command *symtableData;
130struct nlist *symbolEntry;
131
132char* symbolString;
133
134UInt32 kernelIndex = 0;
135kernelIndex += sizeof(struct mach_header);
136
137if(((struct mach_header*)kernelData)->magic != MH_MAGIC) return KERNEL_64;
138
139
140//printf("%d load commands beginning at 0x%X\n", (unsigned int)header->ncmds, (unsigned int)kernelIndex);
141//printf("Commands take up %d bytes\n", header->sizeofcmds);
142
143
144int cmd = 0;
145while(cmd < ((struct mach_header*)kernelData)->ncmds)// TODO: for loop instead
146{
147cmd++;
148
149loadCommand = kernelData + kernelIndex;
150
151UInt cmdSize = loadCommand->cmdsize;
152
153
154// Locate start of _panic and _cpuid_set_info in the symbol tabe.
155// Load commands should be anded with 0x7FFFFFFF to ignore theLC_REQ_DYLD flag
156if((loadCommand->cmd & 0x7FFFFFFF) == LC_SYMTAB)// We only care about the symtab segment
157{
158//printf("Located symtable, length is 0x%X, 0x%X\n", (unsigned int)loadCommand->cmdsize, (unsigned int)sizeof(symtableData));
159
160symtableData = kernelData + kernelIndex;
161kernelIndex += sizeof(struct symtab_command);
162
163cmdSize -= sizeof(struct symtab_command);
164
165// Loop through symbol table untill all of the symbols have been found
166
167symbolString = kernelData + symtableData->stroff;
168
169
170UInt16 symbolIndex = 0;
171UInt8 numSymbolsFound = 0;
172
173while(symbolIndex < symtableData->nsyms && numSymbolsFound < NUM_SYMBOLS)// TODO: for loop
174{
175int i = 0;
176while(i < NUM_SYMBOLS)
177{
178if(strcmp(symbolString, kernelSymbols[i]) == 0)
179{
180symbolIndexes[i] = symbolIndex;
181numSymbolsFound++;
182}
183i++;
184
185}
186symbolString += strlen(symbolString) + 1;
187symbolIndex++;
188}
189
190// loop again
191symbolIndex = 0;
192numSymbolsFound = 0;
193while(symbolIndex < symtableData->nsyms && numSymbolsFound < NUM_SYMBOLS)// TODO: for loop
194{
195
196symbolEntry = kernelData + symtableData->symoff + (symbolIndex * sizeof(struct nlist));
197
198int i = 0;
199while(i < NUM_SYMBOLS)
200{
201if(symbolIndex == (symbolIndexes[i] - 4))
202{
203kernelSymbolAddresses[i] = (UInt32)symbolEntry->n_value;
204numSymbolsFound++;
205}
206i++;
207
208}
209
210symbolIndex ++;
211}
212// Load commands should be anded with 0x7FFFFFFF to ignore theLC_REQ_DYLD flag
213} else if((loadCommand->cmd & 0x7FFFFFFF) == LC_SEGMENT)// We only care about the __TEXT segment, any other load command can be ignored
214{
215
216struct segment_command *segCommand;
217
218segCommand = kernelData + kernelIndex;
219
220//printf("Segment name is %s\n", segCommand->segname);
221
222if(strcmp("__TEXT", segCommand->segname) == 0)
223{
224UInt32 sectionIndex;
225
226sectionIndex = sizeof(struct segment_command);
227
228struct section *sect;
229
230while(sectionIndex < segCommand->cmdsize)
231{
232sect = kernelData + kernelIndex + sectionIndex;
233
234sectionIndex += sizeof(struct section);
235
236
237if(strcmp("__text", sect->sectname) == 0)
238{
239// __TEXT,__text found, save the offset and address for when looking for the calls.
240textSection = sect->offset;
241textAddress = sect->addr;
242break;
243}
244}
245}
246
247
248kernelIndex += cmdSize;
249} else {
250kernelIndex += cmdSize;
251}
252}
253
254return KERNEL_32;
255}
256
257
258/**
259 ** Locate the fisrt instance of _panic inside of _cpuid_set_info, and either remove it
260 ** Or replace it so that the cpuid is set to a valid value.
261 **/
262void patch_cpuid_set_info(void* kernelData, UInt32 impersonateFamily, UInt8 inpersonateModel)
263{
264UInt8* bytes = (UInt8*)kernelData;
265UInt32 patchLocation = (kernelSymbolAddresses[SYMBOL_CPUID_SET_INFO] - textAddress + textSection);
266UInt32 jumpLocation = 0;
267UInt32 panicAddr = kernelSymbolAddresses[SYMBOL_PANIC] - textAddress;
268if(kernelSymbolAddresses[SYMBOL_CPUID_SET_INFO] == 0)
269{
270printf("Unable to locate _cpuid_set_info\n");
271return;
272
273}
274if(kernelSymbolAddresses[SYMBOL_PANIC] == 0)
275{
276printf("Unable to locate _panic\n");
277return;
278}
279
280//TODO: don't assume it'll always work (Look for *next* function address in symtab and fail once it's been reached)
281while(
282 (bytes[patchLocation -1] != 0xE8) ||
283 ( ( (UInt32)(panicAddr - patchLocation - 4) + textSection ) != (UInt32)((bytes[patchLocation + 0] << 0 |
284bytes[patchLocation + 1] << 8 |
285bytes[patchLocation + 2] << 16 |
286bytes[patchLocation + 3] << 24)))
287 )
288{
289patchLocation++;
290}
291patchLocation--;
292
293
294// Locate the jump call, so that 10 bytes can be reclamed.
295jumpLocation = patchLocation - 15;
296while((bytes[jumpLocation - 1] != 0x77 ||
297 bytes[jumpLocation] != (patchLocation - jumpLocation - -8)) &&
298 (patchLocation - jumpLocation) < 0xEF)
299{
300jumpLocation--;
301}
302
303// If found... AND we want to impersonate a specific cpumodel / family...
304if(impersonateFamily &&
305 inpersonateModel &&
306 ((patchLocation - jumpLocation) < 0xEF))
307{
308
309bytes[jumpLocation] -= 10;// sizeof(movl$0x6b5a4cd2,0x00872eb4) = 10bytes
310
311/*
312 * Inpersonate the specified CPU FAMILY and CPU Model
313 */
314
315// bytes[patchLocation - 17] = 0xC7;// already here... not needed to be done
316// bytes[patchLocation - 16] = 0x05;// see above
317UInt32 cpuid_cpufamily_addr =bytes[patchLocation - 15] << 0 |
318bytes[patchLocation - 14] << 8 |
319bytes[patchLocation - 13] << 16 |
320bytes[patchLocation - 12] << 24;
321
322// NOTE: may change, determined based on cpuid_info struct
323UInt32 cpuid_model_addr = cpuid_cpufamily_addr - 299;
324
325
326// cpufamily = CPUFAMILY_INTEL_PENRYN
327bytes[patchLocation - 11] = (impersonateFamily & 0x000000FF) >> 0;
328bytes[patchLocation - 10] = (impersonateFamily & 0x0000FF00) >> 8;
329bytes[patchLocation - 9] = (impersonateFamily & 0x00FF0000) >> 16;
330bytes[patchLocation - 8] = (impersonateFamily & 0xFF000000) >> 24;
331
332// NOPS, just in case if the jmp call wasn't patched, we'll jump to a
333// nop and continue with the rest of the patch
334// Yay two free bytes :), 10 more can be reclamed if needed, as well as a few
335// from the above code (only cpuid_model needs to be set.
336bytes[patchLocation - 7] = 0x90;
337bytes[patchLocation - 6] = 0x90;
338
339bytes[patchLocation - 5] = 0xC7;
340bytes[patchLocation - 4] = 0x05;
341bytes[patchLocation - 3] = (cpuid_model_addr & 0x000000FF) >> 0;
342bytes[patchLocation - 2] = (cpuid_model_addr & 0x0000FF00) >> 8;
343bytes[patchLocation - 1] = (cpuid_model_addr & 0x00FF0000) >> 16;
344bytes[patchLocation - 0] = (cpuid_model_addr & 0xFF000000) >> 24;
345
346// Note: I could have just copied the 8bit cpuid_model in and saved about 4 bytes
347// so if this function need a different patch it's still possible. Also, about ten bytes previous can be freed.
348bytes[patchLocation + 1] = inpersonateModel;// cpuid_model
349bytes[patchLocation + 2] = 0x01;// cpuid_extmodel
350bytes[patchLocation + 3] = 0x00;// cpuid_extfamily
351bytes[patchLocation + 4] = 0x02;// cpuid_stepping
352
353}
354else
355{
356// Either We were unable to change the jump call due to the funciton's sctructure
357// changing, or the user did not request a patch. As such, resort to just
358// removing the panic call. Note that the IntelCPUPM kext may still panic
359// due to the cpu's Model ID not being patched
360bytes[patchLocation + 0] = 0x90;
361bytes[patchLocation + 1] = 0x90;
362bytes[patchLocation + 2] = 0x90;
363bytes[patchLocation + 3] = 0x90;
364bytes[patchLocation + 4] = 0x90;
365
366}
367}
368
369
370/**
371 ** SleepEnabler.kext replacement (for those that need it)
372 ** Located the KERN_INVALID_ARGUMENT return and replace it with KERN_SUCCESS
373 **/
374void patch_pmCPUExitHaltToOff(void* kernelData)
375{
376UInt8* bytes = (UInt8*)kernelData;
377UInt32 patchLocation = (kernelSymbolAddresses[SYMBOL_PMCPUEXITHALTTOOFF] - textAddress + textSection);
378
379if(kernelSymbolAddresses[SYMBOL_PMCPUEXITHALTTOOFF] == 0)
380{
381printf("Unable to locate _pmCPUExitHaltToOff\n");
382return;
383}
384
385while(bytes[patchLocation - 1]!= 0xB8 ||
386 bytes[patchLocation]!= 0x04 ||// KERN_INVALID_ARGUMENT (0x00000004)
387 bytes[patchLocation + 1]!= 0x00 ||// KERN_INVALID_ARGUMENT
388 bytes[patchLocation + 2]!= 0x00 ||// KERN_INVALID_ARGUMENT
389 bytes[patchLocation + 3]!= 0x00)// KERN_INVALID_ARGUMENT
390
391{
392patchLocation++;
393}
394bytes[patchLocation] = 0x00;// KERN_SUCCESS;
395}
396
397void patch_lapic_init(void* kernelData)
398{
399UInt8 panicIndex = 0;
400UInt8* bytes = (UInt8*)kernelData;
401UInt32 patchLocation = (kernelSymbolAddresses[SYMBOL_LAPIC_INIT] - textAddress + textSection);
402UInt32 panicAddr = kernelSymbolAddresses[SYMBOL_PANIC] - textAddress;
403
404if(kernelSymbolAddresses[SYMBOL_LAPIC_INIT] == 0)
405{
406printf("Unable to locate _cpuid_set_info\n");
407return;
408
409}
410if(kernelSymbolAddresses[SYMBOL_PANIC] == 0)
411{
412printf("Unable to locate _panic\n");
413return;
414}
415
416
417
418// Locate the (panicIndex + 1) panic call
419while(panicIndex < 3)// Find the third panic call
420{
421while(
422 (bytes[patchLocation -1] != 0xE8) ||
423 ( ( (UInt32)(panicAddr - patchLocation - 4) + textSection ) != (UInt32)((bytes[patchLocation + 0] << 0 |
424bytes[patchLocation + 1] << 8 |
425bytes[patchLocation + 2] << 16 |
426bytes[patchLocation + 3] << 24)))
427 )
428{
429patchLocation++;
430}
431patchLocation++;
432panicIndex++;
433}
434bytes[--patchLocation] = 0x90;
435bytes[++patchLocation] = 0x90;
436bytes[++patchLocation] = 0x90;
437bytes[++patchLocation] = 0x90;
438bytes[++patchLocation] = 0x90;
439
440
441}
442

Archive Download this file

Revision: 154