1 /*
2  *  Copyright (C) 2005-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "CPUInfoFreebsd.h"
10 
11 #include "utils/Temperature.h"
12 #include "utils/log.h"
13 
14 #include <array>
15 #include <vector>
16 
17 #if defined(__i386__) || defined(__x86_64__)
18 #include <cpuid.h>
19 #elif __has_include(<sys/auxv.h>)
20 #include <sys/auxv.h>
21 #endif
22 
23 #include <sys/resource.h>
24 #include <sys/sysctl.h>
25 #include <sys/types.h>
26 
27 namespace
28 {
29 
30 struct CpuData
31 {
32 public:
GetActiveTime__anon575c8d8f0111::CpuData33   std::size_t GetActiveTime() const { return state[CP_USER] + state[CP_NICE] + state[CP_SYS]; }
34 
GetIdleTime__anon575c8d8f0111::CpuData35   std::size_t GetIdleTime() const { return state[CP_INTR] + state[CP_IDLE]; }
36 
GetTotalTime__anon575c8d8f0111::CpuData37   std::size_t GetTotalTime() const { return GetActiveTime() + GetIdleTime(); }
38 
39   std::size_t state[CPUSTATES];
40 };
41 
42 } // namespace
43 
GetCPUInfo()44 std::shared_ptr<CCPUInfo> CCPUInfo::GetCPUInfo()
45 {
46   return std::make_shared<CCPUInfoFreebsd>();
47 }
48 
CCPUInfoFreebsd()49 CCPUInfoFreebsd::CCPUInfoFreebsd()
50 {
51   int count = 0;
52   size_t countLength = sizeof(count);
53   if (sysctlbyname("hw.ncpu", &count, &countLength, nullptr, 0) == 0)
54     m_cpuCount = count;
55   else
56     m_cpuCount = 1;
57 
58   std::array<char, 512> cpuModel;
59   size_t length = cpuModel.size();
60   if (sysctlbyname("hw.model", cpuModel.data(), &length, nullptr, 0) == 0)
61     m_cpuModel = cpuModel.data();
62 
63   for (size_t i = 0; i < m_cpuCount; i++)
64   {
65     CoreInfo core;
66     core.m_id = i;
67     m_cores.emplace_back(core);
68   }
69 #if defined(__i386__) || defined(__x86_64__)
70   uint32_t eax, ebx, ecx, edx;
71 
72   m_cpuVendor.clear();
73 
74   if (__get_cpuid(CPUID_INFOTYPE_MANUFACTURER, &eax, &ebx, &ecx, &edx))
75   {
76     m_cpuVendor.append(reinterpret_cast<const char*>(&ebx), 4);
77     m_cpuVendor.append(reinterpret_cast<const char*>(&edx), 4);
78     m_cpuVendor.append(reinterpret_cast<const char*>(&ecx), 4);
79   }
80 
81   if (__get_cpuid(CPUID_INFOTYPE_EXTENDED_IMPLEMENTED, &eax, &ebx, &ecx, &edx))
82   {
83     if (eax >= CPUID_INFOTYPE_PROCESSOR_3)
84     {
85       m_cpuModel.clear();
86 
87       if (__get_cpuid(CPUID_INFOTYPE_PROCESSOR_1, &eax, &ebx, &ecx, &edx))
88       {
89         m_cpuModel.append(reinterpret_cast<const char*>(&eax), 4);
90         m_cpuModel.append(reinterpret_cast<const char*>(&ebx), 4);
91         m_cpuModel.append(reinterpret_cast<const char*>(&ecx), 4);
92         m_cpuModel.append(reinterpret_cast<const char*>(&edx), 4);
93       }
94 
95       if (__get_cpuid(CPUID_INFOTYPE_PROCESSOR_2, &eax, &ebx, &ecx, &edx))
96       {
97         m_cpuModel.append(reinterpret_cast<const char*>(&eax), 4);
98         m_cpuModel.append(reinterpret_cast<const char*>(&ebx), 4);
99         m_cpuModel.append(reinterpret_cast<const char*>(&ecx), 4);
100         m_cpuModel.append(reinterpret_cast<const char*>(&edx), 4);
101       }
102 
103       if (__get_cpuid(CPUID_INFOTYPE_PROCESSOR_3, &eax, &ebx, &ecx, &edx))
104       {
105         m_cpuModel.append(reinterpret_cast<const char*>(&eax), 4);
106         m_cpuModel.append(reinterpret_cast<const char*>(&ebx), 4);
107         m_cpuModel.append(reinterpret_cast<const char*>(&ecx), 4);
108         m_cpuModel.append(reinterpret_cast<const char*>(&edx), 4);
109       }
110     }
111   }
112 
113   if (__get_cpuid(CPUID_INFOTYPE_STANDARD, &eax, &eax, &ecx, &edx))
114   {
115     if (edx & CPUID_00000001_EDX_MMX)
116       m_cpuFeatures |= CPU_FEATURE_MMX;
117 
118     // Set MMX2 when SSE is present as SSE is a superset of MMX2 and Intel doesn't set the MMX2 cap
119     if (edx & CPUID_00000001_EDX_SSE)
120       m_cpuFeatures |= (CPU_FEATURE_SSE | CPU_FEATURE_MMX2);
121 
122     if (edx & CPUID_00000001_EDX_SSE2)
123       m_cpuFeatures |= CPU_FEATURE_SSE2;
124 
125     if (ecx & CPUID_00000001_ECX_SSE3)
126       m_cpuFeatures |= CPU_FEATURE_SSE3;
127 
128     if (ecx & CPUID_00000001_ECX_SSSE3)
129       m_cpuFeatures |= CPU_FEATURE_SSSE3;
130 
131     if (ecx & CPUID_00000001_ECX_SSE4)
132       m_cpuFeatures |= CPU_FEATURE_SSE4;
133 
134     if (ecx & CPUID_00000001_ECX_SSE42)
135       m_cpuFeatures |= CPU_FEATURE_SSE42;
136   }
137 
138   if (__get_cpuid(CPUID_INFOTYPE_EXTENDED_IMPLEMENTED, &eax, &eax, &ecx, &edx))
139   {
140     if (eax >= CPUID_INFOTYPE_EXTENDED)
141     {
142       if (edx & CPUID_80000001_EDX_MMX)
143         m_cpuFeatures |= CPU_FEATURE_MMX;
144 
145       if (edx & CPUID_80000001_EDX_MMX2)
146         m_cpuFeatures |= CPU_FEATURE_MMX2;
147 
148       if (edx & CPUID_80000001_EDX_3DNOW)
149         m_cpuFeatures |= CPU_FEATURE_3DNOW;
150 
151       if (edx & CPUID_80000001_EDX_3DNOWEXT)
152         m_cpuFeatures |= CPU_FEATURE_3DNOWEXT;
153     }
154   }
155 #endif
156 
157 #if defined(HAS_NEON)
158 #if defined(__ARM_NEON)
159   m_cpuFeatures |= CPU_FEATURE_NEON;
160 #elif __has_include(<sys/auxv.h>)
161   unsigned long hwcap = 0;
162   elf_aux_info(AT_HWCAP, &hwcap, sizeof(hwcap));
163   if (hwcap & HWCAP_NEON)
164     m_cpuFeatures |= CPU_FEATURE_NEON;
165 #endif
166 #endif
167 }
168 
GetUsedPercentage()169 int CCPUInfoFreebsd::GetUsedPercentage()
170 {
171   if (!m_nextUsedReadTime.IsTimePast())
172     return m_lastUsedPercentage;
173 
174   size_t len = sizeof(long);
175 
176   if (sysctlbyname("kern.cp_times", nullptr, &len, nullptr, 0) != 0)
177     return false;
178 
179   std::vector<long> cptimes(len);
180   size_t cptimesLength = cptimes.size();
181   if (sysctlbyname("kern.cp_times", cptimes.data(), &cptimesLength, nullptr, 0) != 0)
182     return false;
183 
184   size_t activeTime{0};
185   size_t idleTime{0};
186   size_t totalTime{0};
187 
188   std::vector<CpuData> cpuData;
189 
190   for (size_t i = 0; i < m_cpuCount; i++)
191   {
192     CpuData info;
193 
194     for (size_t state = 0; state < CPUSTATES; state++)
195     {
196       info.state[state] = cptimes[i * CPUSTATES + state];
197     }
198 
199     activeTime += info.GetActiveTime();
200     idleTime += info.GetIdleTime();
201     totalTime += info.GetTotalTime();
202 
203     cpuData.emplace_back(info);
204   }
205 
206   activeTime -= m_activeTime;
207   idleTime -= m_idleTime;
208   totalTime -= m_totalTime;
209 
210   m_activeTime += activeTime;
211   m_idleTime += idleTime;
212   m_totalTime += totalTime;
213 
214   m_lastUsedPercentage = activeTime * 100.0f / totalTime;
215   m_nextUsedReadTime.Set(MINIMUM_TIME_BETWEEN_READS);
216 
217   for (size_t core = 0; core < cpuData.size(); core++)
218   {
219     auto activeTime = cpuData[core].GetActiveTime() - m_cores[core].m_activeTime;
220     auto idleTime = cpuData[core].GetIdleTime() - m_cores[core].m_idleTime;
221     auto totalTime = cpuData[core].GetTotalTime() - m_cores[core].m_totalTime;
222 
223     m_cores[core].m_usagePercent = activeTime * 100.0f / totalTime;
224 
225     m_cores[core].m_activeTime += activeTime;
226     m_cores[core].m_idleTime += idleTime;
227     m_cores[core].m_totalTime += totalTime;
228   }
229 
230   return static_cast<int>(m_lastUsedPercentage);
231 }
232 
GetCPUFrequency()233 float CCPUInfoFreebsd::GetCPUFrequency()
234 {
235   int hz = 0;
236   size_t len = sizeof(hz);
237   if (sysctlbyname("dev.cpu.0.freq", &hz, &len, nullptr, 0) != 0)
238     hz = 0;
239 
240   return static_cast<float>(hz);
241 }
242 
243 
GetTemperature(CTemperature & temperature)244 bool CCPUInfoFreebsd::GetTemperature(CTemperature& temperature)
245 {
246   int value;
247   size_t len = sizeof(value);
248 
249   /* Temperature is in Kelvin * 10 */
250   if (sysctlbyname("dev.cpu.0.temperature", &value, &len, nullptr, 0) != 0)
251     return CCPUInfoPosix::GetTemperature(temperature);
252   temperature = CTemperature::CreateFromKelvin(static_cast<double>(value) / 10.0);
253   temperature.SetValid(true);
254 
255   return true;
256 }
257