Checks for two unused processor models found…

Well hello. I almost forgot to share something with you, and that is that I found processor checks in macOS Sierra DP4 for two unused Intel processors. And it’s still there in DP5.

Ok. One check is definitely for a Broadwell based processor – follows the same path – and the other either for a Skylake based processor, or perhaps a Kaby Lake processor. Thing is. The latter shares a lot with Skylake (based) processors, and there isn’t a whole lot of code that we can check, so this one is still a bit of a mystery to me.

Now you ask; When and where did you find it Pike?

You (should) know that I added XCPM support for unsupported Processors for the new Broadwell E processors, and people reported succes with Haswell E and other (slightly older) processor models as well. That was when my eye first caught the checks. I first mentioned it in the comments section (see my reply to racermaster). Now take a look at the next image:

What Apple does is to set a bit in the CPU-bits field for every processor that supports the MSR. The selection is made based on the processor model number in _xcpm_bootstrap, and here is what Apple currently uses for XCPM supported processor models:

Haswell (0x3c) = 0x04
Crystalwell (0x46) = 0x08
Haswell-ULT (0x45) = 0x10
Broadwell-H (0x47) = 0x40
Broadwell (0x3d) = 0x80
Skylake (0x4e) = 0x200
Kabylake (0x8e) = 0x200
Skylake-DT (0x5e) = 0x1000
Kabylake-DT (0x9e) = 0x2000
Xeon W (0x55) = 0x4000

Edit: New processors added.

Currently there is no model check for 0x100 and 0x2000 in _xcpm_bootstrap, but they do get checked in _xcpm_init_complete and _xcpm_monitor_init. The bits corresponding to 0x100 and 0x2000 are also set in most of the programmed MSRs (data from DP4).

Edit: The first thing that we need to do is to obtain the address of the data. This is simple. We can do that with help of:

nm /S*/L*/Kernels/kernel | grep _xcpm_SMT_scope_msrs

This will give you something like: ffffff8000a2d030 D _xcpm_SMT_scope_msrs

This is a virtual memory address, but we need the file offset, and thus we convert this value by taking the last part of it. In this case it’s the a2d030 that we want. Copy and paste it into the calculator app (in programmers view/hex mode) and subtract 0x200000 from it to get: 0x82D030:

xxd -s 0x82d010 -l 528 -u /System/Library/Kernels/kernel DP8
xxd -s 0x82d030 -l 528 -u /System/Library/Kernels/kernel PB-GM1
xxd -s 0x82d030 -l 528 -u /System/Library/Kernels/kernel PB-GM2

//
// _xcpm_SMT_scope_msrs
//
0082D010: 2E 00 00 00 | DC 33 00 00 | 00 00 00 00 | 00 00 00 00
0082D020: 00 04 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00
0082D030: 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00

0082D040: A0 01 00 00 | DC 33 00 00 | 00 00 00 00 | 00 00 00 00
0082D050: 00 00 00 00 | 40 00 00 00 | 01 00 05 00 | 00 00 00 00
0082D060: 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00

0082D070: B0 01 00 00 | 90 01 00 00 | 00 00 00 00 | 00 00 00 00
0082D080: 0F 00 00 00 | 00 00 00 00 | 0F 00 00 00 | 00 00 00 00
0082D090: 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00

0082D0A0: B0 01 00 00 | 4C 22 00 00 | 00 00 00 00 | 00 00 00 00
0082D0B0: 0F 00 00 00 | 00 00 00 00 | 05 00 00 00 | 00 00 00 00
0082D0C0: 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00

0082D0D0: B0 01 00 00 | 00 10 00 00 | 00 00 00 00 | 00 00 00 00
0082D0E0: 0F 00 00 00 | 00 00 00 00 | 01 00 00 00 | 00 00 00 00
0082D0F0: 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00

0082D100: 09 06 00 00 | 90 33 00 00 | 00 00 00 00 | 00 00 00 00
0082D110: FF 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00
0082D120: 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00

0082D130: 8D 03 00 00 | DC 33 00 00 | 00 00 00 00 | 00 00 00 00
0082D140: FF FF FF FF | FF FF FF FF | 33 03 00 00 | 00 00 00 00
0082D150: 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00

0082D160: 8F 03 00 00 | DC 33 00 00 | 00 00 00 00 | 00 00 00 00
0082D170: FF FF FF FF | FF FF FF FF | 0F 00 00 00 | 07 00 00 00
0082D180: 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00

