Skip to content

Commit 630be70

Browse files
committed
Support the umwait instruction.
1 parent 6badd51 commit 630be70

5 files changed

Lines changed: 93 additions & 8 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,6 +1462,7 @@ set(BASIC_TESTS
14621462
tun
14631463
two_signals_with_mask
14641464
ulimit_low
1465+
x86/umwait
14651466
uname
14661467
unexpected_exit
14671468
unexpected_exit_execve

src/record_signal.cc

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ static bool try_handle_trapped_instruction(RecordTask* t, siginfo_t* si) {
8787
case SpecialInstOpcode::X86_RDTSCP:
8888
case SpecialInstOpcode::X86_TPAUSE:
8989
case SpecialInstOpcode::X86_TPAUSE_REX:
90+
case SpecialInstOpcode::X86_UMWAIT:
91+
case SpecialInstOpcode::X86_UMWAIT_REX:
9092
if (t->tsc_mode == PR_TSC_SIGSEGV) {
9193
return false;
9294
}
@@ -139,7 +141,9 @@ static bool try_handle_trapped_instruction(RecordTask* t, siginfo_t* si) {
139141
cpuid_data.edx);
140142
LOG(debug) << " trapped for cpuid: " << HEX(eax) << ":" << HEX(ecx);
141143
} else if (special_instruction.opcode == SpecialInstOpcode::X86_TPAUSE ||
142-
special_instruction.opcode == SpecialInstOpcode::X86_TPAUSE_REX) {
144+
special_instruction.opcode == SpecialInstOpcode::X86_TPAUSE_REX ||
145+
special_instruction.opcode == SpecialInstOpcode::X86_UMWAIT ||
146+
special_instruction.opcode == SpecialInstOpcode::X86_UMWAIT_REX) {
143147
// The Intel SDM states:
144148
//
145149
// > Prior to executing the TPAUSE instruction, an operating system may
@@ -155,16 +159,16 @@ static bool try_handle_trapped_instruction(RecordTask* t, siginfo_t* si) {
155159
// > - IA32_UMWAIT_CONTROL[0] — C0.2 is not allowed by the OS. Value of “1”
156160
// > means all C0.2 requests revert to C0.1.
157161
// >
158-
// > If the processor that executed a TPAUSE instruction wakes due to the
159-
// > expiration of the operating system time-limit, the instructions sets
162+
// > If the processor that executed a TPAUSE/UMWAIT instruction wakes due to
163+
// > the expiration of the operating system time-limit, the instructions sets
160164
// > RFLAGS.CF; otherwise, that flag is cleared.
161165
//
162166
// As the "operating system", we choose a time-limit of zero. The point of
163-
// TPAUSE is to make spin-wait loops more efficient without yielding to the
164-
// operating system scheduler and triggering a full context switch. But
165-
// because we serialize all execution to a single physical core, no progress
166-
// will ever be made without yielding, and of course we've already paid the
167-
// cost of a context switch and then some.
167+
// TPAUSE/UMWAIT is to make spin-wait loops more efficient without yielding
168+
// to the operating system scheduler and triggering a full context switch.
169+
// But because we serialize all execution to a single physical core, no
170+
// progress will ever be made without yielding, and of course we've already
171+
// paid the cost of a context switch and then some.
168172
r.set_flags((r.flags() & ~X86_ALL_ARITH_FLAGS) | X86_CF_FLAG);
169173
}
170174

src/test/x86/umwait.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/* -*- Mode: C; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
2+
3+
#include "util.h"
4+
5+
enum cpuid_requests {
6+
CPUID_GETEXTENDEDFEATURES = 0x07,
7+
};
8+
9+
static void cpuid(int code, int subrequest, unsigned int* a, unsigned int* c,
10+
unsigned int* d) {
11+
asm volatile("cpuid"
12+
: "=a"(*a), "=c"(*c), "=d"(*d)
13+
: "a"(code), "c"(subrequest)
14+
: "ebx");
15+
}
16+
17+
int main(void) {
18+
uint32_t eax, ecx, edx, state = 0;
19+
#ifdef __x86_64__
20+
register uint32_t r15d __asm__("r15") = state;
21+
#endif
22+
uint64_t tsc;
23+
24+
cpuid(CPUID_GETEXTENDEDFEATURES, 0, &eax, &ecx, &edx);
25+
if (!(ecx & (1 << 5))) {
26+
// WAITPKG not present.
27+
atomic_puts("umwait not supported on this system, skipping");
28+
atomic_puts("EXIT-SUCCESS");
29+
return 0;
30+
}
31+
32+
tsc = __rdtsc() + 1000;
33+
eax = tsc;
34+
edx = tsc >> 32;
35+
36+
asm volatile(
37+
".byte 0xf2, 0x0f, 0xae, 0xf7;" // umwait %edi
38+
"lahf;"
39+
: "+a"(eax), "+d"(edx)
40+
: "D"(state)
41+
: "cc", "memory");
42+
43+
// All the arith flags should be cleared except CF, which should be set.
44+
test_assert((uint8_t)(eax >> 8) == 0x3);
45+
46+
#ifdef __x86_64__
47+
tsc = __rdtsc() + 1000;
48+
eax = tsc;
49+
edx = tsc >> 32;
50+
51+
// Test umwait with a REX prefix since the instruction length
52+
// is different.
53+
asm volatile(
54+
".byte 0xf2, 0x41, 0x0f, 0xae, 0xf7;" // umwait %r15d
55+
"lahf;"
56+
: "+a"(eax), "+d"(edx)
57+
: "r"(r15d)
58+
: "cc", "memory");
59+
60+
// All the arith flags should be cleared except CF, which should be set.
61+
test_assert((uint8_t)(eax >> 8) == 0x3);
62+
#endif
63+
64+
atomic_puts("EXIT-SUCCESS");
65+
return 0;
66+
}

src/util.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1927,6 +1927,8 @@ const uint8_t rdtsc_insn[2] = { 0x0f, 0x31 };
19271927
static const uint8_t rdtscp_insn[] = { 0x0f, 0x01, 0xf9 };
19281928
static const uint8_t tpause_insn[] = { 0x66, 0x0f, 0xae, 0xf0 };
19291929
static const uint8_t tpause_rex_insn[] = { 0x66, 0x41, 0x0f, 0xae, 0xf0 };
1930+
static const uint8_t umwait_insn[] = { 0xf2, 0x0f, 0xae, 0xf0 };
1931+
static const uint8_t umwait_rex_insn[] = { 0xf2, 0x41, 0x0f, 0xae, 0xf0 };
19301932
static const uint8_t cpuid_insn[] = { 0x0f, 0xa2 };
19311933
static const uint8_t int3_insn[] = { 0xcc };
19321934
static const uint8_t pushf_insn[] = { 0x9c };
@@ -1973,13 +1975,19 @@ SpecialInst special_instruction_at(Task* t, remote_code_ptr ip) {
19731975
if (!memcmp(insn, tpause_rex_insn, sizeof(tpause_rex_insn))) {
19741976
return {SpecialInstOpcode::X86_TPAUSE_REX};
19751977
}
1978+
if (!memcmp(insn, umwait_rex_insn, sizeof(umwait_rex_insn))) {
1979+
return {SpecialInstOpcode::X86_UMWAIT_REX};
1980+
}
19761981
}
19771982
if (len >= sizeof(tpause_insn)) {
19781983
// Mask off the register selector.
19791984
insn[3] &= ~0x07;
19801985
if (!memcmp(insn, tpause_insn, sizeof(tpause_insn))) {
19811986
return {SpecialInstOpcode::X86_TPAUSE};
19821987
}
1988+
if (!memcmp(insn, umwait_insn, sizeof(umwait_insn))) {
1989+
return {SpecialInstOpcode::X86_UMWAIT};
1990+
}
19831991
}
19841992
} else if (t->arch() == aarch64) {
19851993
uint8_t insn[4];
@@ -2020,6 +2028,10 @@ size_t special_instruction_len(SpecialInstOpcode insn) {
20202028
return sizeof(tpause_insn);
20212029
} else if (insn == SpecialInstOpcode::X86_TPAUSE_REX) {
20222030
return sizeof(tpause_rex_insn);
2031+
} else if (insn == SpecialInstOpcode::X86_UMWAIT) {
2032+
return sizeof(umwait_insn);
2033+
} else if (insn == SpecialInstOpcode::X86_UMWAIT_REX) {
2034+
return sizeof(umwait_rex_insn);
20232035
} else if (insn == SpecialInstOpcode::ARM_MRS_CNTFRQ_EL0 ||
20242036
insn == SpecialInstOpcode::ARM_MRS_CNTVCT_EL0 ||
20252037
insn == SpecialInstOpcode::ARM_MRS_CNTVCTSS_EL0) {

src/util.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,8 @@ enum class SpecialInstOpcode {
495495
X86_PUSHF16,
496496
X86_TPAUSE,
497497
X86_TPAUSE_REX,
498+
X86_UMWAIT,
499+
X86_UMWAIT_REX,
498500
};
499501

500502
struct SpecialInst {

0 commit comments

Comments
 (0)