2a38440bec98daa6922c97c7bee5314d75b69c6a
[dpdk.git] / lib / librte_eal / x86 / rte_power_intrinsics.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2020 Intel Corporation
3  */
4
5 #include "rte_power_intrinsics.h"
6
7 static bool wait_supported;
8
9 static inline uint64_t
10 __get_umwait_val(const volatile void *p, const uint8_t sz)
11 {
12         switch (sz) {
13         case sizeof(uint8_t):
14                 return *(const volatile uint8_t *)p;
15         case sizeof(uint16_t):
16                 return *(const volatile uint16_t *)p;
17         case sizeof(uint32_t):
18                 return *(const volatile uint32_t *)p;
19         case sizeof(uint64_t):
20                 return *(const volatile uint64_t *)p;
21         default:
22                 /* shouldn't happen */
23                 RTE_ASSERT(0);
24                 return 0;
25         }
26 }
27
28 static inline int
29 __check_val_size(const uint8_t sz)
30 {
31         switch (sz) {
32         case sizeof(uint8_t):  /* fall-through */
33         case sizeof(uint16_t): /* fall-through */
34         case sizeof(uint32_t): /* fall-through */
35         case sizeof(uint64_t): /* fall-through */
36                 return 0;
37         default:
38                 /* unexpected size */
39                 return -1;
40         }
41 }
42
43 /**
44  * This function uses UMONITOR/UMWAIT instructions and will enter C0.2 state.
45  * For more information about usage of these instructions, please refer to
46  * Intel(R) 64 and IA-32 Architectures Software Developer's Manual.
47  */
48 int
49 rte_power_monitor(const volatile void *p, const uint64_t expected_value,
50                 const uint64_t value_mask, const uint64_t tsc_timestamp,
51                 const uint8_t data_sz)
52 {
53         const uint32_t tsc_l = (uint32_t)tsc_timestamp;
54         const uint32_t tsc_h = (uint32_t)(tsc_timestamp >> 32);
55
56         /* prevent user from running this instruction if it's not supported */
57         if (!wait_supported)
58                 return -ENOTSUP;
59
60         if (__check_val_size(data_sz) < 0)
61                 return -EINVAL;
62
63         /*
64          * we're using raw byte codes for now as only the newest compiler
65          * versions support this instruction natively.
66          */
67
68         /* set address for UMONITOR */
69         asm volatile(".byte 0xf3, 0x0f, 0xae, 0xf7;"
70                         :
71                         : "D"(p));
72
73         if (value_mask) {
74                 const uint64_t cur_value = __get_umwait_val(p, data_sz);
75                 const uint64_t masked = cur_value & value_mask;
76
77                 /* if the masked value is already matching, abort */
78                 if (masked == expected_value)
79                         return 0;
80         }
81         /* execute UMWAIT */
82         asm volatile(".byte 0xf2, 0x0f, 0xae, 0xf7;"
83                         : /* ignore rflags */
84                         : "D"(0), /* enter C0.2 */
85                           "a"(tsc_l), "d"(tsc_h));
86
87         return 0;
88 }
89
90 /**
91  * This function uses UMONITOR/UMWAIT instructions and will enter C0.2 state.
92  * For more information about usage of these instructions, please refer to
93  * Intel(R) 64 and IA-32 Architectures Software Developer's Manual.
94  */
95 int
96 rte_power_monitor_sync(const volatile void *p, const uint64_t expected_value,
97                 const uint64_t value_mask, const uint64_t tsc_timestamp,
98                 const uint8_t data_sz, rte_spinlock_t *lck)
99 {
100         const uint32_t tsc_l = (uint32_t)tsc_timestamp;
101         const uint32_t tsc_h = (uint32_t)(tsc_timestamp >> 32);
102
103         /* prevent user from running this instruction if it's not supported */
104         if (!wait_supported)
105                 return -ENOTSUP;
106
107         if (__check_val_size(data_sz) < 0)
108                 return -EINVAL;
109
110         /*
111          * we're using raw byte codes for now as only the newest compiler
112          * versions support this instruction natively.
113          */
114
115         /* set address for UMONITOR */
116         asm volatile(".byte 0xf3, 0x0f, 0xae, 0xf7;"
117                         :
118                         : "D"(p));
119
120         if (value_mask) {
121                 const uint64_t cur_value = __get_umwait_val(p, data_sz);
122                 const uint64_t masked = cur_value & value_mask;
123
124                 /* if the masked value is already matching, abort */
125                 if (masked == expected_value)
126                         return 0;
127         }
128         rte_spinlock_unlock(lck);
129
130         /* execute UMWAIT */
131         asm volatile(".byte 0xf2, 0x0f, 0xae, 0xf7;"
132                         : /* ignore rflags */
133                         : "D"(0), /* enter C0.2 */
134                           "a"(tsc_l), "d"(tsc_h));
135
136         rte_spinlock_lock(lck);
137
138         return 0;
139 }
140
141 /**
142  * This function uses TPAUSE instruction  and will enter C0.2 state. For more
143  * information about usage of this instruction, please refer to Intel(R) 64 and
144  * IA-32 Architectures Software Developer's Manual.
145  */
146 int
147 rte_power_pause(const uint64_t tsc_timestamp)
148 {
149         const uint32_t tsc_l = (uint32_t)tsc_timestamp;
150         const uint32_t tsc_h = (uint32_t)(tsc_timestamp >> 32);
151
152         /* prevent user from running this instruction if it's not supported */
153         if (!wait_supported)
154                 return -ENOTSUP;
155
156         /* execute TPAUSE */
157         asm volatile(".byte 0x66, 0x0f, 0xae, 0xf7;"
158                         : /* ignore rflags */
159                         : "D"(0), /* enter C0.2 */
160                         "a"(tsc_l), "d"(tsc_h));
161
162         return 0;
163 }
164
165 RTE_INIT(rte_power_intrinsics_init) {
166         struct rte_cpu_intrinsics i;
167
168         rte_cpu_get_intrinsics_support(&i);
169
170         if (i.power_monitor && i.power_pause)
171                 wait_supported = 1;
172 }