tl;dr: This is correct behavior, as designed by Intel.
Details:
machdep.cpu.logical_per_package
is implemented in the kernel in bsd/dev/i386/sysctl.c
:
static int
cpu_logical_per_package SYSCTL_HANDLER_ARGS
{
__unused struct sysctl_oid *unused_oidp = oidp;
__unused void *unused_arg1 = arg1;
__unused int unused_arg2 = arg2;
i386_cpu_info_t *cpu_info = cpuid_info();
if (!(cpuid_features() & CPUID_FEATURE_HTT)) {
return ENOENT;
}
return SYSCTL_OUT(req, &cpu_info->cpuid_logical_per_package,
sizeof(cpu_info->cpuid_logical_per_package));
}
This sysctl
returns the cpuid_logical_per_package
field of the cpu_info
structure. To see how that field is populated, we examine osfmk/i386/cpuid.c
:
static void
cpuid_set_generic_info(i386_cpu_info_t *info_p)
{
...
cpuid_fn(1, reg);
...
if (info_p->cpuid_features & CPUID_FEATURE_HTT) {
info_p->cpuid_logical_per_package =
bitfield32(reg[ebx], 23, 16);
} else {
info_p->cpuid_logical_per_package = 1;
}
...
}
The first part of this function executes the Intel x86 CPUID
instruction with an operand value of 1
, which will gather certain information from the CPU and populate the E[A,B,C,D]X
registers with it. Later on, we examine bits 16-23
of the EBX
register and save them into the cpuid_logical_per_package
field.
To understand the meaning of what will be found in that bitfield of EBX
, we turn to the Intel Software Developer's Manual, Volume 2A, Chapter 3, Page 215 (CPUID—CPU Identification), and find the register definition for the 1
opcode:
The part we're interested in says:
Bits 23 - 16: Maximum number of addressable IDs for logical processors
in this physical package *.
*- The nearest power-of-2 integer that is not smaller than EBX[23:16] is the number of unique initial APIC IDs reserved for addressing
different logical processors in a physical package. This field is only
valid if CPUID.1.EDX.HTT[bit 28]= 1.
So what this is telling us is that the sysctl
selector name "cpu_logical_per_package
" is a misnomer. It tells you the valid range of ID numbers that the interrupt controller may use to identify logical CPUs and this does not necessarily correspond to how many logical CPUs you actually have.
The situation is analogous for machdep.cpu.cores_per_package
, which is generated in cpuid_set_cache_info()
in the same osfmk/i386/cpuid.c
file:
static void
cpuid_set_cache_info( i386_cpu_info_t * info_p )
{
...
reg[eax] = 4; /* cpuid request 4 */
reg[ecx] = index; /* index starting at 0 */
cpuid(reg);
...
info_p->cpuid_cores_per_package
= bitfield32(reg[eax], 31, 26) + 1;
...
}
And looking at the SDM:
So the moral is:
You should rely on machdep.cpu.core_count
and .thread_count
to determine the actual number of physical and logical cores, respectively, and regard the other two fields as being useful only to programmers who need to work with the kernel and have a specific need to know the maximum addressable ID for logical and physical processors.
If you're still wondering why a logical or physical processor is allowed to have a higher ID than the actual number of processors available on the silicon, the reason is that it's a result of the way that silicon is manufactured. Chip fab is an enormously expensive and complex process, and any reduction in complexity is going to save millions of dollars. It turns out to actually be cheaper to manufacture a chip with eight physical cores etched into the die and then burn a few one-time-programmable internal fuses to disconnect four of the physical cores from that circuit, than it is to create dedicated masks and tooling for a totally separate four-core die. So Intel makes a bunch of 8-core chips and then sells the amputated 4-core version for cheaper.
As far as most of the rest of the chip is concerned, though, it still thinks that it has 8 cores. Each subsystem on a processor has a unique ID by which the rest of the subsystems can identify it, and these IDs are physically implemented as arrays of transistors that build up a sequence of binary 1
s and 0
s to represent a decimal number. Because these IDs are made from transistors, they are immutable parts of the mask and they do not change even if the OTP fuses are blown to disable half the cores. So you may end up with an original 8-core chip having, e.g., logical core IDs ranging from 0-15
but the cheaper 4-core version only uses IDs 8-15
and the first 0-7
are not actually addressable. Hence the numbering scheme can go higher than the actual number of cores you have available to you. Hope that makes sense!