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