0082D190: 87 01 00 00 | DC 33 00 00 | 00 00 00 00 | 00 00 00 00
0082D1A0: FF FF FF FF | FF FF FF FF | 24 27 43 00 | 00 00 00 00
0082D1B0: 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00

0082D1C0: 88 01 00 00 | DC 33 00 00 | 00 00 00 00 | 00 00 00 00
0082D1D0: FF FF FF FF | FF FF FF FF | D0 81 43 00 | 00 00 00 00
0082D1E0: 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00

0082D1F0: 89 01 00 00 | DC 33 00 00 | 00 00 00 00 | 00 00 00 00
0082D200: FF FF FF FF | FF FF FF FF | D0 82 43 00 | 00 00 00 00
0082D210: 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00

Edit: Use the same trick as what we did for _xcpm_SMT_scope_msrs to get the file offset.

xxd -s 0x82d220 -l 96 -u /System/Library/Kernels/kernel DP8
xxd -s 0x82d240 -l 96 -u /System/Library/Kernels/kernel PB-GM1
xxd -s 0x82d240 -l 96 -u /System/Library/Kernels/kernel PB-GM2

//
// _xcpm_core_scope_msrs
//
0082D220: E2 00 00 00 | 4C 00 00 00 | 00 00 00 00 | 00 00 00 00
0082D230: 0F 04 00 00 | 00 00 00 00 | 05 00 00 1E | 00 00 00 00
0082D240: 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00

0082D250: E2 00 00 00 | 90 33 00 00 | 00 00 00 00 | 00 00 00 00
0082D260: 0F 04 00 00 | 00 00 00 00 | 08 00 00 7E | 00 00 00 00
0082D270: 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00

Edit: Use the same trick as what we did for _xcpm_SMT_scope_msrs to get the file offset.

xxd -s 0x82d280 -l 336 -u /System/Library/Kernels/kernel DP8
xxd -s 0x82d2a0 -l 336 -u /System/Library/Kernels/kernel PB-GM1
xxd -s 0x82d2a0 -l 336 -u /System/Library/Kernels/kernel PB-GM2

//
// _xcpm_pkg_scope_msrs
//
0082D280: A0 01 00 00 | DC 33 00 00 | 00 00 00 00 | 00 00 00 00
0082D290: 00 00 00 00 | 40 00 00 00 | 01 00 05 00 | 00 00 00 00
0082D2A0: 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00

0082D2B0: FC 01 00 00 | DC 33 00 00 | 00 00 00 00 | 00 00 00 00
0082D2C0: 00 00 10 00 | 00 00 00 00 | 1A 00 04 00 | 00 00 00 00
0082D2D0: 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00

0082D2E0: AA 01 00 00 | DC 33 00 00 | 00 00 00 00 | 00 00 00 00
0082D2F0: 00 00 00 00 | 00 00 00 00 | 01 00 00 00 | 00 00 00 00
0082D300: 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00

0082D310: 20 06 00 00 | DC 33 00 00 | 00 00 00 00 | 00 00 00 00
0082D320: 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00
0082D330: 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00

0082D340: 4C 06 00 00 | DC 33 00 00 | 00 00 00 00 | 00 00 00 00
0082D350: FF 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00
0082D360: 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00

0082D370: 3A 06 00 00 | DC 33 00 00 | 00 00 00 00 | 00 00 00 00
0082D380: 1F 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00
0082D390: 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00

0082D3A0: 42 06 00 00 | DC 33 00 00 | 00 00 00 00 | 00 00 00 00
0082D3B0: 1F 00 00 00 | 00 00 00 00 | 18 00 00 00 | 00 00 00 00
0082D3C0: 00 00 00 00 | 00 00 00 00 | 00 00 00 00 | 00 00 00 00

As you can see here, all but four MSRs include the bits for unused processor models. The big question now is; why? To get ready for new hardware perhaps?

Anyway. It looks like it that 0x100 is reserved for another Broadwell based processor – since that follows the same route as the Broadwell (0x80) processor – and the other value (0x2000) follows the same route as the Skylake (0x200) processor – though knowing Apple… anything is possible. Draw your own conclusions 😉

Edit: Clearing a bit for a matching processor model in the CPU-bits field will stop it from being programmed. As in. It won’t write to MSR 0x00 (zero) when you nullify the 32-bit MSR and 32-bit CPU-bits fields. I can’t remember who it was that thought that it worked like that. Fact is. It never did and I told you so back in 2013 (see Update-4).

