2 * SPDX-License-Identifier: GPL-2.0-or-later
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * Copyright (C) 2020 Karel Zak <kzak@redhat.com>
11 #include <sys/utsname.h>
12 #include <sys/personality.h>
14 #if defined(HAVE_LIBRTAS)
20 #include "fileutils.h"
23 /* Lookup a pattern and get the value for format "<pattern> : <key>"
25 int lookup(char *line
, char *pattern
, char **value
)
28 int len
= strlen(pattern
);
30 /* don't re-fill already found tags, first one wins */
34 if (strncmp(line
, pattern
, len
))
37 for (p
= line
+ len
; isspace(*p
); p
++);
43 for (++p
; isspace(*p
); p
++);
51 len
= strlen(line
) - 1;
52 for (p
= line
+ len
; isspace(*(p
-1)); p
--);
59 struct lscpu_cputype
*lscpu_new_cputype(void)
61 struct lscpu_cputype
*ct
;
63 ct
= xcalloc(1, sizeof(struct lscpu_cputype
));
68 DBG(TYPE
, ul_debugobj(ct
, "alloc"));
72 void lscpu_ref_cputype(struct lscpu_cputype
*ct
)
76 DBG(TYPE
, ul_debugobj(ct
, ">>> ref %d", ct
->refcount
));
80 void lscpu_unref_cputype(struct lscpu_cputype
*ct
)
85 /*DBG(TYPE, ul_debugobj(ct, ">>> unref %d", ct->refcount - 1));*/
87 if (--ct
->refcount
<= 0) {
88 DBG(TYPE
, ul_debugobj(ct
, " freeing %s/%s", ct
->vendor
, ct
->model
));
89 lscpu_cputype_free_topology(ct
);
91 free(ct
->bios_vendor
);
92 free(ct
->machinetype
); /* s390 */
96 free(ct
->bios_modelname
);
97 free(ct
->bios_family
);
98 free(ct
->revision
); /* alternative for model (ppc) */
102 free(ct
->mtid
); /* maximum thread id (s390) */
103 free(ct
->addrsz
); /* address sizes */
104 free(ct
->static_mhz
);
105 free(ct
->dynamic_mhz
);
110 struct lscpu_cputype
*lscpu_cputype_get_default(struct lscpu_cxt
*cxt
)
112 return cxt
->cputypes
? cxt
->cputypes
[0] : NULL
;
115 #define match(astr, bstr) \
116 ((!astr && !bstr) || (astr && bstr && strcmp(astr, bstr) == 0))
118 struct lscpu_cputype
*lscpu_add_cputype(struct lscpu_cxt
*cxt
, struct lscpu_cputype
*ct
)
120 DBG(TYPE
, ul_debugobj(ct
, "add new"));
121 cxt
->cputypes
= xreallocarray(cxt
->cputypes
, cxt
->ncputypes
+ 1,
122 sizeof(struct lscpu_cputype
*));
123 cxt
->cputypes
[cxt
->ncputypes
] = ct
;
125 lscpu_ref_cputype(ct
);
129 static void fprintf_cputypes(FILE *f
, struct lscpu_cxt
*cxt
)
133 for (i
= 0; i
< cxt
->ncputypes
; i
++) {
134 struct lscpu_cputype
*ct
= cxt
->cputypes
[i
];
136 fprintf(f
, "\n vendor: %s\n", ct
->vendor
);
137 fprintf(f
, " machinetype: %s\n", ct
->machinetype
);
138 fprintf(f
, " family: %s\n", ct
->family
);
139 fprintf(f
, " model: %s\n", ct
->model
);
140 fprintf(f
, " modelname: %s\n", ct
->modelname
);
141 fprintf(f
, " revision: %s\n", ct
->revision
);
142 fprintf(f
, " stepping: %s\n", ct
->stepping
);
143 fprintf(f
, " mtid: %s\n", ct
->mtid
);
144 fprintf(f
, " addrsz: %s\n", ct
->addrsz
);
149 CPUINFO_LINE_UNKNOWN
, /* unknown line */
150 CPUINFO_LINE_CPUTYPE
, /* line found in type_patterns[] */
151 CPUINFO_LINE_CPU
, /* line found in cpu_patterns[] */
152 CPUINFO_LINE_CACHE
/* line found in cache_pattern[] */
155 /* Describes /proc/cpuinfo fields */
156 struct cpuinfo_pattern
{
157 int id
; /* field ID */
158 int domain
; /* CPUINFO_LINE_* */
159 const char *pattern
; /* field name as used in /proc/cpuinfo */
160 size_t offset
; /* offset in lscpu_cputype or lscpu_cpu struct */
163 /* field identifiers (field name may be different on different archs) */
166 PAT_BOGOMIPS
, /* global */
167 PAT_BOGOMIPS_CPU
, /* per-cpu */
191 * /proc/cpuinfo to lscpu_cputype conversion
193 #define DEF_PAT_CPUTYPE(_str, _id, _member) \
196 .domain = CPUINFO_LINE_CPUTYPE, \
198 .offset = offsetof(struct lscpu_cputype, _member), \
201 static const struct cpuinfo_pattern type_patterns
[] =
203 /* Sort by fields name! */
204 DEF_PAT_CPUTYPE( "ASEs implemented", PAT_FLAGS
, flags
), /* mips */
205 DEF_PAT_CPUTYPE( "Address Sizes", PAT_ADDRESS_SIZES
, addrsz
),/* loongarch */
206 DEF_PAT_CPUTYPE( "BogoMIPS", PAT_BOGOMIPS
, bogomips
), /* aarch64 */
207 DEF_PAT_CPUTYPE( "CPU Family", PAT_FAMILY
, family
), /* loongarch */
208 DEF_PAT_CPUTYPE( "CPU Revision", PAT_REVISION
, revision
), /* loongarch */
209 DEF_PAT_CPUTYPE( "CPU implementer", PAT_IMPLEMENTER
,vendor
), /* ARM and aarch64 */
210 DEF_PAT_CPUTYPE( "CPU part", PAT_PART
, model
), /* ARM and aarch64 */
211 DEF_PAT_CPUTYPE( "CPU revision", PAT_REVISION
, revision
), /* aarch64 */
212 DEF_PAT_CPUTYPE( "CPU variant", PAT_VARIANT
, stepping
), /* aarch64 */
213 DEF_PAT_CPUTYPE( "Features", PAT_FEATURES
, flags
), /* aarch64 */
214 DEF_PAT_CPUTYPE( "ISA", PAT_ISA
, isa
), /* loongarch */
215 DEF_PAT_CPUTYPE( "Model Name", PAT_MODEL_NAME
, modelname
), /* loongarch */
216 DEF_PAT_CPUTYPE( "address sizes", PAT_ADDRESS_SIZES
, addrsz
),/* x86 */
217 DEF_PAT_CPUTYPE( "bogomips per cpu", PAT_BOGOMIPS
, bogomips
), /* s390 */
218 DEF_PAT_CPUTYPE( "cpu", PAT_CPU
, modelname
), /* ppc, sparc */
219 DEF_PAT_CPUTYPE( "cpu family", PAT_FAMILY
, family
),
220 DEF_PAT_CPUTYPE( "cpu model", PAT_MODEL
, model
), /* mips */
221 DEF_PAT_CPUTYPE( "family", PAT_FAMILY
, family
),
222 DEF_PAT_CPUTYPE( "features", PAT_FEATURES
, flags
), /* s390 */
223 DEF_PAT_CPUTYPE( "flags", PAT_FLAGS
, flags
), /* x86 */
224 DEF_PAT_CPUTYPE( "max thread id", PAT_MAX_THREAD_ID
, mtid
), /* s390 */
225 DEF_PAT_CPUTYPE( "model", PAT_MODEL
, model
),
226 DEF_PAT_CPUTYPE( "model name", PAT_MODEL_NAME
, modelname
),
227 DEF_PAT_CPUTYPE( "revision", PAT_REVISION
, revision
),
228 DEF_PAT_CPUTYPE( "stepping", PAT_STEPPING
, stepping
),
229 DEF_PAT_CPUTYPE( "type", PAT_TYPE
, flags
), /* sparc64 */
230 DEF_PAT_CPUTYPE( "vendor", PAT_VENDOR
, vendor
),
231 DEF_PAT_CPUTYPE( "vendor_id", PAT_VENDOR
, vendor
), /* s390 */
235 * /proc/cpuinfo to lscpu_cpu conversion
237 #define DEF_PAT_CPU(_str, _id, _member) \
240 .domain = CPUINFO_LINE_CPU, \
242 .offset = offsetof(struct lscpu_cpu, _member), \
245 static const struct cpuinfo_pattern cpu_patterns
[] =
247 /* Sort by fields name! */
248 DEF_PAT_CPU( "CPU MHz", PAT_MHZ
, mhz
), /* loongarch */
249 DEF_PAT_CPU( "bogomips", PAT_BOGOMIPS_CPU
, bogomips
),
250 DEF_PAT_CPU( "cpu MHz", PAT_MHZ
, mhz
),
251 DEF_PAT_CPU( "cpu MHz dynamic", PAT_MHZ_DYNAMIC
, dynamic_mhz
), /* s390 */
252 DEF_PAT_CPU( "cpu MHz static", PAT_MHZ_STATIC
, static_mhz
), /* s390 */
253 DEF_PAT_CPU( "cpu number", PAT_PROCESSOR
, logical_id
), /* s390 */
254 DEF_PAT_CPU( "processor", PAT_PROCESSOR
, logical_id
),
259 * /proc/cpuinfo to lscpu_cache conversion
261 #define DEF_PAT_CACHE(_str, _id) \
264 .domain = CPUINFO_LINE_CACHE, \
268 static const struct cpuinfo_pattern cache_patterns
[] =
270 /* Sort by fields name! */
271 DEF_PAT_CACHE("cache", PAT_CACHE
),
274 #define CPUTYPE_PATTERN_BUFSZ 32
276 static int cmp_pattern(const void *a0
, const void *b0
)
278 const struct cpuinfo_pattern
279 *a
= (const struct cpuinfo_pattern
*) a0
,
280 *b
= (const struct cpuinfo_pattern
*) b0
;
281 return strcmp(a
->pattern
, b
->pattern
);
284 struct cpuinfo_parser
{
285 struct lscpu_cxt
*cxt
;
286 struct lscpu_cpu
*curr_cpu
;
287 struct lscpu_cputype
*curr_type
;
288 unsigned int curr_type_added
: 1;
291 static int is_different_cputype(struct lscpu_cputype
*ct
, size_t offset
, const char *value
)
294 case offsetof(struct lscpu_cputype
, vendor
):
295 return ct
->vendor
&& value
&& strcmp(ct
->vendor
, value
) != 0;
296 case offsetof(struct lscpu_cputype
, model
):
297 return ct
->model
&& value
&& strcmp(ct
->model
, value
) != 0;
298 case offsetof(struct lscpu_cputype
, modelname
):
299 return ct
->modelname
&& value
&& strcmp(ct
->modelname
, value
) != 0;
300 case offsetof(struct lscpu_cputype
, stepping
):
301 return ct
->stepping
&& value
&& strcmp(ct
->stepping
, value
) != 0;
306 /* canonicalize @str -- remove number at the end return the
307 * number by @keynum. This is usable for example for "processor 5" or "cache1"
309 static char *key_cleanup(char *str
, int *keynum
)
311 size_t sz
= rtrim_whitespace((unsigned char *)str
);
317 for (i
= sz
; i
> 0; i
--) {
318 if (!isdigit(str
[i
- 1]))
323 char *end
= NULL
, *p
= str
+ i
;
327 n
= strtol(p
, &end
, 10);
328 if (errno
|| !end
|| end
== p
)
333 rtrim_whitespace((unsigned char *)str
);
338 static const struct cpuinfo_pattern
*cpuinfo_parse_line(char *str
, char **value
, int *keynum
)
340 struct cpuinfo_pattern key
= { .id
= 0 }, *pat
;
342 char buf
[CPUTYPE_PATTERN_BUFSZ
] = { 0 };
344 DBG(GATHER
, ul_debug("parse \"%s\"", str
));
348 p
= (char *) skip_blank(str
);
356 /* prepare name of the field */
357 xstrncpy(buf
, p
, sizeof(buf
));
362 v
= (char *) skip_space(v
);
366 key
.pattern
= key_cleanup(buf
, keynum
);
368 if ((pat
= bsearch(&key
, type_patterns
,
369 ARRAY_SIZE(type_patterns
),
370 sizeof(struct cpuinfo_pattern
),
375 if ((pat
= bsearch(&key
, cpu_patterns
,
376 ARRAY_SIZE(cpu_patterns
),
377 sizeof(struct cpuinfo_pattern
),
382 if ((pat
= bsearch(&key
, cache_patterns
,
383 ARRAY_SIZE(cache_patterns
),
384 sizeof(struct cpuinfo_pattern
),
390 rtrim_whitespace((unsigned char *) v
);
395 /* Parse extra cache lines contained within /proc/cpuinfo but which are not
396 * part of the cache topology information within the sysfs filesystem. This is
397 * true for all shared caches on e.g. s390. When there are layers of
398 * hypervisors in between it is not knows which CPUs share which caches.
399 * Therefore information about shared caches is only available in
400 * /proc/cpuinfo. Format is:
402 * cache<nr> : level=<lvl> type=<type> scope=<scope> size=<size> line_size=<lsz> associativity=<as>
404 * the cache<nr> part is parsed in cpuinfo_parse_line, in this function parses part after ":".
406 static int cpuinfo_parse_cache(struct lscpu_cxt
*cxt
, int keynum
, char *data
)
408 struct lscpu_cache
*cache
;
412 unsigned int line_size
, associativity
;
414 DBG(GATHER
, ul_debugobj(cxt
, " parse cpuinfo cache '%s'", data
));
416 p
= strstr(data
, "scope=") + 6;
417 /* Skip private caches, also present in sysfs */
418 if (!p
|| strncmp(p
, "Private", 7) == 0)
420 p
= strstr(data
, "level=");
421 if (!p
|| sscanf(p
, "level=%d", &level
) != 1)
423 p
= strstr(data
, "type=") + 5;
427 if (strncmp(p
, "Data", 4) == 0)
429 else if (strncmp(p
, "Instruction", 11) == 0)
431 else if (strncmp(p
, "Unified", 7) == 0)
433 p
= strstr(data
, "size=");
434 if (!p
|| sscanf(p
, "size=%lld", &size
) != 1)
437 p
= strstr(data
, "line_size=");
438 if (!p
|| sscanf(p
, "line_size=%u", &line_size
) != 1)
441 p
= strstr(data
, "associativity=");
442 if (!p
|| sscanf(p
, "associativity=%u", &associativity
) != 1)
446 cxt
->ecaches
= xreallocarray(cxt
->ecaches
,
447 cxt
->necaches
, sizeof(struct lscpu_cache
));
448 cache
= &cxt
->ecaches
[cxt
->necaches
- 1];
449 memset(cache
, 0 , sizeof(*cache
));
451 if (type
== 'i' || type
== 'd')
452 xasprintf(&cache
->name
, "L%d%c", level
, type
);
454 xasprintf(&cache
->name
, "L%d", level
);
457 cache
->level
= level
;
458 cache
->size
= size
* 1024;
459 cache
->ways_of_associativity
= associativity
;
460 cache
->coherency_line_size
= line_size
;
461 /* Number of sets for s390. For safety, just check divide by zero */
462 cache
->number_of_sets
= line_size
? (cache
->size
/ line_size
): 0;
463 cache
->number_of_sets
= associativity
? (cache
->number_of_sets
/ associativity
) : 0;
465 cache
->type
= type
== 'i' ? xstrdup("Instruction") :
466 type
== 'd' ? xstrdup("Data") :
467 type
== 'u' ? xstrdup("Unified") : NULL
;
471 int lscpu_read_cpuinfo(struct lscpu_cxt
*cxt
)
474 /* Used to be BUFSIZ which is small on some platforms e.g, musl,
475 * therefore hardcode to 4K */
478 struct lscpu_cputype
*ct
;
479 struct cpuinfo_parser _pr
= { .cxt
= cxt
}, *pr
= &_pr
;
481 assert(cxt
->npossibles
); /* lscpu_create_cpus() required */
484 DBG(GATHER
, ul_debugobj(cxt
, "reading cpuinfo"));
486 fp
= ul_path_fopen(cxt
->procfs
, "r", "cpuinfo");
488 err(EXIT_FAILURE
, _("cannot open %s"), "/proc/cpuinfo");
492 char *p
= NULL
, *value
= NULL
;
493 const struct cpuinfo_pattern
*pattern
;
495 if (fgets(buf
, sizeof(buf
), fp
) != NULL
)
496 p
= (char *) skip_space(buf
);
498 if (p
== NULL
|| (*buf
&& !*p
)) {
499 /* Blank line separates information */
501 break; /* fgets() returns nothing; EOF */
505 rtrim_whitespace((unsigned char *) buf
);
508 pattern
= cpuinfo_parse_line(p
, &value
, &keynum
);
510 DBG(GATHER
, ul_debug("'%s' not found", buf
));
515 switch (pattern
->domain
) {
516 case CPUINFO_LINE_CPU
:
517 if (pattern
->id
== PAT_PROCESSOR
) {
525 if (ul_strtou32(value
, &n
, 10) == 0)
529 if (pr
->curr_cpu
&& pr
->curr_type
)
530 lscpu_cpu_set_type(pr
->curr_cpu
, pr
->curr_type
);
532 lscpu_unref_cpu(pr
->curr_cpu
);
533 pr
->curr_cpu
= lscpu_get_cpu(cxt
, id
);
536 DBG(GATHER
, ul_debug("*** cpu ID '%d' undefined", id
));
538 DBG(GATHER
, ul_debug(" switch to CPU %d", id
));
539 lscpu_ref_cpu(pr
->curr_cpu
);
543 DBG(GATHER
, ul_debug("*** cpu data before cpu ID"));
545 strdup_to_offset(pr
->curr_cpu
, pattern
->offset
, value
);
547 if (pattern
->id
== PAT_MHZ_DYNAMIC
&& pr
->curr_type
&& !pr
->curr_type
->dynamic_mhz
)
548 pr
->curr_type
->dynamic_mhz
= xstrdup(value
);
549 if (pattern
->id
== PAT_MHZ_STATIC
&& pr
->curr_type
&& !pr
->curr_type
->static_mhz
)
550 pr
->curr_type
->static_mhz
= xstrdup(value
);
551 if (pattern
->id
== PAT_BOGOMIPS_CPU
&& pr
->curr_type
&& !pr
->curr_type
->bogomips
)
552 pr
->curr_type
->bogomips
= xstrdup(value
);
553 if (pattern
->id
== PAT_MHZ
&& pr
->curr_cpu
&& value
) {
555 pr
->curr_cpu
->mhz_cur_freq
= (float) c_strtod(value
, NULL
);
557 pr
->curr_cpu
->mhz_cur_freq
= 0;
560 case CPUINFO_LINE_CPUTYPE
:
561 if (pr
->curr_type
&& is_different_cputype(pr
->curr_type
, pattern
->offset
, value
)) {
562 lscpu_unref_cputype(pr
->curr_type
);
563 pr
->curr_type
= NULL
;
565 if (!pr
->curr_type
) {
566 pr
->curr_type
= lscpu_new_cputype();
567 lscpu_add_cputype(cxt
, pr
->curr_type
);
570 strdup_to_offset(pr
->curr_type
, pattern
->offset
, value
);
572 case CPUINFO_LINE_CACHE
:
573 if (pattern
->id
!= PAT_CACHE
)
575 cpuinfo_parse_cache(cxt
, keynum
, value
);
580 DBG(GATHER
, fprintf_cputypes(stderr
, cxt
));
582 if (pr
->curr_cpu
&& !pr
->curr_cpu
->type
)
583 lscpu_cpu_set_type(pr
->curr_cpu
, pr
->curr_type
);
585 lscpu_unref_cputype(pr
->curr_type
);
586 lscpu_unref_cpu(pr
->curr_cpu
);
589 lscpu_sort_caches(cxt
->ecaches
, cxt
->necaches
);
591 /* Set the default type to CPUs which are missing (or not parsed)
593 ct
= lscpu_cputype_get_default(cxt
);
594 for (i
= 0; ct
&& i
< cxt
->npossibles
; i
++) {
595 struct lscpu_cpu
*cpu
= cxt
->cpus
[i
];
597 if (cpu
&& !cpu
->type
)
598 lscpu_cpu_set_type(cpu
, ct
);
604 struct lscpu_arch
*lscpu_read_architecture(struct lscpu_cxt
*cxt
)
606 struct utsname utsbuf
;
607 struct lscpu_arch
*ar
;
608 struct lscpu_cputype
*ct
;
612 DBG(GATHER
, ul_debug("reading architecture"));
614 if (uname(&utsbuf
) == -1)
615 err(EXIT_FAILURE
, _("error: uname failed"));
617 ar
= xcalloc(1, sizeof(*cxt
->arch
));
618 ar
->name
= xstrdup(utsbuf
.machine
);
621 /* reading info from any /{sys,proc} dump, don't mix it with
622 * information about our real CPU */
625 #if defined(__alpha__) || defined(__ia64__)
626 ar
->bit64
= 1; /* 64bit platforms only */
628 /* platforms with 64bit flag in /proc/cpuinfo, define
629 * 32bit default here */
630 #if defined(__i386__) || defined(__x86_64__) || \
631 defined(__s390x__) || defined(__s390__) || defined(__sparc_v9__)
635 #if defined(__aarch64__)
637 /* personality() is the most reliable way (since 4.7)
638 * to determine aarch32 support */
639 int pers
= personality(PER_LINUX32
);
649 ct
= lscpu_cputype_get_default(cxt
);
650 if (ct
&& ct
->flags
) {
653 snprintf(buf
, sizeof(buf
), " %s ", ct
->flags
);
654 if (strstr(buf
, " lm "))
655 ar
->bit32
= ar
->bit64
= 1; /* x86_64 */
656 if (strstr(buf
, " zarch "))
657 ar
->bit32
= ar
->bit64
= 1; /* s390x */
658 if (strstr(buf
, " sun4v ") || strstr(buf
, " sun4u "))
659 ar
->bit32
= ar
->bit64
= 1; /* sparc64 */
665 snprintf(buf
, sizeof(buf
), " %s ", ct
->isa
);
666 if (strstr(buf
, " loongarch32 "))
668 if (strstr(buf
, " loongarch64 "))
672 if (ar
->name
&& !cxt
->noalive
) {
673 if (strcmp(ar
->name
, "ppc64") == 0)
674 ar
->bit32
= 1, ar
->bit64
= 1;
675 else if (strcmp(ar
->name
, "ppc") == 0)
679 DBG(GATHER
, ul_debugobj(ar
, "arch: name=%s %s %s",
681 ar
->bit64
? "64-bit" : "",
682 ar
->bit64
? "32-bit" : ""));
686 void lscpu_free_architecture(struct lscpu_arch
*ar
)
694 int lscpu_read_cpulists(struct lscpu_cxt
*cxt
)
696 cpu_set_t
*cpuset
= NULL
;
699 DBG(GATHER
, ul_debugobj(cxt
, "reading cpulists"));
701 if (ul_path_read_s32(cxt
->syscpu
, &cxt
->maxcpus
, "kernel_max") == 0)
702 /* note that kernel_max is maximum index [NR_CPUS-1] */
705 else if (!cxt
->noalive
)
706 /* the root is '/' so we are working with data from the current kernel */
707 cxt
->maxcpus
= get_max_number_of_cpus();
709 if (cxt
->maxcpus
<= 0)
710 /* error or we are reading some /sys snapshot instead of the
711 * real /sys, let's use any crazy number... */
714 cxt
->setsize
= CPU_ALLOC_SIZE(cxt
->maxcpus
);
716 /* create CPUs from possible mask */
717 if (ul_path_readf_cpulist(cxt
->syscpu
, &cpuset
, cxt
->maxcpus
, "possible") == 0) {
718 lscpu_create_cpus(cxt
, cpuset
, cxt
->setsize
);
722 err(EXIT_FAILURE
, _("failed to determine number of CPUs: %s"),
723 _PATH_SYS_CPU
"/possible");
726 /* get mask for present CPUs */
727 if (ul_path_readf_cpulist(cxt
->syscpu
, &cxt
->present
, cxt
->maxcpus
, "present") == 0)
728 cxt
->npresents
= CPU_COUNT_S(cxt
->setsize
, cxt
->present
);
730 /* get mask for online CPUs */
731 if (ul_path_readf_cpulist(cxt
->syscpu
, &cxt
->online
, cxt
->maxcpus
, "online") == 0)
732 cxt
->nonlines
= CPU_COUNT_S(cxt
->setsize
, cxt
->online
);
737 #if defined(HAVE_LIBRTAS)
738 # define PROCESSOR_MODULE_INFO 43
739 static int strbe16toh(const char *buf
, int offset
)
741 return (buf
[offset
] << 8) + buf
[offset
+1];
745 /* some extra information for the default CPU type */
746 int lscpu_read_archext(struct lscpu_cxt
*cxt
)
750 struct lscpu_cputype
*ct
;
752 DBG(GATHER
, ul_debugobj(cxt
, "reading extra arch info"));
755 ct
= lscpu_cputype_get_default(cxt
);
759 /* get dispatching mode */
760 if (ul_path_read_s32(cxt
->syscpu
, &ct
->dispatching
, "dispatching") != 0)
761 ct
->dispatching
= -1;
763 /* get cpufreq boost mode */
764 if (ul_path_read_s32(cxt
->syscpu
, &ct
->freqboost
, "cpufreq/boost") != 0)
767 if ((f
= ul_path_fopen(cxt
->procfs
, "r", "sysinfo"))) {
768 while (fgets(buf
, sizeof(buf
), f
) != NULL
) {
769 if (lookup(buf
, "Type", &ct
->machinetype
))
775 #if defined(HAVE_LIBRTAS)
776 /* Get PowerPC specific info */
780 ct
->physsockets
= ct
->physchips
= ct
->physcoresperchip
= 0;
782 rc
= rtas_get_sysparm(PROCESSOR_MODULE_INFO
, sizeof(buf
), buf
);
786 len
= strbe16toh(buf
, 0);
790 ntypes
= strbe16toh(buf
, 2);
794 ct
->physsockets
= strbe16toh(buf
, 4);
795 ct
->physchips
= strbe16toh(buf
, 6);
796 ct
->physcoresperchip
= strbe16toh(buf
, 8);
803 static int cmp_vulnerability_name(const void *a0
, const void *b0
)
805 const struct lscpu_vulnerability
806 *a
= (const struct lscpu_vulnerability
*) a0
,
807 *b
= (const struct lscpu_vulnerability
*) b0
;
808 return strcmp(a
->name
, b
->name
);
811 int lscpu_read_vulnerabilities(struct lscpu_cxt
*cxt
)
819 DBG(GATHER
, ul_debugobj(cxt
, "reading vulnerabilities"));
821 dir
= ul_path_opendir(cxt
->syscpu
, "vulnerabilities");
826 while (xreaddir(dir
))
834 cxt
->vuls
= xcalloc(n
, sizeof(struct lscpu_vulnerability
));
836 while (cxt
->nvuls
< n
&& (d
= xreaddir(dir
))) {
838 struct lscpu_vulnerability
*vu
;
840 #ifdef _DIRENT_HAVE_D_TYPE
841 if (d
->d_type
== DT_DIR
|| d
->d_type
== DT_UNKNOWN
)
844 if (ul_path_readf_string(cxt
->syscpu
, &str
,
845 "vulnerabilities/%s", d
->d_name
) <= 0)
848 vu
= &cxt
->vuls
[cxt
->nvuls
++];
851 vu
->name
= xstrdup(d
->d_name
);
852 *vu
->name
= toupper(*vu
->name
);
853 strrep(vu
->name
, '_', ' ');
857 p
= (char *) startswith(vu
->text
, "Mitigation");
860 strrem(vu
->text
, ':');
865 qsort(cxt
->vuls
, cxt
->nvuls
,
866 sizeof(struct lscpu_vulnerability
), cmp_vulnerability_name
);
871 static inline int is_node_dirent(struct dirent
*d
)
875 #ifdef _DIRENT_HAVE_D_TYPE
876 (d
->d_type
== DT_DIR
|| d
->d_type
== DT_UNKNOWN
) &&
878 strncmp(d
->d_name
, "node", 4) == 0 &&
879 isdigit_string(d
->d_name
+ 4);
882 static int nodecmp(const void *ap
, const void *bp
)
884 int *a
= (int *) ap
, *b
= (int *) bp
;
888 int lscpu_read_numas(struct lscpu_cxt
*cxt
)
893 struct path_cxt
*sys
;
895 assert(!cxt
->nnodes
);
898 sys
= ul_new_path(_PATH_SYS_NODE
);
900 err(EXIT_FAILURE
, _("failed to initialize %s handler"), _PATH_SYS_NODE
);
902 ul_path_set_prefix(sys
, cxt
->prefix
);
904 dir
= ul_path_opendir(sys
, NULL
);
908 while ((d
= readdir(dir
))) {
909 if (is_node_dirent(d
))
918 cxt
->nodemaps
= xcalloc(cxt
->nnodes
, sizeof(cpu_set_t
*));
919 cxt
->idx2nodenum
= xmalloc(cxt
->nnodes
* sizeof(int));
922 for (i
= 0; (d
= readdir(dir
)) && i
< cxt
->nnodes
;) {
923 if (is_node_dirent(d
))
924 cxt
->idx2nodenum
[i
++] = strtol_or_err(((d
->d_name
) + 4),
925 _("Failed to extract the node number"));
928 qsort(cxt
->idx2nodenum
, cxt
->nnodes
, sizeof(int), nodecmp
);
930 /* information about how nodes share different CPUs */
931 for (i
= 0; i
< cxt
->nnodes
; i
++)
932 ul_path_readf_cpuset(sys
, &cxt
->nodemaps
[i
], cxt
->maxcpus
,
933 "node%d/cpumap", cxt
->idx2nodenum
[i
]);
935 DBG(GATHER
, ul_debugobj(cxt
, "read %zu numas", cxt
->nnodes
));