mirror of
				https://gitlab.com/qemu-project/edk2.git
				synced 2025-10-30 07:56:39 +08:00 
			
		
		
		
	UefiCpuPkg: MpInitLib: Fix Task Register Race Condition GP Fault
TR is used to enable a separate safe stack when a stack overflow occurs. When PEI starts up the APs, TR is non-zero and so each processor has its own GDT. TR is an offset into the GDT and so points to a different TSS entry in each AP. There is a small window in early DXE after MpInitLibInitialize() is called where: - TR is non-zero because it has been inherited from the PEI phase - TR is not restored to 0 - The APs are all switched to using the BSP's GDT - SaveVolatileRegisters() is called from ApWakeupFunction() before the APs go to sleep, which saves the non-zero TR value to CpuMpData->CpuData[].VolatileRegisters.Tr, cause TR to point to the same TSS entry in the BSP's GDT - The next time the APs are woken up, RestoreVolatileRegisters() is called from ApWakeupFunction() which would attempt to load the non-zero TR value into the actual task register, which creates a race condition to a #GP fault because loading the task register sets the busy bit in the TSS descriptor and a #GP fault occurs if the busy bit is already set when loading the task register. To avoid this issue, the task register is only loaded if TR is non-zero and the TSS descriptor is valid and not busy. HW sets the busy bit and does not clear it. edk2 does not clear the busy bit, so the BSP's TSS descriptor will be marked busy forever and the APs will not load the task register until they have their own GDT/TSS set up. Co-authored-by: Ray Ni <ray.ni@intel.com> Signed-off-by: Oliver Smith-Denny <osde@microsoft.com>
This commit is contained in:
		 Oliver Smith-Denny
					Oliver Smith-Denny
				
			
				
					committed by
					
						![mergify[bot]](/assets/img/avatar_default.png) mergify[bot]
						mergify[bot]
					
				
			
			
				
	
			
			
			![mergify[bot]](/assets/img/avatar_default.png) mergify[bot]
						mergify[bot]
					
				
			
						parent
						
							e67f405713
						
					
				
				
					commit
					34cd1aca46
				
			| @ -278,8 +278,30 @@ RestoreVolatileRegisters ( | ||||
|   { | ||||
|     Tss = (IA32_TSS_DESCRIPTOR *)(VolatileRegisters->Gdtr.Base + | ||||
|                                   VolatileRegisters->Tr); | ||||
|     if (Tss->Bits.P == 1) { | ||||
|       Tss->Bits.Type &= 0xD;  // 1101 - Clear busy bit just in case | ||||
|  | ||||
|     if ((Tss->Bits.P == 1) && ((Tss->Bits.Type & BIT1) == 0)) { | ||||
|       // TR is used to enable a separate safe stack when a stack overflow occurs. | ||||
|       // When PEI starts up the APs, TR is non-zero and so each processor has its | ||||
|       // own GDT. TR is an offset into the GDT and so points to a different TSS entry | ||||
|       // in each AP. | ||||
|       // There is a small window in early DXE after MpInitLibInitialize() is called | ||||
|       // where: | ||||
|       // - TR is non-zero because it has been inherited from the PEI phase | ||||
|       // - TR is not restored to 0 | ||||
|       // - The APs are all switched to using the BSP's GDT | ||||
|       // - SaveVolatileRegisters() is called from ApWakeupFunction() before the APs | ||||
|       //   go to sleep, which saves the non-zero TR value to CpuMpData->CpuData[].VolatileRegisters.Tr, | ||||
|       //   cause TR to point to the same TSS entry in the BSP's GDT | ||||
|       // - The next time the APs are woken up, RestoreVolatileRegisters() is called | ||||
|       //   from ApWakeupFunction() which would attempt to load the non-zero TR value into the actual | ||||
|       //   task register, which creates a race condition to a #GP fault because loading the task | ||||
|       //   register sets the busy bit in the TSS descriptor and a #GP fault occurs if the busy bit | ||||
|       //   is already set when loading the task register. | ||||
|       // | ||||
|       // To avoid this issue, the task register is only loaded if TR is non-zero and the | ||||
|       // TSS descriptor is valid and not busy. HW sets the busy bit and does not clear it. edk2 does | ||||
|       // not clear the busy bit, so the BSP's TSS descriptor will be marked busy forever and the APs | ||||
|       // will not load the task register until they have their own GDT/TSS set up. | ||||
|       AsmWriteTr (VolatileRegisters->Tr); | ||||
|     } | ||||
|   } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user