From: "H. Peter Anvin" <hpa@zytor.com>
To: Sohil Mehta <sohil.mehta@intel.com>, x86@kernel.org
Cc: Thomas Gleixner <tglx@kernel.org>, Ingo Molnar <mingo@redhat.com>,
Borislav Petkov <bp@alien8.de>,
Dave Hansen <dave.hansen@linux.intel.com>,
Shuah Khan <shuah@kernel.org>,
Binbin Wu <binbin.wu@linux.intel.com>,
Peter Zijlstra <peterz@infradead.org>,
"Chang S . Bae" <chang.seok.bae@intel.com>,
Kishen Maloor <kishen.maloor@intel.com>,
Sean Christopherson <seanjc@google.com>,
Paolo Bonzini <pbonzini@redhat.com>,
linux-kernel@vger.kernel.org, kvm@vger.kernel.org,
linux-kselftest@vger.kernel.org
Subject: Re: [PATCH v2] selftests/x86: Add a test for LASS enforcement
Date: Tue, 13 Oct 2026 11:54:00 -0700 [thread overview]
Message-ID: <b6cee0b8-c5cf-4136-928f-96cb0a58eaa3@zytor.com> (raw)
In-Reply-To: <20261009020025.4175228-1-sohil.mehta@intel.com>
[-- Attachment #1: Type: text/plain, Size: 692 bytes --]
On 2026-10-08 19:00, Sohil Mehta wrote:
>
> Note, the CPUID bit does not reflect whether the kernel enabled LASS;
> only the cpuinfo flag does. But parsing cpuinfo is cumbersome, and the
> kernel enables LASS whenever the CPU supports it unless the command line
> opts out (vsyscall=emulate or clearcpuid=lass). To keep it simple, rely
> on the CPUID bit and accept a failure in these unusual cases.
>
Cumbersome, really?
Here is a set of parsing functions -- tested, even! -- that you can make
available as library routines. Integrating them into the selftests build
framework has been left as an exercise for the reader ;-)
Signed-off-by: H. Peter Anvin (Rosaic) <hpa@zytor.com>
-hpa
[-- Attachment #2: cpuinfo.c --]
[-- Type: text/x-csrc, Size: 4995 bytes --]
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
static char *read_cpuinfo(void)
{
const size_t bufstep = 64*1024;
size_t bufsize = 0;
size_t len = 0;
ssize_t rlen;
char *buf = NULL;
char *newbuf;
int fd;
int err;
char *p;
fd = open("/proc/cpuinfo", O_RDONLY);
if (fd < 0)
return NULL;
do {
if (len >= bufsize) {
bufsize += bufstep;
newbuf = realloc(buf, bufsize);
if (!newbuf)
goto error;
buf = newbuf;
}
rlen = read(fd, buf + len, bufsize - len);
if (rlen < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
else
goto error;
}
len += rlen;
} while (rlen);
/*
* There should always be a blank line after each CPU,
* including at the end.
*/
if (len < 2 || memcmp(buf + len - 2, "\n\n", 2)) {
errno = EPROTO;
goto error;
}
close(fd);
/*
* buf will have at least one free byte at the end. As a result:
* - Each processor data block is terminated by an empty string.
* - The entire file is terminated by two empty strings in a row.
*/
buf[len] = '\0';
/* Make each line a separate string */
for (p = buf; p < buf + len; p++) {
if (*p == '\n')
*p = '\0';
}
newbuf = realloc(buf, len+1);
if (newbuf)
buf = newbuf;
return buf;
error:
err = errno;
close(fd);
free(buf);
errno = err;
return NULL;
}
const char *get_cpuinfo(void)
{
static char *cpuinfo;
if (unlikely(!cpuinfo))
cpuinfo = read_cpuinfo();
return cpuinfo;
}
/*
* Sets *value to the contents of the field for a specific CPU or NULL.
* Returns 0 on success or:
* -errno - any file I/O error
* -EPROTO - parsing error
* -ESRCH - no such CPU
* -ENOMSG - no such tag
*/
int cpuinfo_get_field(const char **value, const char *field, unsigned long cpu)
{
static const char processor_prefix[] = "processor\t: ";
const size_t pfxlen = sizeof processor_prefix - 1;
size_t fieldlen;
const char *p;
*value = NULL;
p = get_cpuinfo();
if (!p)
return -errno;
while (1) {
/* Blank line at start -> no more processors */
if (!*p)
return -ESRCH;
/* First line must be processor */
if (strncmp(p, processor_prefix, pfxlen))
return -EPROTO;
if (strtoul(p + pfxlen, NULL, 10) == cpu)
break; /* Found our CPU */
/*
* Skip to the next processor.
* Each processor block is terminated by a blank line.
*/
do {
p = strchr(p, '\0') + 1;
} while (*p);
p++; /* Skip the blank line */
}
/* Look for the requested field inside the processor block. */
fieldlen = strlen(field);
while (*p) {
if (!strncmp(p, field, fieldlen)) {
p += fieldlen;
if (!*p)
return -EPROTO; /* No field data present */
if (*p == '\t' || *p == ':') {
/* Field matches */
while (*p == '\t')
p++;
if (*p != ':')
return -EPROTO; /* Colon missing */
p++;
/* Skip space after colon */
if (*p == ' ')
p++;
*value = p;
return 0; /* Field found */
}
}
p = strchr(p, '\0') + 1; /* Next field */
}
return -ENOMSG; /* CPU found but no such field */
}
/*
* Look for a specific flag inside a specific multi-token field;
* this currently applies to the "flags" or "bugs" fields.
*
* Return 1 if the given flag is present, 0 if it is not, or
* a negative error code returned from cpuinfo_get_field().
*/
int cpuinfo_has_flag(const char *flag, const char *field, unsigned long cpu)
{
const char *p;
size_t flaglen;
int err;
err = cpuinfo_get_field(&p, field, cpu);
if (err)
return err;
flaglen = strlen(flag);
while (*p) {
if (!strncmp(p, flag, flaglen) &&
(!p[flaglen] || p[flaglen] == ' '))
return 1; /* Flag found */
/* Skip to the next flag */
p = strchr(p, ' ');
if (!p)
break;
p++;
}
return 0; /* Not found */
}
#ifdef TEST_CPUINFO
#include <stdio.h>
#include <sysexits.h>
int main(int argc, char **argv)
{
const char *field, *flag;
unsigned long cpu;
int rv;
if (argc < 3) {
fprintf(stderr, "Usage: %s cpu field\n", argv[0]);
exit(EX_USAGE);
}
cpu = strtoul(argv[1], NULL, 10);
field = argv[2];
flag = argv[3];
if (flag) {
rv = cpuinfo_has_flag(flag, field, cpu);
if (rv >= 0) {
printf("CPU %lu %s(%s) = %d\n", cpu, field, flag, rv);
exit(rv);
}
} else {
const char *data;
rv = cpuinfo_get_field(&data, field, cpu);
if (!rv) {
printf("CPU %lu %s = %s\n", cpu, field, data);
exit(0);
}
}
/*
* On error. Errors that should never happen no matter what the
* input are sent to stderr.
*/
switch (rv) {
case -ESRCH:
printf("CPU %lu does not exist\n", cpu);
exit(EX_NOUSER);
case -ENOMSG:
printf("CPU %lu %s : field does not exist\n", cpu, field);
exit(EX_NOINPUT);
case -EPROTO:
fprintf(stderr, "%s: could not parse /proc/cpuinfo\n", argv[0]);
exit(EX_PROTOCOL);
default:
fprintf(stderr, "/proc/cpuinfo: %s\n", strerror(-rv));
exit(EX_OSFILE);
}
}
#endif /* TEST_CPUINFO */
next prev parent reply other threads:[~2026-10-13 18:54 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-09 2:00 Sohil Mehta
2026-10-13 18:54 ` H. Peter Anvin [this message]
2026-10-13 19:51 ` Sohil Mehta
2026-10-13 23:06 ` H. Peter Anvin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=b6cee0b8-c5cf-4136-928f-96cb0a58eaa3@zytor.com \
--to=hpa@zytor.com \
--cc=binbin.wu@linux.intel.com \
--cc=bp@alien8.de \
--cc=chang.seok.bae@intel.com \
--cc=dave.hansen@linux.intel.com \
--cc=kishen.maloor@intel.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=pbonzini@redhat.com \
--cc=peterz@infradead.org \
--cc=seanjc@google.com \
--cc=shuah@kernel.org \
--cc=sohil.mehta@intel.com \
--cc=tglx@kernel.org \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®