VirtualBox

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

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

IPRT/mp/darwin: Reimplemented RTMpGetOnlineSet to avoid getting the same information over and over again for each CPU. Updated comment in RTMpGetCurFrequency explaining how seemingly difficult it would be to implement. Updated todo in RTMpGetDescription. bugref:9898

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 12.9 KB
Line 
1/* $Id: mp-darwin.cpp 94087 2022-03-04 14:03: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.215389.xyz. 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 cCpus = -1;
326 cb = sizeof(cCpus);
327 rc = sysctl(aiMib, RT_ELEMENTS(aiMib), &cCpus, &cb, NULL, 0);
328 if (rc != -1 && cCpus >= 1)
329 return cCpus;
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 /* Assume names on the form "cpu<N>" are only for CPUs. */
354 char szCpuName[32];
355 RTStrPrintf(szCpuName, sizeof(szCpuName), "cpu%u", idCpu);
356 CFMutableDictionaryRef hMatchingDict = IOServiceNameMatching(szCpuName);
357 AssertReturn(hMatchingDict, 0);
358
359 /* Just get the first one. */
360 io_object_t hCpu = IOServiceGetMatchingService(kIOMasterPortDefault, hMatchingDict);
361 if (hCpu != 0)
362 {
363 uint32_t uCpuFrequency = rtMpDarwinGetMaxFrequencyFromIOService(hCpu);
364 IOObjectRelease(hCpu);
365 if (uCpuFrequency)
366 return uCpuFrequency;
367 }
368
369#if 1 /* Just in case... */
370 /* Create a matching dictionary for searching for CPU services in the IOKit. */
371# if defined(RT_ARCH_ARM64) || defined(RT_ARCH_ARM32)
372 hMatchingDict = IOServiceMatching("AppleARMCPU");
373# elif defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
374 hMatchingDict = IOServiceMatching("AppleACPICPU");
375# else
376# error "Port me!"
377# endif
378 AssertReturn(hMatchingDict, 0);
379
380 /* Perform the search and get a collection of Apple CPU services. */
381 io_iterator_t hCpuServices = IO_OBJECT_NULL;
382 IOReturn irc = IOServiceGetMatchingServices(kIOMasterPortDefault, hMatchingDict, &hCpuServices);
383 AssertMsgReturn(irc == kIOReturnSuccess, ("irc=%d\n", irc), 0);
384 hMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
385
386 /* Enumerate the matching services. */
387 uint32_t uCpuFrequency = 0;
388 io_object_t hCurCpu;
389 while (uCpuFrequency == 0 && (hCurCpu = IOIteratorNext(hCpuServices)) != IO_OBJECT_NULL)
390 {
391 io_object_t hParent = (io_object_t)0;
392 irc = IORegistryEntryGetParentEntry(hCurCpu, kIOServicePlane, &hParent);
393 if (irc == kIOReturnSuccess && hParent)
394 {
395 uCpuFrequency = rtMpDarwinGetMaxFrequencyFromIOService(hParent);
396 IOObjectRelease(hParent);
397 }
398 IOObjectRelease(hCurCpu);
399 }
400 IOObjectRelease(hCpuServices);
401#endif
402
403 AssertFailed();
404 return 0;
405}
406
Note: See TracBrowser for help on using the repository browser.

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