VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/darwin/mp-darwin.cpp@ 94157

Last change on this file since 94157 was 94094, checked in by vboxsync, 3 years ago

IPRT/darwin: Renamed variable in RTMpGetMaxFrequency. bugref:9898

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 13.8 KB
Line 
1/* $Id: mp-darwin.cpp 94094 2022-03-04 22:52:32Z vboxsync $ */
2/** @file
3 * IPRT - Multiprocessor, Darwin.
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_DEFAULT /*RTLOGGROUP_SYSTEM*/
32#include <iprt/types.h>
33
34#include <unistd.h>
35#include <stdio.h>
36#include <sys/sysctl.h>
37#include <sys/stat.h>
38#include <sys/fcntl.h>
39#include <errno.h>
40#include <mach/mach.h>
41
42#include <CoreFoundation/CFBase.h>
43#include <IOKit/IOKitLib.h>
44/*#include <IOKit/IOBSD.h>
45#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
46#include <IOKit/storage/IOBlockStorageDevice.h>
47#include <IOKit/storage/IOMedia.h>
48#include <IOKit/storage/IOCDMedia.h>
49#include <IOKit/scsi/SCSITaskLib.h>
50#include <SystemConfiguration/SystemConfiguration.h>
51#include <mach/mach_error.h>
52#include <sys/param.h>
53#include <paths.h>*/
54
55#include <iprt/mp.h>
56#include <iprt/assert.h>
57#include <iprt/cpuset.h>
58#include <iprt/log.h>
59#include <iprt/string.h>
60
61
62/**
63 * Internal worker that determines the max possible logical CPU count (hyperthreads).
64 *
65 * @returns Max cpus.
66 */
67static RTCPUID rtMpDarwinMaxLogicalCpus(void)
68{
69 int cCpus = -1;
70 size_t cb = sizeof(cCpus);
71 int rc = sysctlbyname("hw.logicalcpu_max", &cCpus, &cb, NULL, 0);
72 if (rc != -1 && cCpus >= 1)
73 return cCpus;
74 AssertFailed();
75 return 1;
76}
77
78/**
79 * Internal worker that determines the max possible physical core count.
80 *
81 * @returns Max cpus.
82 */
83static RTCPUID rtMpDarwinMaxPhysicalCpus(void)
84{
85 int cCpus = -1;
86 size_t cb = sizeof(cCpus);
87 int rc = sysctlbyname("hw.physicalcpu_max", &cCpus, &cb, NULL, 0);
88 if (rc != -1 && cCpus >= 1)
89 return cCpus;
90 AssertFailed();
91 return 1;
92}
93
94
95#if 0 /* unused */
96/**
97 * Internal worker that determines the current number of logical CPUs (hyperthreads).
98 *
99 * @returns Max cpus.
100 */
101static RTCPUID rtMpDarwinOnlineLogicalCpus(void)
102{
103 int cCpus = -1;
104 size_t cb = sizeof(cCpus);
105 int rc = sysctlbyname("hw.logicalcpu", &cCpus, &cb, NULL, 0);
106 if (rc != -1 && cCpus >= 1)
107 return cCpus;
108 AssertFailed();
109 return 1;
110}
111#endif /* unused */
112
113
114/**
115 * Internal worker that determines the current number of physical CPUs.
116 *
117 * @returns Max cpus.
118 */
119static RTCPUID rtMpDarwinOnlinePhysicalCpus(void)
120{
121 int cCpus = -1;
122 size_t cb = sizeof(cCpus);
123 int rc = sysctlbyname("hw.physicalcpu", &cCpus, &cb, NULL, 0);
124 if (rc != -1 && cCpus >= 1)
125 return cCpus;
126 AssertFailed();
127 return 1;
128}
129
130
131#if defined(RT_ARCH_ARM64)
132RTDECL(RTCPUID) RTMpCpuId(void)
133{
134 /* xnu-7195.50.7.100.1/osfmk/arm64/start.s and machine_routines.c sets TPIDRRO_EL0
135 to the cpu_data_t::cpu_id value. */
136 uint64_t u64Ret;
137 __asm__ __volatile__("mrs %0,TPIDRRO_EL0\n\t" : "=r" (u64Ret));
138 return (RTCPUID)u64Ret;
139}
140#elif defined(RT_ARCH_ARM32)
141RTDECL(RTCPUID) RTMpCpuId(void)
142{
143 /* xnu-7195.50.7.100.1/osfmk/arm/start.s and machine_routines.c sets TPIDRURO
144 to the cpu_data_t::cpu_id value. */
145 uint32_t u32Ret;
146 __asm__ __volatile__("mrs p15, 0, %0, c13, c0, 3\n\t" : "=r" (u32Ret));
147 return (RTCPUID)u32Ret;
148}
149#endif
150
151
152RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu)
153{
154 return idCpu < RTCPUSET_MAX_CPUS && idCpu < rtMpDarwinMaxLogicalCpus() ? idCpu : -1;
155}
156
157
158RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu)
159{
160 return (unsigned)iCpu < rtMpDarwinMaxLogicalCpus() ? iCpu : NIL_RTCPUID;
161}
162
163
164RTDECL(RTCPUID) RTMpGetMaxCpuId(void)
165{
166 return rtMpDarwinMaxLogicalCpus() - 1;
167}
168
169
170RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu)
171{
172#if 0
173 return RTMpIsCpuPossible(idCpu);
174#else
175 /** @todo proper ring-3 support on darwin, see @bugref{3014}. */
176 natural_t cCpus;
177 processor_basic_info_t paInfo;
178 mach_msg_type_number_t cInfo;
179 kern_return_t krc = host_processor_info(mach_host_self(), PROCESSOR_BASIC_INFO,
180 &cCpus, (processor_info_array_t*)&paInfo, &cInfo);
181 AssertReturn(krc == KERN_SUCCESS, true);
182 bool const fIsOnline = idCpu < cCpus ? paInfo[idCpu].running : false;
183 vm_deallocate(mach_task_self(), (vm_address_t)paInfo, cInfo * sizeof(paInfo[0]));
184 return fIsOnline;
185#endif
186}
187
188
189RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu)
190{
191 return idCpu != NIL_RTCPUID
192 && idCpu < rtMpDarwinMaxLogicalCpus();
193}
194
195
196RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet)
197{
198#if 0
199 RTCPUID cCpus = rtMpDarwinMaxLogicalCpus();
200 return RTCpuSetFromU64(RT_BIT_64(cCpus) - 1);
201
202#else
203 RTCpuSetEmpty(pSet);
204 RTCPUID cMax = rtMpDarwinMaxLogicalCpus();
205 for (RTCPUID idCpu = 0; idCpu < cMax; idCpu++)
206 if (RTMpIsCpuPossible(idCpu))
207 RTCpuSetAdd(pSet, idCpu);
208 return pSet;
209#endif
210}
211
212
213RTDECL(RTCPUID) RTMpGetCount(void)
214{
215 return rtMpDarwinMaxLogicalCpus();
216}
217
218
219RTDECL(RTCPUID) RTMpGetCoreCount(void)
220{
221 return rtMpDarwinMaxPhysicalCpus();
222}
223
224
225RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet)
226{
227 RTCpuSetEmpty(pSet);
228#if 0
229 RTCPUID cMax = rtMpDarwinMaxLogicalCpus();
230 for (RTCPUID idCpu = 0; idCpu < cMax; idCpu++)
231 if (RTMpIsCpuOnline(idCpu))
232 RTCpuSetAdd(pSet, idCpu);
233#else
234 natural_t cCpus = 0;
235 processor_basic_info_t paInfo = NULL;
236 mach_msg_type_number_t cInfo = 0;
237 kern_return_t krc = host_processor_info(mach_host_self(), PROCESSOR_BASIC_INFO,
238 &cCpus, (processor_info_array_t *)&paInfo, &cInfo);
239 AssertReturn(krc == KERN_SUCCESS, pSet);
240
241 AssertStmt(cCpus <= RTCPUSET_MAX_CPUS, cCpus = RTCPUSET_MAX_CPUS);
242 for (natural_t idCpu = 0; idCpu < cCpus; idCpu++)
243 if (paInfo[idCpu].running)
244 RTCpuSetAdd(pSet, idCpu);
245
246 vm_deallocate(mach_task_self(), (vm_address_t)paInfo, cInfo * sizeof(paInfo[0]));
247#endif
248 return pSet;
249}
250
251
252RTDECL(RTCPUID) RTMpGetOnlineCount(void)
253{
254 RTCPUSET Set;
255 RTMpGetOnlineSet(&Set);
256 return RTCpuSetCount(&Set);
257}
258
259
260RTDECL(RTCPUID) RTMpGetOnlineCoreCount(void)
261{
262 return rtMpDarwinOnlinePhysicalCpus();
263}
264
265
266RTDECL(uint32_t) RTMpGetCurFrequency(RTCPUID idCpu)
267{
268 /** @todo figure out how to get the current cpu speed on darwin. Have to
269 * check what powermanagement does. The powermetrics uses a private
270 * IOReportXxxx interface and *seems* (guessing) to calculate the frequency
271 * based on the frequency distribution over the last report period... This
272 * means that it's not really an suitable API for here. */
273 NOREF(idCpu);
274 return 0;
275}
276
277
278/**
279 * Worker for RTMpGetMaxFrequency.
280 * @returns Non-zero frequency in MHz on success, 0 on failure.
281 */
282static uint32_t rtMpDarwinGetMaxFrequencyFromIOService(io_service_t hCpu)
283{
284 io_struct_inband_t Buf = {0};
285 uint32_t cbActual = sizeof(Buf);
286 kern_return_t krc = IORegistryEntryGetProperty(hCpu, "clock-frequency", Buf, &cbActual);
287 Log2(("rtMpDarwinGetMaxFrequencyFromIOService: krc=%d; cbActual=%#x %.16Rhxs\n", krc, cbActual, Buf));
288 if (krc == kIOReturnSuccess)
289 {
290 switch (cbActual)
291 {
292 case sizeof(uint32_t):
293 return RT_BE2H_U32(*(uint32_t *)Buf) / 1000;
294 case sizeof(uint64_t):
295 AssertFailed();
296 return RT_BE2H_U64(*(uint64_t *)Buf) / 1000;
297 default:
298 AssertFailed();
299 }
300 }
301 return 0;
302}
303
304
305RTDECL(uint32_t) RTMpGetMaxFrequency(RTCPUID idCpu)
306{
307 if (!RTMpIsCpuOnline(idCpu))
308 return 0;
309
310 /*
311 * Try the 'hw.cpufrequency_max' one.
312 */
313 uint64_t CpuFrequencyMax = 0;
314 size_t cb = sizeof(CpuFrequencyMax);
315 int rc = sysctlbyname("hw.cpufrequency_max", &CpuFrequencyMax, &cb, NULL, 0);
316 if (!rc)
317 return (CpuFrequencyMax + 999999) / 1000000;
318
319 /*
320 * Use the deprecated one.
321 */
322 int aiMib[2];
323 aiMib[0] = CTL_HW;
324 aiMib[1] = HW_CPU_FREQ;
325 int iDeprecatedFrequency = -1;
326 cb = sizeof(iDeprecatedFrequency);
327 rc = sysctl(aiMib, RT_ELEMENTS(aiMib), &iDeprecatedFrequency, &cb, NULL, 0);
328 if (rc != -1 && iDeprecatedFrequency >= 1)
329 return iDeprecatedFrequency;
330
331 /*
332 * The above does not work for Apple M1 / xnu 20.1.0, so go look at the I/O registry instead.
333 *
334 * A sample ARM layout:
335 * | +-o cpu1@1 <class IOPlatformDevice, id 0x100000110, registered, matched, active, busy 0 (182 ms), retain 8>
336 * | | +-o AppleARMCPU <class AppleARMCPU, id 0x10000021b, registered, matched, active, busy 0 (1 ms), retain 6>
337 * | +-o cpu2@2 <class IOPlatformDevice, id 0x100000111, registered, matched, active, busy 0 (175 ms), retain 8>
338 * | | +-o AppleARMCPU <class AppleARMCPU, id 0x10000021c, registered, matched, active, busy 0 (3 ms), retain 6>
339 * | +-o cpu3@3 <class IOPlatformDevice, id 0x100000112, registered, matched, active, busy 0 (171 ms), retain 8>
340 * | | +-o AppleARMCPU <class AppleARMCPU, id 0x10000021d, registered, matched, active, busy 0 (1 ms), retain 6>
341 * | +-o cpu4@100 <class IOPlatformDevice, id 0x100000113, registered, matched, active, busy 0 (171 ms), retain 8>
342 * | | +-o AppleARMCPU <class AppleARMCPU, id 0x10000021e, registered, matched, active, busy 0 (1 ms), retain 6>
343 * | +-o cpu5@101 <class IOPlatformDevice, id 0x100000114, registered, matched, active, busy 0 (179 ms), retain 8>
344 * | | +-o AppleARMCPU <class AppleARMCPU, id 0x10000021f, registered, matched, active, busy 0 (9 ms), retain 6>
345 * | +-o cpu6@102 <class IOPlatformDevice, id 0x100000115, registered, matched, active, busy 0 (172 ms), retain 8>
346 * | | +-o AppleARMCPU <class AppleARMCPU, id 0x100000220, registered, matched, active, busy 0 (1 ms), retain 6>
347 * | +-o cpu7@103 <class IOPlatformDevice, id 0x100000116, registered, matched, active, busy 0 (175 ms), retain 8>
348 * | | +-o AppleARMCPU <class AppleARMCPU, id 0x100000221, registered, matched, active, busy 0 (5 ms), retain 6>
349 * | +-o cpus <class IOPlatformDevice, id 0x10000010e, registered, matched, active, busy 0 (12 ms), retain 15>
350 *
351 */
352
353#if 1 /* simpler way to get at it inspired by powermetrics, this is also used
354 in the arm version of RTMpGetDescription. */
355 /* Assume names on the form "cpu<N>" are only for CPUs. */
356 char szCpuPath[64];
357# if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
358 RTStrPrintf(szCpuPath, sizeof(szCpuPath), "IODeviceTree:/cpus/CPU%X", idCpu);
359# else
360 RTStrPrintf(szCpuPath, sizeof(szCpuPath), "IODeviceTree:/cpus/cpu%x", idCpu); /** @todo Hex? M1 Max only has 10 cores... */
361# endif
362 io_registry_entry_t hIoRegEntry = IORegistryEntryFromPath(kIOMasterPortDefault, szCpuPath);
363 if (hIoRegEntry != MACH_PORT_NULL)
364 {
365 uint32_t uCpuFrequency = rtMpDarwinGetMaxFrequencyFromIOService(hIoRegEntry);
366 IOObjectRelease(hIoRegEntry);
367 if (uCpuFrequency)
368 return uCpuFrequency;
369 }
370
371#else
372 /* Assume names on the form "cpu<N>" are only for CPUs. */
373 char szCpuName[32];
374 RTStrPrintf(szCpuName, sizeof(szCpuName), "cpu%u", idCpu);
375 CFMutableDictionaryRef hMatchingDict = IOServiceNameMatching(szCpuName);
376 AssertReturn(hMatchingDict, 0);
377
378 /* Just get the first one. */
379 io_object_t hCpu = IOServiceGetMatchingService(kIOMasterPortDefault, hMatchingDict);
380 if (hCpu != 0)
381 {
382 uint32_t uCpuFrequency = rtMpDarwinGetMaxFrequencyFromIOService(hCpu);
383 IOObjectRelease(hCpu);
384 if (uCpuFrequency)
385 return uCpuFrequency;
386 }
387
388# if 1 /* Just in case... */
389 /* Create a matching dictionary for searching for CPU services in the IOKit. */
390# if defined(RT_ARCH_ARM64) || defined(RT_ARCH_ARM32)
391 hMatchingDict = IOServiceMatching("AppleARMCPU");
392# elif defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
393 hMatchingDict = IOServiceMatching("AppleACPICPU");
394# else
395# error "Port me!"
396# endif
397 AssertReturn(hMatchingDict, 0);
398
399 /* Perform the search and get a collection of Apple CPU services. */
400 io_iterator_t hCpuServices = IO_OBJECT_NULL;
401 IOReturn irc = IOServiceGetMatchingServices(kIOMasterPortDefault, hMatchingDict, &hCpuServices);
402 AssertMsgReturn(irc == kIOReturnSuccess, ("irc=%d\n", irc), 0);
403 hMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
404
405 /* Enumerate the matching services. */
406 uint32_t uCpuFrequency = 0;
407 io_object_t hCurCpu;
408 while (uCpuFrequency == 0 && (hCurCpu = IOIteratorNext(hCpuServices)) != IO_OBJECT_NULL)
409 {
410 io_object_t hParent = (io_object_t)0;
411 irc = IORegistryEntryGetParentEntry(hCurCpu, kIOServicePlane, &hParent);
412 if (irc == kIOReturnSuccess && hParent)
413 {
414 uCpuFrequency = rtMpDarwinGetMaxFrequencyFromIOService(hParent);
415 IOObjectRelease(hParent);
416 }
417 IOObjectRelease(hCurCpu);
418 }
419 IOObjectRelease(hCpuServices);
420# endif
421#endif
422 AssertFailed();
423 return 0;
424}
425
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette