/** Lowers the task priority to the previous value. If the new priority unmasks events at a higher priority, they are dispatched. @param NewTpl New, lower, task priority **/ VOID EFIAPI CoreRestoreTpl ( IN EFI_TPL NewTpl ) { EFI_TPL OldTpl; EFI_TPL PendingTpl; OldTpl = gEfiCurrentTpl; if (NewTpl > OldTpl) { DEBUG ((EFI_D_ERROR, "FATAL ERROR - RestoreTpl with NewTpl(0x%x) > OldTpl(0x%x)\n", NewTpl, OldTpl)); ASSERT (FALSE); } ASSERT (VALID_TPL (NewTpl)); // // If lowering below HIGH_LEVEL, make sure // interrupts are enabled // if (OldTpl >= TPL_HIGH_LEVEL && NewTpl < TPL_HIGH_LEVEL) { gEfiCurrentTpl = TPL_HIGH_LEVEL; } // // Dispatch any pending events // while (gEventPending != 0) { PendingTpl = (UINTN) HighBitSet64 (gEventPending); if (PendingTpl <= NewTpl) { break; } gEfiCurrentTpl = PendingTpl; if (gEfiCurrentTpl < TPL_HIGH_LEVEL) { CoreSetInterruptState (TRUE); } CoreDispatchEventNotifies (gEfiCurrentTpl); } // // Set the new value // gEfiCurrentTpl = NewTpl; // // If lowering below HIGH_LEVEL, make sure // interrupts are enabled // if (gEfiCurrentTpl < TPL_HIGH_LEVEL) { CoreSetInterruptState (TRUE); } }
/** Returns the value of the highest bit set in a 64-bit value. Equivalent to 1 << log2(x). This function computes the value of the highest bit set in the 64-bit value specified by Operand. If Operand is zero, then zero is returned. @param Operand The 64-bit operand to evaluate. @return 1 << HighBitSet64(Operand) @retval 0 Operand is zero. **/ UINT64 EFIAPI GetPowerOfTwo64 ( IN UINT64 Operand ) { if (Operand == 0) { return 0; } return LShiftU64 (1, (UINTN) HighBitSet64 (Operand)); }
/** Lowers the task priority to the previous value. If the new priority unmasks events at a higher priority, they are dispatched. @param NewTpl New, lower, task priority **/ VOID EFIAPI CoreRestoreTpl ( IN EFI_TPL NewTpl ) { EFI_TPL OldTpl; OldTpl = gEfiCurrentTpl; ASSERT (NewTpl <= OldTpl); ASSERT (VALID_TPL (NewTpl)); // // If lowering below HIGH_LEVEL, make sure // interrupts are enabled // if (OldTpl >= TPL_HIGH_LEVEL && NewTpl < TPL_HIGH_LEVEL) { gEfiCurrentTpl = TPL_HIGH_LEVEL; } // // Dispatch any pending events // while (((-2 << NewTpl) & gEventPending) != 0) { gEfiCurrentTpl = HighBitSet64 (gEventPending); if (gEfiCurrentTpl < TPL_HIGH_LEVEL) { CoreSetInterruptState (TRUE); } CoreDispatchEventNotifies (gEfiCurrentTpl); } // // Set the new value // gEfiCurrentTpl = NewTpl; // // If lowering below HIGH_LEVEL, make sure // interrupts are enabled // if (gEfiCurrentTpl < TPL_HIGH_LEVEL) { CoreSetInterruptState (TRUE); } }
/** Update page table according to protected memory ranges and the 4KB-page mapped memory ranges. **/ VOID InitPaging ( VOID ) { UINT64 *Pml4; UINT64 *Pde; UINT64 *Pte; UINT64 *Pt; UINTN Address; UINTN Level1; UINTN Level2; UINTN Level3; UINTN Level4; UINTN NumberOfPdpEntries; UINTN NumberOfPml4Entries; UINTN SizeOfMemorySpace; BOOLEAN Nx; if (sizeof (UINTN) == sizeof (UINT64)) { Pml4 = (UINT64*)(UINTN)mSmmProfileCr3; SizeOfMemorySpace = HighBitSet64 (gPhyMask) + 1; // // Calculate the table entries of PML4E and PDPTE. // if (SizeOfMemorySpace <= 39 ) { NumberOfPml4Entries = 1; NumberOfPdpEntries = (UINT32)LShiftU64 (1, (SizeOfMemorySpace - 30)); } else { NumberOfPml4Entries = (UINT32)LShiftU64 (1, (SizeOfMemorySpace - 39)); NumberOfPdpEntries = 512; } } else { NumberOfPml4Entries = 1; NumberOfPdpEntries = 4; } // // Go through page table and change 2MB-page into 4KB-page. // for (Level1 = 0; Level1 < NumberOfPml4Entries; Level1++) { if (sizeof (UINTN) == sizeof (UINT64)) { if ((Pml4[Level1] & IA32_PG_P) == 0) { // // If Pml4 entry does not exist, skip it // continue; } Pde = (UINT64 *)(UINTN)(Pml4[Level1] & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK); } else { Pde = (UINT64*)(UINTN)mSmmProfileCr3; } for (Level2 = 0; Level2 < NumberOfPdpEntries; Level2++, Pde++) { if ((*Pde & IA32_PG_P) == 0) { // // If PDE entry does not exist, skip it // continue; } if ((*Pde & IA32_PG_PS) != 0) { // // This is 1G entry, skip it // continue; } Pte = (UINT64 *)(UINTN)(*Pde & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK); if (Pte == 0) { continue; } for (Level3 = 0; Level3 < SIZE_4KB / sizeof (*Pte); Level3++, Pte++) { if ((*Pte & IA32_PG_P) == 0) { // // If PTE entry does not exist, skip it // continue; } Address = (((Level2 << 9) + Level3) << 21); // // If it is 2M page, check IsAddressSplit() // if (((*Pte & IA32_PG_PS) != 0) && IsAddressSplit (Address)) { // // Based on current page table, create 4KB page table for split area. // ASSERT (Address == (*Pte & PHYSICAL_ADDRESS_MASK)); Pt = AllocatePageTableMemory (1); ASSERT (Pt != NULL); // Split it for (Level4 = 0; Level4 < SIZE_4KB / sizeof(*Pt); Level4++) { Pt[Level4] = Address + ((Level4 << 12) | mAddressEncMask | PAGE_ATTRIBUTE_BITS); } // end for PT *Pte = (UINT64)(UINTN)Pt | mAddressEncMask | PAGE_ATTRIBUTE_BITS; } // end if IsAddressSplit } // end for PTE } // end for PDE } // // Go through page table and set several page table entries to absent or execute-disable. // DEBUG ((EFI_D_INFO, "Patch page table start ...\n")); for (Level1 = 0; Level1 < NumberOfPml4Entries; Level1++) { if (sizeof (UINTN) == sizeof (UINT64)) { if ((Pml4[Level1] & IA32_PG_P) == 0) { // // If Pml4 entry does not exist, skip it // continue; } Pde = (UINT64 *)(UINTN)(Pml4[Level1] & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK); } else { Pde = (UINT64*)(UINTN)mSmmProfileCr3; } for (Level2 = 0; Level2 < NumberOfPdpEntries; Level2++, Pde++) { if ((*Pde & IA32_PG_P) == 0) { // // If PDE entry does not exist, skip it // continue; } if ((*Pde & IA32_PG_PS) != 0) { // // This is 1G entry, set NX bit and skip it // if (mXdSupported) { *Pde = *Pde | IA32_PG_NX; } continue; } Pte = (UINT64 *)(UINTN)(*Pde & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK); if (Pte == 0) { continue; } for (Level3 = 0; Level3 < SIZE_4KB / sizeof (*Pte); Level3++, Pte++) { if ((*Pte & IA32_PG_P) == 0) { // // If PTE entry does not exist, skip it // continue; } Address = (((Level2 << 9) + Level3) << 21); if ((*Pte & IA32_PG_PS) != 0) { // 2MB page if (!IsAddressValid (Address, &Nx)) { // // Patch to remove Present flag and RW flag // *Pte = *Pte & (INTN)(INT32)(~PAGE_ATTRIBUTE_BITS); } if (Nx && mXdSupported) { *Pte = *Pte | IA32_PG_NX; } } else { // 4KB page Pt = (UINT64 *)(UINTN)(*Pte & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK); if (Pt == 0) { continue; } for (Level4 = 0; Level4 < SIZE_4KB / sizeof(*Pt); Level4++, Pt++) { if (!IsAddressValid (Address, &Nx)) { *Pt = *Pt & (INTN)(INT32)(~PAGE_ATTRIBUTE_BITS); } if (Nx && mXdSupported) { *Pt = *Pt | IA32_PG_NX; } Address += SIZE_4KB; } // end for PT } // end if PS } // end for PTE } // end for PDE } // // Flush TLB // CpuFlushTlb (); DEBUG ((EFI_D_INFO, "Patch page table done!\n")); // // Set execute-disable flag // mXdEnabled = TRUE; return ; }
/** These are the notifications from the PCI bus driver that it is about to enter a certain phase of the PCI enumeration process. This member function can be used to notify the host bridge driver to perform specific actions, including any chipset-specific initialization, so that the chipset is ready to enter the next phase. Eight notification points are defined at this time. See belows: EfiPciHostBridgeBeginEnumeration Resets the host bridge PCI apertures and internal data structures. The PCI enumerator should issue this notification before starting a fresh enumeration process. Enumeration cannot be restarted after sending any other notification such as EfiPciHostBridgeBeginBusAllocation. EfiPciHostBridgeBeginBusAllocation The bus allocation phase is about to begin. No specific action is required here. This notification can be used to perform any chipset-specific programming. EfiPciHostBridgeEndBusAllocation The bus allocation and bus programming phase is complete. No specific action is required here. This notification can be used to perform any chipset-specific programming. EfiPciHostBridgeBeginResourceAllocation The resource allocation phase is about to begin. No specific action is required here. This notification can be used to perform any chipset-specific programming. EfiPciHostBridgeAllocateResources Allocates resources per previously submitted requests for all the PCI root bridges. These resource settings are returned on the next call to GetProposedResources(). Before calling NotifyPhase() with a Phase of EfiPciHostBridgeAllocateResource, the PCI bus enumerator is responsible for gathering I/O and memory requests for all the PCI root bridges and submitting these requests using SubmitResources(). This function pads the resource amount to suit the root bridge hardware, takes care of dependencies between the PCI root bridges, and calls the Global Coherency Domain (GCD) with the allocation request. In the case of padding, the allocated range could be bigger than what was requested. EfiPciHostBridgeSetResources Programs the host bridge hardware to decode previously allocated resources (proposed resources) for all the PCI root bridges. After the hardware is programmed, reassigning resources will not be supported. The bus settings are not affected. EfiPciHostBridgeFreeResources Deallocates resources that were previously allocated for all the PCI root bridges and resets the I/O and memory apertures to their initial state. The bus settings are not affected. If the request to allocate resources fails, the PCI enumerator can use this notification to deallocate previous resources, adjust the requests, and retry allocation. EfiPciHostBridgeEndResourceAllocation The resource allocation phase is completed. No specific action is required here. This notification can be used to perform any chipsetspecific programming. @param[in] This The instance pointer of EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL @param[in] Phase The phase during enumeration @retval EFI_NOT_READY This phase cannot be entered at this time. For example, this error is valid for a Phase of EfiPciHostBridgeAllocateResources if SubmitResources() has not been called for one or more PCI root bridges before this call @retval EFI_DEVICE_ERROR Programming failed due to a hardware error. This error is valid for a Phase of EfiPciHostBridgeSetResources. @retval EFI_INVALID_PARAMETER Invalid phase parameter @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. This error is valid for a Phase of EfiPciHostBridgeAllocateResources if the previously submitted resource requests cannot be fulfilled or were only partially fulfilled. @retval EFI_SUCCESS The notification was accepted without any errors. **/ EFI_STATUS EFIAPI NotifyPhase( IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This, IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PHASE Phase ) { PCI_HOST_BRIDGE_INSTANCE *HostBridgeInstance; PCI_ROOT_BRIDGE_INSTANCE *RootBridgeInstance; PCI_RESOURCE_TYPE Index; LIST_ENTRY *List; EFI_PHYSICAL_ADDRESS BaseAddress; UINT64 AddrLen; UINTN BitsOfAlignment; EFI_STATUS Status; EFI_STATUS ReturnStatus; HostBridgeInstance = INSTANCE_FROM_RESOURCE_ALLOCATION_THIS (This); switch (Phase) { case EfiPciHostBridgeBeginEnumeration: if (HostBridgeInstance->CanRestarted) { // // Reset the Each Root Bridge // List = HostBridgeInstance->Head.ForwardLink; while (List != &HostBridgeInstance->Head) { RootBridgeInstance = DRIVER_INSTANCE_FROM_LIST_ENTRY (List); for (Index = TypeIo; Index < TypeMax; Index++) { RootBridgeInstance->ResAllocNode[Index].Type = Index; RootBridgeInstance->ResAllocNode[Index].Base = 0; RootBridgeInstance->ResAllocNode[Index].Length = 0; RootBridgeInstance->ResAllocNode[Index].Status = ResNone; } List = List->ForwardLink; } HostBridgeInstance->ResourceSubmited = FALSE; HostBridgeInstance->CanRestarted = TRUE; } else { // // Can not restart // return EFI_NOT_READY; } break; case EfiPciHostBridgeEndEnumeration: break; case EfiPciHostBridgeBeginBusAllocation: // // No specific action is required here, can perform any chipset specific programing // HostBridgeInstance->CanRestarted = FALSE; break; case EfiPciHostBridgeEndBusAllocation: // // No specific action is required here, can perform any chipset specific programing // //HostBridgeInstance->CanRestarted = FALSE; break; case EfiPciHostBridgeBeginResourceAllocation: // // No specific action is required here, can perform any chipset specific programing // //HostBridgeInstance->CanRestarted = FALSE; break; case EfiPciHostBridgeAllocateResources: ReturnStatus = EFI_SUCCESS; if (HostBridgeInstance->ResourceSubmited) { // // Take care of the resource dependencies between the root bridges // List = HostBridgeInstance->Head.ForwardLink; while (List != &HostBridgeInstance->Head) { RootBridgeInstance = DRIVER_INSTANCE_FROM_LIST_ENTRY (List); for (Index = TypeIo; Index < TypeBus; Index++) { if (RootBridgeInstance->ResAllocNode[Index].Status != ResNone) { AddrLen = RootBridgeInstance->ResAllocNode[Index].Length; // // Get the number of '1' in Alignment. // BitsOfAlignment = (UINTN) (HighBitSet64 (RootBridgeInstance->ResAllocNode[Index].Alignment) + 1); switch (Index) { case TypeIo: // // It is impossible for this chipset to align 0xFFFF for IO16 // So clear it // if (BitsOfAlignment >= 16) { BitsOfAlignment = 0; } Status = gDS->AllocateIoSpace ( EfiGcdAllocateAnySearchBottomUp, EfiGcdIoTypeIo, BitsOfAlignment, AddrLen, &BaseAddress, mDriverImageHandle, NULL ); if (!EFI_ERROR (Status)) { RootBridgeInstance->ResAllocNode[Index].Base = (UINTN)BaseAddress; RootBridgeInstance->ResAllocNode[Index].Status = ResAllocated; } else { ReturnStatus = Status; if (Status != EFI_OUT_OF_RESOURCES) { RootBridgeInstance->ResAllocNode[Index].Length = 0; } } break; case TypeMem32: // // It is impossible for this chipset to align 0xFFFFFFFF for Mem32 // So clear it // if (BitsOfAlignment >= 32) { BitsOfAlignment = 0; } Status = gDS->AllocateMemorySpace ( EfiGcdAllocateAnySearchBottomUp, EfiGcdMemoryTypeMemoryMappedIo, BitsOfAlignment, AddrLen, &BaseAddress, mDriverImageHandle, NULL ); if (!EFI_ERROR (Status)) { // We were able to allocate the PCI memory RootBridgeInstance->ResAllocNode[Index].Base = (UINTN)BaseAddress; RootBridgeInstance->ResAllocNode[Index].Status = ResAllocated; } else { // Not able to allocate enough PCI memory ReturnStatus = Status; if (Status != EFI_OUT_OF_RESOURCES) { RootBridgeInstance->ResAllocNode[Index].Length = 0; } ASSERT (FALSE); } break; case TypePMem32: case TypeMem64: case TypePMem64: ReturnStatus = EFI_ABORTED; break; default: ASSERT (FALSE); break; }; //end switch } } List = List->ForwardLink; } return ReturnStatus; } else { return EFI_NOT_READY; } break; case EfiPciHostBridgeSetResources: break; case EfiPciHostBridgeFreeResources: ReturnStatus = EFI_SUCCESS; List = HostBridgeInstance->Head.ForwardLink; while (List != &HostBridgeInstance->Head) { RootBridgeInstance = DRIVER_INSTANCE_FROM_LIST_ENTRY (List); for (Index = TypeIo; Index < TypeBus; Index++) { if (RootBridgeInstance->ResAllocNode[Index].Status == ResAllocated) { AddrLen = RootBridgeInstance->ResAllocNode[Index].Length; BaseAddress = RootBridgeInstance->ResAllocNode[Index].Base; switch (Index) { case TypeIo: Status = gDS->FreeIoSpace (BaseAddress, AddrLen); if (EFI_ERROR (Status)) { ReturnStatus = Status; } break; case TypeMem32: Status = gDS->FreeMemorySpace (BaseAddress, AddrLen); if (EFI_ERROR (Status)) { ReturnStatus = Status; } break; case TypePMem32: break; case TypeMem64: break; case TypePMem64: break; default: ASSERT (FALSE); break; }; //end switch RootBridgeInstance->ResAllocNode[Index].Type = Index; RootBridgeInstance->ResAllocNode[Index].Base = 0; RootBridgeInstance->ResAllocNode[Index].Length = 0; RootBridgeInstance->ResAllocNode[Index].Status = ResNone; } } List = List->ForwardLink; } HostBridgeInstance->ResourceSubmited = FALSE; HostBridgeInstance->CanRestarted = TRUE; return ReturnStatus; case EfiPciHostBridgeEndResourceAllocation: HostBridgeInstance->CanRestarted = FALSE; break; default: return EFI_INVALID_PARAMETER; } return EFI_SUCCESS; }