1 /*++ @file
2   Emu driver to produce CPU Architectural Protocol.
3 
4 Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>
5 Portions copyright (c) 2011 - 2012, Apple Inc. All rights reserved.
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution.  The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10 
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 
14 **/
15 
16 #include "CpuDriver.h"
17 
18 UINT64  mTimerPeriod;
19 
20 CPU_ARCH_PROTOCOL_PRIVATE mCpuTemplate = {
21   CPU_ARCH_PROT_PRIVATE_SIGNATURE,
22   NULL,
23   {
24     EmuFlushCpuDataCache,
25     EmuEnableInterrupt,
26     EmuDisableInterrupt,
27     EmuGetInterruptState,
28     EmuInit,
29     EmuRegisterInterruptHandler,
30     EmuGetTimerValue,
31     EmuSetMemoryAttributes,
32     0,
33     4
34   },
35   {
36     {
37       CpuMemoryServiceRead,
38       CpuMemoryServiceWrite
39     },
40     {
41       CpuIoServiceRead,
42       CpuIoServiceWrite
43     }
44   },
45   TRUE
46 };
47 
48 #define EFI_CPU_DATA_MAXIMUM_LENGTH 0x100
49 
50 SMBIOS_TABLE_TYPE4 mCpuSmbiosType4 = {
51   { EFI_SMBIOS_TYPE_PROCESSOR_INFORMATION, sizeof (SMBIOS_TABLE_TYPE4), 0},
52   1,                    // Socket String
53   ProcessorOther,       // ProcessorType;          ///< The enumeration value from PROCESSOR_TYPE_DATA.
54   ProcessorFamilyOther, // ProcessorFamily;        ///< The enumeration value from PROCESSOR_FAMILY_DATA.
55   2,                    // ProcessorManufacture String;
56   {                     // ProcessorId;
57     {  // PROCESSOR_SIGNATURE
58       0, //  ProcessorSteppingId:4;
59       0, //  ProcessorModel:     4;
60       0, //  ProcessorFamily:    4;
61       0, //  ProcessorType:      2;
62       0, //  ProcessorReserved1: 2;
63       0, //  ProcessorXModel:    4;
64       0, //  ProcessorXFamily:   8;
65       0, //  ProcessorReserved2: 4;
66     },
67     {  // PROCESSOR_FEATURE_FLAGS
68       0, //  ProcessorFpu       :1;
69       0, //  ProcessorVme       :1;
70       0, //  ProcessorDe        :1;
71       0, //  ProcessorPse       :1;
72       0, //  ProcessorTsc       :1;
73       0, //  ProcessorMsr       :1;
74       0, //  ProcessorPae       :1;
75       0, //  ProcessorMce       :1;
76       0, //  ProcessorCx8       :1;
77       0, //  ProcessorApic      :1;
78       0, //  ProcessorReserved1 :1;
79       0, //  ProcessorSep       :1;
80       0, //  ProcessorMtrr      :1;
81       0, //  ProcessorPge       :1;
82       0, //  ProcessorMca       :1;
83       0, //  ProcessorCmov      :1;
84       0, //  ProcessorPat       :1;
85       0, //  ProcessorPse36     :1;
86       0, //  ProcessorPsn       :1;
87       0, //  ProcessorClfsh     :1;
88       0, //  ProcessorReserved2 :1;
89       0, //  ProcessorDs        :1;
90       0, //  ProcessorAcpi      :1;
91       0, //  ProcessorMmx       :1;
92       0, //  ProcessorFxsr      :1;
93       0, //  ProcessorSse       :1;
94       0, //  ProcessorSse2      :1;
95       0, //  ProcessorSs        :1;
96       0, //  ProcessorReserved3 :1;
97       0, //  ProcessorTm        :1;
98       0, //  ProcessorReserved4 :2;
99     }
100   },
101   3,                    // ProcessorVersion String;
102   {                     // Voltage;
103     1,  // ProcessorVoltageCapability5V        :1;
104     1,  // ProcessorVoltageCapability3_3V      :1;
105     1,  // ProcessorVoltageCapability2_9V      :1;
106     0,  // ProcessorVoltageCapabilityReserved  :1; ///< Bit 3, must be zero.
107     0,  // ProcessorVoltageReserved            :3; ///< Bits 4-6, must be zero.
108     0   // ProcessorVoltageIndicateLegacy      :1;
109   },
110   0,                      // ExternalClock;
111   0,                      // MaxSpeed;
112   0,                      // CurrentSpeed;
113   0x41,                   // Status;
114   ProcessorUpgradeOther,  // ProcessorUpgrade;      ///< The enumeration value from PROCESSOR_UPGRADE.
115   0,                      // L1CacheHandle;
116   0,                      // L2CacheHandle;
117   0,                      // L3CacheHandle;
118   4,                      // SerialNumber;
119   5,                      // AssetTag;
120   6,                      // PartNumber;
121   0,                      // CoreCount;
122   0,                      // EnabledCoreCount;
123   0,                      // ThreadCount;
124   0,                      // ProcessorCharacteristics;
125   0,                      // ProcessorFamily2;
126 };
127 
128 CHAR8 *mCpuSmbiosType4Strings[] = {
129   "Socket",
130   "edk2.svn.sourceforge.net",
131   "Emulated Processor",
132   "1.0",
133   "1.0",
134   "1.0",
135   NULL
136 };
137 
138 
139 /**
140   Create SMBIOS record.
141 
142   Converts a fixed SMBIOS structure and an array of pointers to strings into
143   an SMBIOS record where the strings are cat'ed on the end of the fixed record
144   and terminated via a double NULL and add to SMBIOS table.
145 
146   SMBIOS_TABLE_TYPE32 gSmbiosType12 = {
147     { EFI_SMBIOS_TYPE_SYSTEM_CONFIGURATION_OPTIONS, sizeof (SMBIOS_TABLE_TYPE12), 0 },
148     1 // StringCount
149   };
150   CHAR8 *gSmbiosType12Strings[] = {
151     "Not Found",
152     NULL
153   };
154 
155   ...
156   LogSmbiosData (
157     (EFI_SMBIOS_TABLE_HEADER*)&gSmbiosType12,
158     gSmbiosType12Strings
159     );
160 
161   @param  Template    Fixed SMBIOS structure, required.
162   @param  StringArray Array of strings to convert to an SMBIOS string pack.
163                       NULL is OK.
164 
165 **/
166 EFI_STATUS
LogSmbiosData(IN EFI_SMBIOS_TABLE_HEADER * Template,IN CHAR8 ** StringPack)167 LogSmbiosData (
168   IN  EFI_SMBIOS_TABLE_HEADER *Template,
169   IN  CHAR8                   **StringPack
170   )
171 {
172   EFI_STATUS                Status;
173   EFI_SMBIOS_PROTOCOL       *Smbios;
174   EFI_SMBIOS_HANDLE         SmbiosHandle;
175   EFI_SMBIOS_TABLE_HEADER   *Record;
176   UINTN                     Index;
177   UINTN                     StringSize;
178   UINTN                     Size;
179   CHAR8                     *Str;
180 
181   //
182   // Locate Smbios protocol.
183   //
184   Status = gBS->LocateProtocol (&gEfiSmbiosProtocolGuid, NULL, (VOID **)&Smbios);
185   if (EFI_ERROR (Status)) {
186     return Status;
187   }
188 
189   // Calculate the size of the fixed record and optional string pack
190   Size = Template->Length;
191   if (StringPack == NULL) {
192     // At least a double null is required
193     Size += 2;
194   } else {
195     for (Index = 0; StringPack[Index] != NULL; Index++) {
196       StringSize = AsciiStrSize (StringPack[Index]);
197       Size += StringSize;
198     }
199     if (StringPack[0] == NULL) {
200       // At least a double null is required
201       Size += 1;
202     }
203     // Don't forget the terminating double null
204     Size += 1;
205   }
206 
207   // Copy over Template
208   Record = (EFI_SMBIOS_TABLE_HEADER *)AllocateZeroPool (Size);
209   if (Record == NULL) {
210     return EFI_OUT_OF_RESOURCES;
211   }
212   CopyMem (Record, Template, Template->Length);
213 
214   // Append string pack
215   Str = ((CHAR8 *)Record) + Record->Length;
216   for (Index = 0; StringPack[Index] != NULL; Index++) {
217     StringSize = AsciiStrSize (StringPack[Index]);
218     CopyMem (Str, StringPack[Index], StringSize);
219     Str += StringSize;
220   }
221   *Str = 0;
222 
223   SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;
224   Status = Smbios->Add (
225                      Smbios,
226                      gImageHandle,
227                      &SmbiosHandle,
228                      Record
229                      );
230   ASSERT_EFI_ERROR (Status);
231 
232   FreePool (Record);
233   return Status;
234 }
235 
236 
237 
238 
239 VOID
CpuUpdateSmbios(IN UINTN MaxCpus)240 CpuUpdateSmbios (
241   IN UINTN  MaxCpus
242   )
243 {
244   mCpuSmbiosType4.CoreCount        = (UINT8) MaxCpus;
245   mCpuSmbiosType4.EnabledCoreCount = (UINT8) MaxCpus;
246   mCpuSmbiosType4.ThreadCount      = (UINT8) MaxCpus;
247 
248   LogSmbiosData ((EFI_SMBIOS_TABLE_HEADER *)&mCpuSmbiosType4, mCpuSmbiosType4Strings);
249 }
250 
251 
252 //
253 // Service routines for the driver
254 //
255 EFI_STATUS
256 EFIAPI
EmuFlushCpuDataCache(IN EFI_CPU_ARCH_PROTOCOL * This,IN EFI_PHYSICAL_ADDRESS Start,IN UINT64 Length,IN EFI_CPU_FLUSH_TYPE FlushType)257 EmuFlushCpuDataCache (
258   IN EFI_CPU_ARCH_PROTOCOL  *This,
259   IN EFI_PHYSICAL_ADDRESS   Start,
260   IN UINT64                 Length,
261   IN EFI_CPU_FLUSH_TYPE     FlushType
262   )
263 {
264   if (FlushType == EfiCpuFlushTypeWriteBackInvalidate) {
265     //
266     // Only WB flush is supported. We actually need do nothing on Emu emulator
267     // environment. Classify this to follow EFI spec
268     //
269     return EFI_SUCCESS;
270   }
271   //
272   // Other flush types are not supported by Emu emulator
273   //
274   return EFI_UNSUPPORTED;
275 }
276 
277 EFI_STATUS
278 EFIAPI
EmuEnableInterrupt(IN EFI_CPU_ARCH_PROTOCOL * This)279 EmuEnableInterrupt (
280   IN EFI_CPU_ARCH_PROTOCOL  *This
281   )
282 {
283   CPU_ARCH_PROTOCOL_PRIVATE *Private;
284 
285   Private                 = CPU_ARCH_PROTOCOL_PRIVATE_DATA_FROM_THIS (This);
286   Private->InterruptState = TRUE;
287   gEmuThunk->EnableInterrupt ();
288   return EFI_SUCCESS;
289 }
290 
291 EFI_STATUS
292 EFIAPI
EmuDisableInterrupt(IN EFI_CPU_ARCH_PROTOCOL * This)293 EmuDisableInterrupt (
294   IN EFI_CPU_ARCH_PROTOCOL  *This
295   )
296 {
297   CPU_ARCH_PROTOCOL_PRIVATE *Private;
298 
299   Private                 = CPU_ARCH_PROTOCOL_PRIVATE_DATA_FROM_THIS (This);
300   Private->InterruptState = FALSE;
301   gEmuThunk->DisableInterrupt ();
302   return EFI_SUCCESS;
303 }
304 
305 EFI_STATUS
306 EFIAPI
EmuGetInterruptState(IN EFI_CPU_ARCH_PROTOCOL * This,OUT BOOLEAN * State)307 EmuGetInterruptState (
308   IN EFI_CPU_ARCH_PROTOCOL  *This,
309   OUT BOOLEAN               *State
310   )
311 {
312   CPU_ARCH_PROTOCOL_PRIVATE *Private;
313 
314   if (State == NULL) {
315     return EFI_INVALID_PARAMETER;
316   }
317 
318   Private = CPU_ARCH_PROTOCOL_PRIVATE_DATA_FROM_THIS (This);
319   *State  = Private->InterruptState;
320   return EFI_SUCCESS;
321 }
322 
323 EFI_STATUS
324 EFIAPI
EmuInit(IN EFI_CPU_ARCH_PROTOCOL * This,IN EFI_CPU_INIT_TYPE InitType)325 EmuInit (
326   IN EFI_CPU_ARCH_PROTOCOL  *This,
327   IN EFI_CPU_INIT_TYPE      InitType
328   )
329 {
330   CPU_ARCH_PROTOCOL_PRIVATE *Private;
331 
332   Private = CPU_ARCH_PROTOCOL_PRIVATE_DATA_FROM_THIS (This);
333   return EFI_UNSUPPORTED;
334 }
335 
336 EFI_STATUS
337 EFIAPI
EmuRegisterInterruptHandler(IN EFI_CPU_ARCH_PROTOCOL * This,IN EFI_EXCEPTION_TYPE InterruptType,IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler)338 EmuRegisterInterruptHandler (
339   IN EFI_CPU_ARCH_PROTOCOL      *This,
340   IN EFI_EXCEPTION_TYPE         InterruptType,
341   IN EFI_CPU_INTERRUPT_HANDLER  InterruptHandler
342   )
343 {
344   CPU_ARCH_PROTOCOL_PRIVATE *Private;
345 
346   //
347   // Do parameter checking for EFI spec conformance
348   //
349   if (InterruptType < 0 || InterruptType > 0xff) {
350     return EFI_UNSUPPORTED;
351   }
352   //
353   // Do nothing for Emu emulation
354   //
355   Private = CPU_ARCH_PROTOCOL_PRIVATE_DATA_FROM_THIS (This);
356   return EFI_UNSUPPORTED;
357 }
358 
359 EFI_STATUS
360 EFIAPI
EmuGetTimerValue(IN EFI_CPU_ARCH_PROTOCOL * This,IN UINT32 TimerIndex,OUT UINT64 * TimerValue,OUT UINT64 * TimerPeriod OPTIONAL)361 EmuGetTimerValue (
362   IN  EFI_CPU_ARCH_PROTOCOL *This,
363   IN  UINT32                TimerIndex,
364   OUT UINT64                *TimerValue,
365   OUT UINT64                *TimerPeriod OPTIONAL
366   )
367 {
368   if (TimerValue == NULL) {
369     return EFI_INVALID_PARAMETER;
370   }
371 
372   if (TimerIndex != 0) {
373     return EFI_INVALID_PARAMETER;
374   }
375 
376   *TimerValue = gEmuThunk->QueryPerformanceCounter ();
377 
378   if (TimerPeriod != NULL) {
379     *TimerPeriod = mTimerPeriod;
380   }
381 
382   return EFI_SUCCESS;
383 }
384 
385 
386 EFI_STATUS
387 EFIAPI
EmuSetMemoryAttributes(IN EFI_CPU_ARCH_PROTOCOL * This,IN EFI_PHYSICAL_ADDRESS BaseAddress,IN UINT64 Length,IN UINT64 Attributes)388 EmuSetMemoryAttributes (
389   IN EFI_CPU_ARCH_PROTOCOL  *This,
390   IN EFI_PHYSICAL_ADDRESS   BaseAddress,
391   IN UINT64                 Length,
392   IN UINT64                 Attributes
393   )
394 {
395   CPU_ARCH_PROTOCOL_PRIVATE *Private;
396 
397   //
398   // Check for invalid parameter for Spec conformance
399   //
400   if (Length == 0) {
401     return EFI_INVALID_PARAMETER;
402   }
403 
404   //
405   // Do nothing for Nt32 emulation
406   //
407   Private = CPU_ARCH_PROTOCOL_PRIVATE_DATA_FROM_THIS (This);
408   return EFI_UNSUPPORTED;
409 }
410 
411 
412 
413 
414 /**
415   Callback function for idle events.
416 
417   @param  Event                 Event whose notification function is being invoked.
418   @param  Context               The pointer to the notification function's context,
419                                 which is implementation-dependent.
420 
421 **/
422 VOID
423 EFIAPI
IdleLoopEventCallback(IN EFI_EVENT Event,IN VOID * Context)424 IdleLoopEventCallback (
425   IN EFI_EVENT                Event,
426   IN VOID                     *Context
427   )
428 {
429   gEmuThunk->CpuSleep ();
430 }
431 
432 
433 EFI_STATUS
434 EFIAPI
InitializeCpu(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)435 InitializeCpu (
436   IN EFI_HANDLE        ImageHandle,
437   IN EFI_SYSTEM_TABLE  *SystemTable
438   )
439 {
440   EFI_STATUS    Status;
441   UINT64        Frequency;
442   EFI_EVENT     IdleLoopEvent;
443   UINTN         MaxCpu;
444 
445   //
446   // Retrieve the frequency of the performance counter in Hz.
447   //
448   Frequency = gEmuThunk->QueryPerformanceFrequency ();
449 
450   //
451   // Convert frequency in Hz to a clock period in femtoseconds.
452   //
453   mTimerPeriod = DivU64x64Remainder (1000000000000000ULL, Frequency, NULL);
454 
455   CpuMpServicesInit (&MaxCpu);
456 
457   CpuUpdateSmbios (MaxCpu);
458 
459 
460   Status = gBS->CreateEventEx (
461                   EVT_NOTIFY_SIGNAL,
462                   TPL_NOTIFY,
463                   IdleLoopEventCallback,
464                   NULL,
465                   &gIdleLoopEventGuid,
466                   &IdleLoopEvent
467                   );
468   ASSERT_EFI_ERROR (Status);
469 
470 
471   Status = gBS->InstallMultipleProtocolInterfaces (
472                   &mCpuTemplate.Handle,
473                   &gEfiCpuArchProtocolGuid,   &mCpuTemplate.Cpu,
474                   &gEfiCpuIo2ProtocolGuid,    &mCpuTemplate.CpuIo,
475                   NULL
476                   );
477   ASSERT_EFI_ERROR (Status);
478 
479   return Status;
480 }
481