a3317526aa770c11a53e0ec22760205d6a968838
[mirror_edk2.git] / Nt32Pkg / CpuRuntimeDxe / Cpu.c
1 /*++
2
3 Copyright (c) 2006 - 2007, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12 Module Name:
13
14 Cpu.c
15
16 Abstract:
17
18 NT Emulation Architectural Protocol Driver as defined in Tiano.
19 This CPU module abstracts the interrupt subsystem of a platform and
20 the CPU-specific setjump/long pair. Other services are not implemented
21 in this driver.
22
23 --*/
24
25
26 #include "CpuDriver.h"
27
28 #define EFI_CPU_DATA_MAXIMUM_LENGTH 0x100
29
30 EFI_STATUS
31 EFIAPI
32 InitializeCpu (
33 IN EFI_HANDLE ImageHandle,
34 IN EFI_SYSTEM_TABLE *SystemTable
35 );
36
37 VOID
38 EFIAPI
39 WinNtIoProtocolNotifyFunction (
40 IN EFI_EVENT Event,
41 IN VOID *Context
42 );
43
44 typedef union {
45 EFI_CPU_DATA_RECORD *DataRecord;
46 UINT8 *Raw;
47 } EFI_CPU_DATA_RECORD_BUFFER;
48
49 EFI_SUBCLASS_TYPE1_HEADER mCpuDataRecordHeader = {
50 EFI_PROCESSOR_SUBCLASS_VERSION, // Version
51 sizeof (EFI_SUBCLASS_TYPE1_HEADER), // Header Size
52 0, // Instance, Initialize later
53 EFI_SUBCLASS_INSTANCE_NON_APPLICABLE, // SubInstance
54 0 // RecordType, Initialize later
55 };
56
57 //
58 // Service routines for the driver
59 //
60 STATIC
61 EFI_STATUS
62 EFIAPI
63 WinNtFlushCpuDataCache (
64 IN EFI_CPU_ARCH_PROTOCOL *This,
65 IN EFI_PHYSICAL_ADDRESS Start,
66 IN UINT64 Length,
67 IN EFI_CPU_FLUSH_TYPE FlushType
68 )
69 /*++
70
71 Routine Description:
72
73 This routine would provide support for flushing the CPU data cache.
74 In the case of NT emulation environment, this flushing is not necessary and
75 is thus not implemented.
76
77 Arguments:
78
79 Pointer to CPU Architectural Protocol interface
80 Start adddress in memory to flush
81 Length of memory to flush
82 Flush type
83
84 Returns:
85
86 Status
87 EFI_SUCCESS
88
89 --*/
90 // TODO: This - add argument and description to function comment
91 // TODO: FlushType - add argument and description to function comment
92 // TODO: EFI_UNSUPPORTED - add return value to function comment
93 {
94 if (FlushType == EfiCpuFlushTypeWriteBackInvalidate) {
95 //
96 // Only WB flush is supported. We actually need do nothing on NT emulator
97 // environment. Classify this to follow EFI spec
98 //
99 return EFI_SUCCESS;
100 }
101 //
102 // Other flush types are not supported by NT emulator
103 //
104 return EFI_UNSUPPORTED;
105 }
106
107 STATIC
108 EFI_STATUS
109 EFIAPI
110 WinNtEnableInterrupt (
111 IN EFI_CPU_ARCH_PROTOCOL *This
112 )
113 /*++
114
115 Routine Description:
116
117 This routine provides support for emulation of the interrupt enable of the
118 the system. For our purposes, CPU enable is just a BOOLEAN that the Timer
119 Architectural Protocol observes in order to defer behaviour while in its
120 emulated interrupt, or timer tick.
121
122 Arguments:
123
124 Pointer to CPU Architectural Protocol interface
125
126 Returns:
127
128 Status
129 EFI_SUCCESS
130
131 --*/
132 // TODO: This - add argument and description to function comment
133 {
134 CPU_ARCH_PROTOCOL_PRIVATE *Private;
135
136 Private = CPU_ARCH_PROTOCOL_PRIVATE_DATA_FROM_THIS (This);
137 Private->InterruptState = TRUE;
138 return EFI_SUCCESS;
139 }
140
141 STATIC
142 EFI_STATUS
143 EFIAPI
144 WinNtDisableInterrupt (
145 IN EFI_CPU_ARCH_PROTOCOL *This
146 )
147 /*++
148
149 Routine Description:
150
151 This routine provides support for emulation of the interrupt disable of the
152 the system. For our purposes, CPU enable is just a BOOLEAN that the Timer
153 Architectural Protocol observes in order to defer behaviour while in its
154 emulated interrupt, or timer tick.
155
156 Arguments:
157
158 Pointer to CPU Architectural Protocol interface
159
160 Returns:
161
162 Status
163 EFI_SUCCESS
164
165 --*/
166 // TODO: This - add argument and description to function comment
167 {
168 CPU_ARCH_PROTOCOL_PRIVATE *Private;
169
170 Private = CPU_ARCH_PROTOCOL_PRIVATE_DATA_FROM_THIS (This);
171 Private->InterruptState = FALSE;
172 return EFI_SUCCESS;
173 }
174
175 STATIC
176 EFI_STATUS
177 EFIAPI
178 WinNtGetInterruptState (
179 IN EFI_CPU_ARCH_PROTOCOL *This,
180 OUT BOOLEAN *State
181 )
182 /*++
183
184 Routine Description:
185
186 This routine provides support for emulation of the interrupt disable of the
187 the system. For our purposes, CPU enable is just a BOOLEAN that the Timer
188 Architectural Protocol observes in order to defer behaviour while in its
189 emulated interrupt, or timer tick.
190
191 Arguments:
192
193 Pointer to CPU Architectural Protocol interface
194
195 Returns:
196
197 Status
198 EFI_SUCCESS
199
200 --*/
201 // TODO: This - add argument and description to function comment
202 // TODO: State - add argument and description to function comment
203 // TODO: EFI_INVALID_PARAMETER - add return value to function comment
204 {
205 CPU_ARCH_PROTOCOL_PRIVATE *Private;
206
207 if (State == NULL) {
208 return EFI_INVALID_PARAMETER;
209 }
210
211 Private = CPU_ARCH_PROTOCOL_PRIVATE_DATA_FROM_THIS (This);
212 *State = Private->InterruptState;
213 return EFI_SUCCESS;
214 }
215
216 STATIC
217 EFI_STATUS
218 EFIAPI
219 WinNtInit (
220 IN EFI_CPU_ARCH_PROTOCOL *This,
221 IN EFI_CPU_INIT_TYPE InitType
222 )
223 /*++
224
225 Routine Description:
226
227 This routine would support generation of a CPU INIT. At
228 present, this code does not provide emulation.
229
230 Arguments:
231
232 Pointer to CPU Architectural Protocol interface
233 INIT Type
234
235 Returns:
236
237 Status
238 EFI_UNSUPPORTED - not yet implemented
239
240 --*/
241 // TODO: This - add argument and description to function comment
242 // TODO: InitType - add argument and description to function comment
243 {
244 CPU_ARCH_PROTOCOL_PRIVATE *Private;
245
246 Private = CPU_ARCH_PROTOCOL_PRIVATE_DATA_FROM_THIS (This);
247 return EFI_UNSUPPORTED;
248 }
249
250 STATIC
251 EFI_STATUS
252 EFIAPI
253 WinNtRegisterInterruptHandler (
254 IN EFI_CPU_ARCH_PROTOCOL *This,
255 IN EFI_EXCEPTION_TYPE InterruptType,
256 IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
257 )
258 /*++
259
260 Routine Description:
261
262 This routine would support registration of an interrupt handler. At
263 present, this code does not provide emulation.
264
265 Arguments:
266
267 Pointer to CPU Architectural Protocol interface
268 Pointer to interrupt handlers
269 Interrupt type
270
271 Returns:
272
273 Status
274 EFI_UNSUPPORTED - not yet implemented
275
276 --*/
277 // TODO: This - add argument and description to function comment
278 // TODO: InterruptType - add argument and description to function comment
279 // TODO: InterruptHandler - add argument and description to function comment
280 {
281 CPU_ARCH_PROTOCOL_PRIVATE *Private;
282
283 //
284 // Do parameter checking for EFI spec conformance
285 //
286 if (InterruptType < 0 || InterruptType > 0xff) {
287 return EFI_UNSUPPORTED;
288 }
289 //
290 // Do nothing for Nt32 emulation
291 //
292 Private = CPU_ARCH_PROTOCOL_PRIVATE_DATA_FROM_THIS (This);
293 return EFI_UNSUPPORTED;
294 }
295
296 STATIC
297 EFI_STATUS
298 EFIAPI
299 WinNtGetTimerValue (
300 IN EFI_CPU_ARCH_PROTOCOL *This,
301 IN UINT32 TimerIndex,
302 OUT UINT64 *TimerValue,
303 OUT UINT64 *TimerPeriod OPTIONAL
304 )
305 /*++
306
307 Routine Description:
308
309 This routine would support querying of an on-CPU timer. At present,
310 this code does not provide timer emulation.
311
312 Arguments:
313
314 This - Pointer to CPU Architectural Protocol interface
315 TimerIndex - Index of given CPU timer
316 TimerValue - Output of the timer
317 TimerPeriod - Output of the timer period
318
319 Returns:
320
321 EFI_UNSUPPORTED - not yet implemented
322 EFI_INVALID_PARAMETER - TimeValue is NULL
323
324 --*/
325 {
326 if (TimerValue == NULL) {
327 return EFI_INVALID_PARAMETER;
328 }
329
330 //
331 // No timer supported
332 //
333 return EFI_UNSUPPORTED;
334 }
335
336 STATIC
337 EFI_STATUS
338 EFIAPI
339 WinNtSetMemoryAttributes (
340 IN EFI_CPU_ARCH_PROTOCOL *This,
341 IN EFI_PHYSICAL_ADDRESS BaseAddress,
342 IN UINT64 Length,
343 IN UINT64 Attributes
344 )
345 /*++
346
347 Routine Description:
348
349 This routine would support querying of an on-CPU timer. At present,
350 this code does not provide timer emulation.
351
352 Arguments:
353
354 Pointer to CPU Architectural Protocol interface
355 Start address of memory region
356 The size in bytes of the memory region
357 The bit mask of attributes to set for the memory region
358
359 Returns:
360
361 Status
362 EFI_UNSUPPORTED - not yet implemented
363
364 --*/
365 // TODO: This - add argument and description to function comment
366 // TODO: BaseAddress - add argument and description to function comment
367 // TODO: Length - add argument and description to function comment
368 // TODO: Attributes - add argument and description to function comment
369 // TODO: EFI_INVALID_PARAMETER - add return value to function comment
370 {
371 CPU_ARCH_PROTOCOL_PRIVATE *Private;
372
373 //
374 // Check for invalid parameter for Spec conformance
375 //
376 if (Length == 0) {
377 return EFI_INVALID_PARAMETER;
378 }
379
380 //
381 // Do nothing for Nt32 emulation
382 //
383 Private = CPU_ARCH_PROTOCOL_PRIVATE_DATA_FROM_THIS (This);
384 return EFI_UNSUPPORTED;
385 }
386
387
388 EFI_STATUS
389 EFIAPI
390 InitializeCpu (
391 IN EFI_HANDLE ImageHandle,
392 IN EFI_SYSTEM_TABLE *SystemTable
393 )
394 /*++
395
396 Routine Description:
397
398 Initialize the state information for the CPU Architectural Protocol
399
400 Arguments:
401
402 ImageHandle of the loaded driver
403 Pointer to the System Table
404
405 Returns:
406
407 Status
408
409 EFI_SUCCESS - protocol instance can be published
410 EFI_OUT_OF_RESOURCES - cannot allocate protocol data structure
411 EFI_DEVICE_ERROR - cannot create the thread
412
413 --*/
414 // TODO: SystemTable - add argument and description to function comment
415 {
416 EFI_STATUS Status;
417 EFI_EVENT Event;
418 CPU_ARCH_PROTOCOL_PRIVATE *Private;
419 VOID *Registration;
420
421 Private = AllocatePool (sizeof (CPU_ARCH_PROTOCOL_PRIVATE));
422 ASSERT (Private != NULL);
423
424 Private->Signature = CPU_ARCH_PROT_PRIVATE_SIGNATURE;
425 Private->Cpu.FlushDataCache = WinNtFlushCpuDataCache;
426 Private->Cpu.EnableInterrupt = WinNtEnableInterrupt;
427 Private->Cpu.DisableInterrupt = WinNtDisableInterrupt;
428 Private->Cpu.GetInterruptState = WinNtGetInterruptState;
429 Private->Cpu.Init = WinNtInit;
430 Private->Cpu.RegisterInterruptHandler = WinNtRegisterInterruptHandler;
431 Private->Cpu.GetTimerValue = WinNtGetTimerValue;
432 Private->Cpu.SetMemoryAttributes = WinNtSetMemoryAttributes;
433
434 Private->Cpu.NumberOfTimers = 0;
435 Private->Cpu.DmaBufferAlignment = 4;
436
437 Private->InterruptState = TRUE;
438
439 Private->CpuIo.Mem.Read = CpuMemoryServiceRead;
440 Private->CpuIo.Mem.Write = CpuMemoryServiceWrite;
441 Private->CpuIo.Io.Read = CpuIoServiceRead;
442 Private->CpuIo.Io.Write = CpuIoServiceWrite;
443
444
445 Private->Handle = NULL;
446 Status = gBS->InstallMultipleProtocolInterfaces (
447 &Private->Handle,
448 &gEfiCpuArchProtocolGuid, &Private->Cpu,
449 &gEfiCpuIoProtocolGuid, &Private->CpuIo,
450 NULL
451 );
452 ASSERT_EFI_ERROR (Status);
453
454 //
455 // Install notify function to store processor data to HII database and data hub.
456 //
457 Status = gBS->CreateEvent (
458 EVT_NOTIFY_SIGNAL,
459 TPL_CALLBACK,
460 WinNtIoProtocolNotifyFunction,
461 ImageHandle,
462 &Event
463 );
464 ASSERT (!EFI_ERROR (Status));
465
466 Status = gBS->RegisterProtocolNotify (
467 &gEfiWinNtIoProtocolGuid,
468 Event,
469 &Registration
470 );
471 ASSERT (!EFI_ERROR (Status));
472
473 //
474 // Should be at EFI_D_INFO, but lets us now things are running
475 //
476 DEBUG ((EFI_D_ERROR, "CPU Architectural Protocol Loaded\n"));
477
478
479
480 return Status;
481 }
482
483 UINTN
484 Atoi (
485 CHAR16 *String
486 )
487 /*++
488
489 Routine Description:
490 Convert a unicode string to a UINTN
491
492 Arguments:
493 String - Unicode string.
494
495 Returns:
496 UINTN of the number represented by String.
497
498 --*/
499 {
500 UINTN Number;
501 CHAR16 *Str;
502
503 //
504 // skip preceeding white space
505 //
506 Str = String;
507 while ((*Str) && (*Str == ' ' || *Str == '"')) {
508 Str++;
509 }
510 //
511 // Convert ot a Number
512 //
513 Number = 0;
514 while (*Str != '\0') {
515 if ((*Str >= '0') && (*Str <= '9')) {
516 Number = (Number * 10) +*Str - '0';
517 } else {
518 break;
519 }
520
521 Str++;
522 }
523
524 return Number;
525 }
526
527 VOID
528 EFIAPI
529 WinNtIoProtocolNotifyFunction (
530 IN EFI_EVENT Event,
531 IN VOID *Context
532 )
533 /*++
534
535 Routine Description:
536 This function will log processor version and frequency data to data hub.
537
538 Arguments:
539 Event - Event whose notification function is being invoked.
540 Context - Pointer to the notification function's context.
541
542 Returns:
543 None.
544
545 --*/
546 {
547 EFI_STATUS Status;
548 EFI_CPU_DATA_RECORD_BUFFER RecordBuffer;
549 EFI_DATA_RECORD_HEADER *Record;
550 EFI_SUBCLASS_TYPE1_HEADER *DataHeader;
551 UINT32 HeaderSize;
552 UINT32 TotalSize;
553 UINTN HandleCount;
554 UINTN HandleIndex;
555 UINT64 MonotonicCount;
556 BOOLEAN RecordFound;
557 EFI_HANDLE *HandleBuffer;
558 EFI_WIN_NT_IO_PROTOCOL *WinNtIo;
559 EFI_DATA_HUB_PROTOCOL *DataHub;
560 EFI_HII_PROTOCOL *Hii;
561 EFI_HII_HANDLE StringHandle;
562 EFI_HII_PACKAGES *PackageList;
563 STRING_REF Token;
564
565 DataHub = NULL;
566 Token = 0;
567 MonotonicCount = 0;
568 RecordFound = FALSE;
569
570 //
571 // Retrieve the list of all handles from the handle database
572 //
573 Status = gBS->LocateHandleBuffer (
574 AllHandles,
575 &gEfiWinNtIoProtocolGuid,
576 NULL,
577 &HandleCount,
578 &HandleBuffer
579 );
580 if (EFI_ERROR (Status)) {
581 return ;
582 }
583 //
584 // Locate HII protocol
585 //
586 Status = gBS->LocateProtocol (&gEfiHiiProtocolGuid, NULL, &Hii);
587 if (EFI_ERROR (Status)) {
588 return ;
589 }
590 //
591 // Locate DataHub protocol.
592 //
593 Status = gBS->LocateProtocol (&gEfiDataHubProtocolGuid, NULL, &DataHub);
594 if (EFI_ERROR (Status)) {
595 return ;
596 }
597 //
598 // Initialize data record header
599 //
600 mCpuDataRecordHeader.Instance = 1;
601 HeaderSize = sizeof (EFI_SUBCLASS_TYPE1_HEADER);
602
603 RecordBuffer.Raw = AllocatePool (HeaderSize + EFI_CPU_DATA_MAXIMUM_LENGTH);
604 if (RecordBuffer.Raw == NULL) {
605 return ;
606 }
607
608 CopyMem (RecordBuffer.Raw, &mCpuDataRecordHeader, HeaderSize);
609
610 //
611 // Search the Handle array to find the CPU model and speed information
612 //
613 for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
614 Status = gBS->OpenProtocol (
615 HandleBuffer[HandleIndex],
616 &gEfiWinNtIoProtocolGuid,
617 &WinNtIo,
618 Context,
619 NULL,
620 EFI_OPEN_PROTOCOL_GET_PROTOCOL
621 );
622 if (EFI_ERROR (Status)) {
623 continue;
624 }
625
626 if ((WinNtIo->WinNtThunk->Signature == EFI_WIN_NT_THUNK_PROTOCOL_SIGNATURE) &&
627 CompareGuid (WinNtIo->TypeGuid, &gEfiWinNtCPUModelGuid)
628 ) {
629 //
630 // Check if this record has been stored in data hub
631 //
632 do {
633 Status = DataHub->GetNextRecord (DataHub, &MonotonicCount, NULL, &Record);
634 if (Record->DataRecordClass == EFI_DATA_RECORD_CLASS_DATA) {
635 DataHeader = (EFI_SUBCLASS_TYPE1_HEADER *) (Record + 1);
636 if (CompareGuid (&Record->DataRecordGuid, &gEfiProcessorSubClassGuid) &&
637 (DataHeader->RecordType == ProcessorVersionRecordType)
638 ) {
639 RecordFound = TRUE;
640 }
641 }
642 } while (MonotonicCount != 0);
643
644 if (RecordFound) {
645 RecordFound = FALSE;
646 continue;
647 }
648 //
649 // Initialize strings to HII database
650 //
651 PackageList = PreparePackages (1, &gEfiProcessorProducerGuid, CpuStrings);
652
653 Status = Hii->NewPack (Hii, PackageList, &StringHandle);
654 ASSERT (!EFI_ERROR (Status));
655
656 FreePool (PackageList);
657
658 //
659 // Store processor version data record to data hub
660 //
661 Status = Hii->NewString (Hii, NULL, StringHandle, &Token, WinNtIo->EnvString);
662 ASSERT (!EFI_ERROR (Status));
663
664 RecordBuffer.DataRecord->DataRecordHeader.RecordType = ProcessorVersionRecordType;
665 RecordBuffer.DataRecord->VariableRecord.ProcessorVersion = Token;
666 TotalSize = HeaderSize + sizeof (EFI_PROCESSOR_VERSION_DATA);
667
668 Status = DataHub->LogData (
669 DataHub,
670 &gEfiProcessorSubClassGuid,
671 &gEfiProcessorProducerGuid,
672 EFI_DATA_RECORD_CLASS_DATA,
673 RecordBuffer.Raw,
674 TotalSize
675 );
676 }
677
678 if ((WinNtIo->WinNtThunk->Signature == EFI_WIN_NT_THUNK_PROTOCOL_SIGNATURE) &&
679 CompareGuid (WinNtIo->TypeGuid, &gEfiWinNtCPUSpeedGuid)
680 ) {
681 //
682 // Check if this record has been stored in data hub
683 //
684 do {
685 Status = DataHub->GetNextRecord (DataHub, &MonotonicCount, NULL, &Record);
686 if (Record->DataRecordClass == EFI_DATA_RECORD_CLASS_DATA) {
687 DataHeader = (EFI_SUBCLASS_TYPE1_HEADER *) (Record + 1);
688 if (CompareGuid (&Record->DataRecordGuid, &gEfiProcessorSubClassGuid) &&
689 (DataHeader->RecordType == ProcessorCoreFrequencyRecordType)
690 ) {
691 RecordFound = TRUE;
692 }
693 }
694 } while (MonotonicCount != 0);
695
696 if (RecordFound) {
697 RecordFound = FALSE;
698 continue;
699 }
700 //
701 // Store CPU frequency data record to data hub
702 //
703 RecordBuffer.DataRecord->DataRecordHeader.RecordType = ProcessorCoreFrequencyRecordType;
704 RecordBuffer.DataRecord->VariableRecord.ProcessorCoreFrequency.Value = (UINT16) Atoi (WinNtIo->EnvString);
705 RecordBuffer.DataRecord->VariableRecord.ProcessorCoreFrequency.Exponent = 6;
706 TotalSize = HeaderSize + sizeof (EFI_PROCESSOR_CORE_FREQUENCY_DATA);
707
708 Status = DataHub->LogData (
709 DataHub,
710 &gEfiProcessorSubClassGuid,
711 &gEfiProcessorProducerGuid,
712 EFI_DATA_RECORD_CLASS_DATA,
713 RecordBuffer.Raw,
714 TotalSize
715 );
716
717 FreePool (RecordBuffer.Raw);
718 }
719
720 gBS->CloseProtocol (
721 HandleBuffer[HandleIndex],
722 &gEfiWinNtIoProtocolGuid,
723 Context,
724 NULL
725 );
726 }
727 }