Here is some pseudo C code from the disassembled routine that programs the MSRs:

void _xcpm_program_msrs(uint8_t * aTableAddress, uint8_t aNumberOfMSRs, uint32_t aTargetMSR)
{
	uint8_t * msr_table		= aTableAddress;

	uint8_t number_of_msrs		= aNumberOfMSRs;

	uint32_t target_msr		= aTargetMSR;

	uint64_t msr_value		= 0;
	uint64_t msr_and_value		= 0;
	uint64_t initial_msr_value	= 0;

	if (number_of_msrs > 0)
	{
		msr_table += 0x28;
		//
		// Main do while loop.
		//
		do
		{
			uint32_t xcpm_cpu_model = *(int32_t *)_xcpm_cpu_model;
			//
			// Checks for matching processor model and target MSR.
			//
			if ( ((*(uint32_t *)(msr_table - 0x24) & xcpm_cpu_model) != 0x0) && 
			     (((target_msr == 0x0) || (*(int32_t *)(msr_table - 0x28) == target_msr))) )
			{
				//
				// Get MSR number field from table.
				//
				int msr = *(uint32_t *)(msr_table - 0x28);

				if (*(int32_t *)0xffffff8000a2ce00 != 0x0)
				{
					printf("xcpm_program_msrs: programming MSR 0x%x\n", msr);
				}
				//
				// Read initial value of MSR.
				//
				initial_msr_value = rdmsr64(msr);
				//
				// Keep value in the "initial value" field.
				//
				*(msr_table - 0x08) = initial_msr_value;
				//
				// Get "AND value" field from table.
				//
				msr_and_value = *(msr_table - 0x18);
				//
				// Update value of MSR (NOT, AND and OR).
				//
				new_msr_value = !initial_msr_value & msr_and_value | *(msr_table - 0x10);
				//
				// Write back modified value of MSR.
				//
				wrmsr64(msr, new_msr_value);
				//
				// Read value of MSR and store it in the "result" field.
				//
				*msr_table = rdmsr64(msr);
			}

			msr_table += 0x30; // Move on to the next MSR.
			number_of_msrs-—; // One MSR less to program.

		} while (number_of_msrs > 0);
	}

	return;
}

This routine is being called from _xcpm_init in the kernel (DP8):

ffffff8000428489	leaq	_xcpm_pkg_scope_msrs(%rip), %rdi	// arg0 = address of data block
ffffff8000428490	movl	$0x7, %esi 							// arg1 = 7 (number of MSRs)
ffffff8000428495	xorl	%edx, %edx							// arg2 = 0 (no MSR specified/program all MSRs)
ffffff8000428497	callq	0xffffff8000428130 					// _xcpm_program_msrs subroutine

../..

ffffff80004284b1	leaq	_xcpm_core_scope_msrs(%rip), %rdi	// arg0 = address of data block
ffffff80004284b8	movl	$0x2, %esi 							// arg1 = 2 (number of MSRs)
ffffff80004284bd	xorl	%edx, %edx 							// arg2 = 0 (no MSR specified/program all MSRs)
ffffff80004284bf	callq	0xffffff8000428130 					// _xcpm_program_msrs subroutine

ffffff80004284c4	leaq	_xcpm_SMT_scope_msrs(%rip), %rdi	// arg0 = address of data block
ffffff80004284cb	movl	$0xb, %esi 							// arg1 = 11 (number of MSRs)
ffffff80004284d0	xorl	%edx, %edx 							// arg2 = 0 (no MSR specified/program all MSRs)
ffffff80004284d2	callq	0xffffff8000428130 					// _xcpm_program_msrs subroutine

Note the callq 0xffffff8000428130 because that is the one you are looking for, but be aware of the fact that the addresses are different for each Developer Preview/Public Beta of macOS Sierra.

Edit: In the PB-GM1 and PB-GM2 the address is: 0xffffff80004283a0
Note: PB = Public Beta.

5 thoughts on “Checks for two unused processor models found…

  1. Have you noticed any signs of how many cores they are going support? I was thinking of pushing my luck with dual 22 cores on an Asus z10, do you have any thoughts on plausibility ?

  2. Pingback: Unused Bits in Processor Model Check… – Pike's Universum

Leave a comment