/src/fwupd/libfwupdplugin/fu-common.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2017 Richard Hughes <richard@hughsie.com> |
3 | | * |
4 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
5 | | */ |
6 | | |
7 | 0 | #define G_LOG_DOMAIN "FuCommon" |
8 | | |
9 | | #include "config.h" |
10 | | |
11 | | #ifdef HAVE_CPUID_H |
12 | | #include <cpuid.h> |
13 | | #endif |
14 | | |
15 | | #include "fu-common-private.h" |
16 | | #include "fu-firmware.h" |
17 | | #include "fu-path.h" |
18 | | #include "fu-string.h" |
19 | | |
20 | | /** |
21 | | * fu_error_map_entry_to_gerror: |
22 | | * @value: the value to look up |
23 | | * @entries: the #FuErrorMapEntry map |
24 | | * @n_entries: number of @entries |
25 | | * @error: (nullable): optional return location for an error |
26 | | * |
27 | | * Sets the #GError from the integer value and the error map. |
28 | | * |
29 | | * Any entries with a error_code of `FWUPD_ERROR_LAST` will return success. |
30 | | * |
31 | | * Returns: boolean success |
32 | | * |
33 | | * Since: 2.0.13 |
34 | | **/ |
35 | | gboolean |
36 | | fu_error_map_entry_to_gerror(guint value, |
37 | | const FuErrorMapEntry entries[], |
38 | | guint n_entries, |
39 | | GError **error) |
40 | 0 | { |
41 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
42 | | |
43 | 0 | for (guint i = 0; i < n_entries; i++) { |
44 | 0 | const FuErrorMapEntry entry = entries[i]; |
45 | 0 | if (entry.value != value) |
46 | 0 | continue; |
47 | 0 | if (entry.code == FWUPD_ERROR_LAST) |
48 | 0 | return TRUE; |
49 | 0 | g_set_error(error, |
50 | 0 | FWUPD_ERROR, |
51 | 0 | entry.code, |
52 | 0 | "%s [0x%x]", |
53 | 0 | entry.message != NULL ? entry.message |
54 | 0 | : fwupd_error_to_string(entry.value), |
55 | 0 | entry.value); |
56 | 0 | return FALSE; |
57 | 0 | } |
58 | 0 | g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "generic failure [0x%x]", value); |
59 | 0 | return FALSE; |
60 | 0 | } |
61 | | |
62 | | /** |
63 | | * fu_cpuid: |
64 | | * @leaf: the CPUID level, now called the 'leaf' by Intel |
65 | | * @eax: (out) (nullable): EAX register |
66 | | * @ebx: (out) (nullable): EBX register |
67 | | * @ecx: (out) (nullable): ECX register |
68 | | * @edx: (out) (nullable): EDX register |
69 | | * @error: (nullable): optional return location for an error |
70 | | * |
71 | | * Calls CPUID and returns the registers for the given leaf. |
72 | | * |
73 | | * Returns: %TRUE if the registers are set. |
74 | | * |
75 | | * Since: 1.8.2 |
76 | | **/ |
77 | | gboolean |
78 | | fu_cpuid(guint32 leaf, guint32 *eax, guint32 *ebx, guint32 *ecx, guint32 *edx, GError **error) |
79 | 0 | { |
80 | | #ifdef HAVE_CPUID_H |
81 | | guint eax_tmp = 0; |
82 | | guint ebx_tmp = 0; |
83 | | guint ecx_tmp = 0; |
84 | | guint edx_tmp = 0; |
85 | | |
86 | | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
87 | | |
88 | | /* get vendor */ |
89 | | __get_cpuid_count(leaf, 0x0, &eax_tmp, &ebx_tmp, &ecx_tmp, &edx_tmp); |
90 | | if (eax != NULL) |
91 | | *eax = eax_tmp; |
92 | | if (ebx != NULL) |
93 | | *ebx = ebx_tmp; |
94 | | if (ecx != NULL) |
95 | | *ecx = ecx_tmp; |
96 | | if (edx != NULL) |
97 | | *edx = edx_tmp; |
98 | | return TRUE; |
99 | | #else |
100 | 0 | g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no <cpuid.h> support"); |
101 | 0 | return FALSE; |
102 | 0 | #endif |
103 | 0 | } |
104 | | |
105 | | /** |
106 | | * fu_cpu_get_attrs: |
107 | | * @error: (nullable): optional return location for an error |
108 | | * |
109 | | * Gets attributes for the first CPU listed in `/proc/cpuinfo`. |
110 | | * |
111 | | * Returns: (element-type utf8 utf8) (transfer full): CPU attributes |
112 | | * |
113 | | * Since: 2.0.7 |
114 | | **/ |
115 | | GHashTable * |
116 | | fu_cpu_get_attrs(GError **error) |
117 | 0 | { |
118 | 0 | gsize bufsz = 0; |
119 | 0 | g_autofree gchar *buf = NULL; |
120 | 0 | g_autofree gchar *procpath = fu_path_from_kind(FU_PATH_KIND_PROCFS); |
121 | 0 | g_autofree gchar *fn = g_build_filename(procpath, "cpuinfo", NULL); |
122 | 0 | g_autoptr(GHashTable) hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); |
123 | |
|
124 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, NULL); |
125 | | |
126 | 0 | if (!g_file_get_contents(fn, &buf, &bufsz, error)) |
127 | 0 | return NULL; |
128 | 0 | if (bufsz > 0) { |
129 | 0 | g_auto(GStrv) lines = fu_strsplit(buf, bufsz, "\n", -1); |
130 | 0 | for (guint i = 0; lines[i] != NULL; i++) { |
131 | 0 | g_auto(GStrv) tokens = NULL; |
132 | 0 | if (lines[i][0] == '\0') |
133 | 0 | break; |
134 | 0 | tokens = g_strsplit(lines[i], ": ", 2); |
135 | 0 | for (guint j = 0; tokens[j] != NULL; j++) { |
136 | 0 | g_hash_table_insert(hash, |
137 | 0 | fu_strstrip(tokens[0]), |
138 | 0 | g_strdup(tokens[1])); |
139 | 0 | } |
140 | 0 | } |
141 | 0 | } |
142 | | |
143 | | /* success */ |
144 | 0 | return g_steal_pointer(&hash); |
145 | 0 | } |
146 | | |
147 | | /** |
148 | | * fu_cpu_get_vendor: |
149 | | * |
150 | | * Uses CPUID to discover the CPU vendor. |
151 | | * |
152 | | * Returns: a CPU vendor, e.g. %FU_CPU_VENDOR_AMD if the vendor was AMD. |
153 | | * |
154 | | * Since: 1.8.2 |
155 | | **/ |
156 | | FuCpuVendor |
157 | | fu_cpu_get_vendor(void) |
158 | 0 | { |
159 | | #ifdef HAVE_CPUID_H |
160 | | guint ebx = 0; |
161 | | guint ecx = 0; |
162 | | guint edx = 0; |
163 | | |
164 | | if (fu_cpuid(0x0, NULL, &ebx, &ecx, &edx, NULL)) { |
165 | | if (ebx == signature_INTEL_ebx && edx == signature_INTEL_edx && |
166 | | ecx == signature_INTEL_ecx) { |
167 | | return FU_CPU_VENDOR_INTEL; |
168 | | } |
169 | | if (ebx == signature_AMD_ebx && edx == signature_AMD_edx && |
170 | | ecx == signature_AMD_ecx) { |
171 | | return FU_CPU_VENDOR_AMD; |
172 | | } |
173 | | } |
174 | | #endif |
175 | | |
176 | | /* failed */ |
177 | 0 | return FU_CPU_VENDOR_UNKNOWN; |
178 | 0 | } |
179 | | |
180 | | /** |
181 | | * fu_common_get_memory_size: |
182 | | * |
183 | | * Returns the size of physical memory. |
184 | | * |
185 | | * Returns: bytes |
186 | | * |
187 | | * Since: 1.5.6 |
188 | | **/ |
189 | | guint64 |
190 | | fu_common_get_memory_size(void) |
191 | 0 | { |
192 | 0 | return fu_common_get_memory_size_impl(); |
193 | 0 | } |
194 | | |
195 | | /** |
196 | | * fu_common_get_kernel_cmdline: |
197 | | * @error: (nullable): optional return location for an error |
198 | | * |
199 | | * Returns the current kernel command line options. |
200 | | * |
201 | | * Returns: options as a string, or %NULL on error |
202 | | * |
203 | | * Since: 1.5.6 |
204 | | **/ |
205 | | gchar * |
206 | | fu_common_get_kernel_cmdline(GError **error) |
207 | 0 | { |
208 | 0 | return fu_common_get_kernel_cmdline_impl(error); |
209 | 0 | } |
210 | | |
211 | | /** |
212 | | * fu_common_get_olson_timezone_id: |
213 | | * @error: (nullable): optional return location for an error |
214 | | * |
215 | | * Gets the system Olson timezone ID, as used in the CLDR and ICU specifications. |
216 | | * |
217 | | * Returns: timezone string, e.g. `Europe/London` or %NULL on error |
218 | | * |
219 | | * Since: 1.9.7 |
220 | | **/ |
221 | | gchar * |
222 | | fu_common_get_olson_timezone_id(GError **error) |
223 | 0 | { |
224 | 0 | return fu_common_get_olson_timezone_id_impl(error); |
225 | 0 | } |
226 | | |
227 | | /** |
228 | | * fu_common_align_up: |
229 | | * @value: value to align |
230 | | * @alignment: align to this power of 2, where 0x1F is the maximum value of 2GB |
231 | | * |
232 | | * Align a value to a power of 2 boundary, where @alignment is the bit position |
233 | | * to align to. If @alignment is zero then @value is always returned unchanged. |
234 | | * |
235 | | * Returns: aligned value, which will be the same as @value if already aligned, |
236 | | * or %G_MAXSIZE if the value would overflow |
237 | | * |
238 | | * Since: 1.6.0 |
239 | | **/ |
240 | | gsize |
241 | | fu_common_align_up(gsize value, guint8 alignment) |
242 | 463k | { |
243 | 463k | gsize value_new; |
244 | 463k | gsize mask = (gsize)1 << alignment; |
245 | | |
246 | 463k | g_return_val_if_fail(alignment <= FU_FIRMWARE_ALIGNMENT_2G, G_MAXSIZE); |
247 | | |
248 | | /* no alignment required */ |
249 | 463k | if ((value & (mask - 1)) == 0) |
250 | 306k | return value; |
251 | | |
252 | | /* increment up to the next alignment value */ |
253 | 157k | value_new = value + mask; |
254 | 157k | value_new &= ~(mask - 1); |
255 | | |
256 | | /* overflow */ |
257 | 157k | if (value_new < value) |
258 | 0 | return G_MAXSIZE; |
259 | | |
260 | | /* success */ |
261 | 157k | return value_new; |
262 | 157k | } |
263 | | |
264 | | /** |
265 | | * fu_power_state_is_ac: |
266 | | * @power_state: a power state, e.g. %FU_POWER_STATE_AC_FULLY_CHARGED |
267 | | * |
268 | | * Determines if the power state can be considered "on AC power". |
269 | | * |
270 | | * Returns: %TRUE for success |
271 | | * |
272 | | * Since: 1.8.11 |
273 | | **/ |
274 | | gboolean |
275 | | fu_power_state_is_ac(FuPowerState power_state) |
276 | 0 | { |
277 | 0 | return power_state != FU_POWER_STATE_BATTERY; |
278 | 0 | } |
279 | | |
280 | | /** |
281 | | * fu_error_convert: |
282 | | * @entries: the #FuErrorConvertEntry map |
283 | | * @n_entries: number of @entries |
284 | | * @perror: (nullable): A #GError, perhaps with domain #GIOError |
285 | | * |
286 | | * Convert the error to a #FwupdError, if required. |
287 | | * |
288 | | * Since: 2.0.14 |
289 | | **/ |
290 | | gboolean |
291 | | fu_error_convert(const FuErrorConvertEntry entries[], guint n_entries, GError **perror) |
292 | 0 | { |
293 | 0 | GError *error = (perror != NULL) ? *perror : NULL; |
294 | | |
295 | | /* sanity check */ |
296 | 0 | if (error == NULL) |
297 | 0 | return TRUE; |
298 | | |
299 | | /* convert GIOError and GFileError */ |
300 | 0 | fwupd_error_convert(perror); |
301 | 0 | if (error->domain == FWUPD_ERROR) |
302 | 0 | return FALSE; |
303 | 0 | for (guint i = 0; i < n_entries; i++) { |
304 | 0 | if (g_error_matches(error, entries[i].domain, entries[i].code)) { |
305 | 0 | error->domain = FWUPD_ERROR; |
306 | 0 | error->code = entries[i].error; |
307 | 0 | return FALSE; |
308 | 0 | } |
309 | 0 | } |
310 | | |
311 | 0 | #ifndef SUPPORTED_BUILD |
312 | | /* fallback */ |
313 | 0 | g_critical("GError %s:%i was not converted to FwupdError", |
314 | 0 | g_quark_to_string(error->domain), |
315 | 0 | error->code); |
316 | 0 | #endif |
317 | 0 | error->domain = FWUPD_ERROR; |
318 | 0 | error->code = FWUPD_ERROR_INTERNAL; |
319 | 0 | return FALSE; |
320 | 0 | } |
321 | | |
322 | | /** |
323 | | * fu_xmlb_builder_insert_kv: |
324 | | * @bn: #XbBuilderNode |
325 | | * @key: string key |
326 | | * @value: string value |
327 | | * |
328 | | * Convenience function to add an XML node with a string value. If @value is %NULL |
329 | | * then no member is added. |
330 | | * |
331 | | * Since: 1.6.0 |
332 | | **/ |
333 | | void |
334 | | fu_xmlb_builder_insert_kv(XbBuilderNode *bn, const gchar *key, const gchar *value) |
335 | 751k | { |
336 | 751k | if (value == NULL) |
337 | 479k | return; |
338 | 272k | xb_builder_node_insert_text(bn, key, value, NULL); |
339 | 272k | } |
340 | | |
341 | | /** |
342 | | * fu_xmlb_builder_insert_kx: |
343 | | * @bn: #XbBuilderNode |
344 | | * @key: string key |
345 | | * @value: integer value |
346 | | * |
347 | | * Convenience function to add an XML node with an integer value. If @value is 0 |
348 | | * then no member is added. |
349 | | * |
350 | | * Since: 1.6.0 |
351 | | **/ |
352 | | void |
353 | | fu_xmlb_builder_insert_kx(XbBuilderNode *bn, const gchar *key, guint64 value) |
354 | 1.20M | { |
355 | 1.20M | g_autofree gchar *value_hex = NULL; |
356 | 1.20M | if (value == 0) |
357 | 865k | return; |
358 | 336k | value_hex = g_strdup_printf("0x%x", (guint)value); |
359 | 336k | xb_builder_node_insert_text(bn, key, value_hex, NULL); |
360 | 336k | } |
361 | | |
362 | | /** |
363 | | * fu_xmlb_builder_insert_kb: |
364 | | * @bn: #XbBuilderNode |
365 | | * @key: string key |
366 | | * @value: boolean value |
367 | | * |
368 | | * Convenience function to add an XML node with a boolean value. |
369 | | * |
370 | | * Since: 1.6.0 |
371 | | **/ |
372 | | void |
373 | | fu_xmlb_builder_insert_kb(XbBuilderNode *bn, const gchar *key, gboolean value) |
374 | 0 | { |
375 | 0 | xb_builder_node_insert_text(bn, key, value ? "true" : "false", NULL); |
376 | 0 | } |
377 | | |
378 | | /** |
379 | | * fu_snap_is_in_snap: |
380 | | * |
381 | | * Check whether the current process is running inside a snap. |
382 | | * |
383 | | * Returns: TRUE if current process is running inside a snap. |
384 | | * |
385 | | * Since: 2.0.4 |
386 | | **/ |
387 | | gboolean |
388 | | fu_snap_is_in_snap(void) |
389 | 0 | { |
390 | 0 | return getenv("SNAP") != NULL; |
391 | 0 | } |