VirtualBox

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

Last change on this file since 89669 was 87280, checked in by vboxsync, 4 years ago

IPRT/mp-darwin.cpp: get the clock-frequency from the CPU IOService if RTMpGetMaxFrequency is unable to figure out things using sysctl. bugref:9898

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 12.0 KB
Line 
1/* $Id: mp-darwin.cpp 87280 2021-01-15 16:29:06Z vboxsync $ */
2/** @file
3 * IPRT - Multiprocessor, Darwin.
4 */
5
6/*
7 * Copyright (C) 2006-2020 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/cpuset.h>
57#include <iprt/assert.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 nCpus;
177 processor_basic_info_t pinfo;
178 mach_msg_type_number_t count;
179 kern_return_t krc = host_processor_info(mach_host_self(),
180 PROCESSOR_BASIC_INFO, &nCpus, (processor_info_array_t*)&pinfo, &count);
181 AssertReturn (krc == KERN_SUCCESS, true);
182 bool isOnline = idCpu < nCpus ? pinfo[idCpu].running : false;
183 vm_deallocate(mach_task_self(), (vm_address_t)pinfo, count * sizeof(*pinfo));
184 return isOnline;
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#if 0
228 return RTMpGetSet(pSet);
229#else
230 RTCpuSetEmpty(pSet);
231 RTCPUID cMax = rtMpDarwinMaxLogicalCpus();
232 for (RTCPUID idCpu = 0; idCpu < cMax; idCpu++)
233 if (RTMpIsCpuOnline(idCpu))
234 RTCpuSetAdd(pSet, idCpu);
235 return pSet;
236#endif
237}
238
239
240RTDECL(RTCPUID) RTMpGetOnlineCount(void)
241{
242 RTCPUSET Set;
243 RTMpGetOnlineSet(&Set);
244 return RTCpuSetCount(&Set);
245}
246
247
248RTDECL(RTCPUID) RTMpGetOnlineCoreCount(void)
249{
250 return rtMpDarwinOnlinePhysicalCpus();
251}
252
253
254RTDECL(uint32_t) RTMpGetCurFrequency(RTCPUID idCpu)
255{
256 /** @todo figure out how to get the current cpu speed on darwin. Have to check what powermanagement does. */
257 NOREF(idCpu);
258 return 0;
259}
260
261
262/**
263 * Worker for RTMpGetMaxFrequency.
264 * @returns Non-zero frequency in MHz on success, 0 on failure.
265 */
266static uint32_t rtMpDarwinGetMaxFrequencyFromIOService(io_service_t hCpu)
267{
268 io_struct_inband_t Buf = {0};
269 uint32_t cbActual = sizeof(Buf);
270 kern_return_t krc = IORegistryEntryGetProperty(hCpu, "clock-frequency", Buf, &cbActual);
271 Log2(("rtMpDarwinGetMaxFrequencyFromIOService: krc=%d; cbActual=%#x %.16Rhxs\n", krc, cbActual, Buf));
272 if (krc == kIOReturnSuccess)
273 {
274 switch (cbActual)
275 {
276 case sizeof(uint32_t):
277 return RT_BE2H_U32(*(uint32_t *)Buf) / 1000;
278 case sizeof(uint64_t):
279 AssertFailed();
280 return RT_BE2H_U64(*(uint64_t *)Buf) / 1000;
281 default:
282 AssertFailed();
283 }
284 }
285 return 0;
286}
287
288
289RTDECL(uint32_t) RTMpGetMaxFrequency(RTCPUID idCpu)
290{
291 if (!RTMpIsCpuOnline(idCpu))
292 return 0;
293
294 /*
295 * Try the 'hw.cpufrequency_max' one.
296 */
297 uint64_t CpuFrequencyMax = 0;
298 size_t cb = sizeof(CpuFrequencyMax);
299 int rc = sysctlbyname("hw.cpufrequency_max", &CpuFrequencyMax, &cb, NULL, 0);
300 if (!rc)
301 return (CpuFrequencyMax + 999999) / 1000000;
302
303 /*
304 * Use the deprecated one.
305 */
306 int aiMib[2];
307 aiMib[0] = CTL_HW;
308 aiMib[1] = HW_CPU_FREQ;
309 int cCpus = -1;
310 cb = sizeof(cCpus);
311 rc = sysctl(aiMib, RT_ELEMENTS(aiMib), &cCpus, &cb, NULL, 0);
312 if (rc != -1 && cCpus >= 1)
313 return cCpus;
314
315 /*
316 * The above does not work for Apple M1 / xnu 20.1.0, so go look at the I/O registry instead.
317 *
318 * A sample ARM layout:
319 * | +-o cpu1@1 <class IOPlatformDevice, id 0x100000110, registered, matched, active, busy 0 (182 ms), retain 8>
320 * | | +-o AppleARMCPU <class AppleARMCPU, id 0x10000021b, registered, matched, active, busy 0 (1 ms), retain 6>
321 * | +-o cpu2@2 <class IOPlatformDevice, id 0x100000111, registered, matched, active, busy 0 (175 ms), retain 8>
322 * | | +-o AppleARMCPU <class AppleARMCPU, id 0x10000021c, registered, matched, active, busy 0 (3 ms), retain 6>
323 * | +-o cpu3@3 <class IOPlatformDevice, id 0x100000112, registered, matched, active, busy 0 (171 ms), retain 8>
324 * | | +-o AppleARMCPU <class AppleARMCPU, id 0x10000021d, registered, matched, active, busy 0 (1 ms), retain 6>
325 * | +-o cpu4@100 <class IOPlatformDevice, id 0x100000113, registered, matched, active, busy 0 (171 ms), retain 8>
326 * | | +-o AppleARMCPU <class AppleARMCPU, id 0x10000021e, registered, matched, active, busy 0 (1 ms), retain 6>
327 * | +-o cpu5@101 <class IOPlatformDevice, id 0x100000114, registered, matched, active, busy 0 (179 ms), retain 8>
328 * | | +-o AppleARMCPU <class AppleARMCPU, id 0x10000021f, registered, matched, active, busy 0 (9 ms), retain 6>
329 * | +-o cpu6@102 <class IOPlatformDevice, id 0x100000115, registered, matched, active, busy 0 (172 ms), retain 8>
330 * | | +-o AppleARMCPU <class AppleARMCPU, id 0x100000220, registered, matched, active, busy 0 (1 ms), retain 6>
331 * | +-o cpu7@103 <class IOPlatformDevice, id 0x100000116, registered, matched, active, busy 0 (175 ms), retain 8>
332 * | | +-o AppleARMCPU <class AppleARMCPU, id 0x100000221, registered, matched, active, busy 0 (5 ms), retain 6>
333 * | +-o cpus <class IOPlatformDevice, id 0x10000010e, registered, matched, active, busy 0 (12 ms), retain 15>
334 *
335 */
336
337 /* Assume names on the form "cpu<N>" are only for CPUs. */
338 char szCpuName[32];
339 RTStrPrintf(szCpuName, sizeof(szCpuName), "cpu%u", idCpu);
340 CFMutableDictionaryRef hMatchingDict = IOServiceNameMatching(szCpuName);
341 AssertReturn(hMatchingDict, 0);
342
343 /* Just get the first one. */
344 io_object_t hCpu = IOServiceGetMatchingService(kIOMasterPortDefault, hMatchingDict);
345 if (hCpu != 0)
346 {
347 uint32_t uCpuFrequency = rtMpDarwinGetMaxFrequencyFromIOService(hCpu);
348 IOObjectRelease(hCpu);
349 if (uCpuFrequency)
350 return uCpuFrequency;
351 }
352
353#if 1 /* Just in case... */
354 /* Create a matching dictionary for searching for CPU services in the IOKit. */
355# if defined(RT_ARCH_ARM64) || defined(RT_ARCH_ARM32)
356 hMatchingDict = IOServiceMatching("AppleARMCPU");
357# elif defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
358 hMatchingDict = IOServiceMatching("AppleACPICPU");
359# else
360# error "Port me!"
361# endif
362 AssertReturn(hMatchingDict, 0);
363
364 /* Perform the search and get a collection of Apple CPU services. */
365 io_iterator_t hCpuServices = IO_OBJECT_NULL;
366 IOReturn irc = IOServiceGetMatchingServices(kIOMasterPortDefault, hMatchingDict, &hCpuServices);
367 AssertMsgReturn(irc == kIOReturnSuccess, ("irc=%d\n", irc), 0);
368 hMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
369
370 /* Enumerate the matching services. */
371 uint32_t uCpuFrequency = 0;
372 io_object_t hCurCpu;
373 while (uCpuFrequency == 0 && (hCurCpu = IOIteratorNext(hCpuServices)) != IO_OBJECT_NULL)
374 {
375 io_object_t hParent = (io_object_t)0;
376 irc = IORegistryEntryGetParentEntry(hCurCpu, kIOServicePlane, &hParent);
377 if (irc == kIOReturnSuccess && hParent)
378 {
379 uCpuFrequency = rtMpDarwinGetMaxFrequencyFromIOService(hParent);
380 IOObjectRelease(hParent);
381 }
382 IOObjectRelease(hCurCpu);
383 }
384 IOObjectRelease(hCpuServices);
385#endif
386
387 AssertFailed();
388 return 0;
389}
390
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