![]() |
|
|||
0001 // SPDX-License-Identifier: GPL-2.0-only 0002 /* 0003 * Copyright 2017, Gustavo Romero, IBM Corp. 0004 * 0005 * Check if thread endianness is flipped inadvertently to BE on trap 0006 * caught in TM whilst MSR.FP and MSR.VEC are zero (i.e. just after 0007 * load_fp and load_vec overflowed). 0008 * 0009 * The issue can be checked on LE machines simply by zeroing load_fp 0010 * and load_vec and then causing a trap in TM. Since the endianness 0011 * changes to BE on return from the signal handler, 'nop' is 0012 * thread as an illegal instruction in following sequence: 0013 * tbegin. 0014 * beq 1f 0015 * trap 0016 * tend. 0017 * 1: nop 0018 * 0019 * However, although the issue is also present on BE machines, it's a 0020 * bit trickier to check it on BE machines because MSR.LE bit is set 0021 * to zero which determines a BE endianness that is the native 0022 * endianness on BE machines, so nothing notably critical happens, 0023 * i.e. no illegal instruction is observed immediately after returning 0024 * from the signal handler (as it happens on LE machines). Thus to test 0025 * it on BE machines LE endianness is forced after a first trap and then 0026 * the endianness is verified on subsequent traps to determine if the 0027 * endianness "flipped back" to the native endianness (BE). 0028 */ 0029 0030 #define _GNU_SOURCE 0031 #include <error.h> 0032 #include <stdio.h> 0033 #include <stdlib.h> 0034 #include <unistd.h> 0035 #include <htmintrin.h> 0036 #include <inttypes.h> 0037 #include <pthread.h> 0038 #include <sched.h> 0039 #include <signal.h> 0040 #include <stdbool.h> 0041 0042 #include "tm.h" 0043 #include "utils.h" 0044 0045 #define pr_error(error_code, format, ...) \ 0046 error_at_line(1, error_code, __FILE__, __LINE__, format, ##__VA_ARGS__) 0047 0048 #define MSR_LE 1UL 0049 #define LE 1UL 0050 0051 pthread_t t0_ping; 0052 pthread_t t1_pong; 0053 0054 int exit_from_pong; 0055 0056 int trap_event; 0057 int le; 0058 0059 bool success; 0060 0061 void trap_signal_handler(int signo, siginfo_t *si, void *uc) 0062 { 0063 ucontext_t *ucp = uc; 0064 uint64_t thread_endianness; 0065 0066 /* Get thread endianness: extract bit LE from MSR */ 0067 thread_endianness = MSR_LE & ucp->uc_mcontext.gp_regs[PT_MSR]; 0068 0069 /* 0070 * Little-Endian Machine 0071 */ 0072 0073 if (le) { 0074 /* First trap event */ 0075 if (trap_event == 0) { 0076 /* Do nothing. Since it is returning from this trap 0077 * event that endianness is flipped by the bug, so just 0078 * let the process return from the signal handler and 0079 * check on the second trap event if endianness is 0080 * flipped or not. 0081 */ 0082 } 0083 /* Second trap event */ 0084 else if (trap_event == 1) { 0085 /* 0086 * Since trap was caught in TM on first trap event, if 0087 * endianness was still LE (not flipped inadvertently) 0088 * after returning from the signal handler instruction 0089 * (1) is executed (basically a 'nop'), as it's located 0090 * at address of tbegin. +4 (rollback addr). As (1) on 0091 * LE endianness does in effect nothing, instruction (2) 0092 * is then executed again as 'trap', generating a second 0093 * trap event (note that in that case 'trap' is caught 0094 * not in transacional mode). On te other hand, if after 0095 * the return from the signal handler the endianness in- 0096 * advertently flipped, instruction (1) is tread as a 0097 * branch instruction, i.e. b .+8, hence instruction (3) 0098 * and (4) are executed (tbegin.; trap;) and we get sim- 0099 * ilaly on the trap signal handler, but now in TM mode. 0100 * Either way, it's now possible to check the MSR LE bit 0101 * once in the trap handler to verify if endianness was 0102 * flipped or not after the return from the second trap 0103 * event. If endianness is flipped, the bug is present. 0104 * Finally, getting a trap in TM mode or not is just 0105 * worth noting because it affects the math to determine 0106 * the offset added to the NIP on return: the NIP for a 0107 * trap caught in TM is the rollback address, i.e. the 0108 * next instruction after 'tbegin.', whilst the NIP for 0109 * a trap caught in non-transactional mode is the very 0110 * same address of the 'trap' instruction that generated 0111 * the trap event. 0112 */ 0113 0114 if (thread_endianness == LE) { 0115 /* Go to 'success', i.e. instruction (6) */ 0116 ucp->uc_mcontext.gp_regs[PT_NIP] += 16; 0117 } else { 0118 /* 0119 * Thread endianness is BE, so it flipped 0120 * inadvertently. Thus we flip back to LE and 0121 * set NIP to go to 'failure', instruction (5). 0122 */ 0123 ucp->uc_mcontext.gp_regs[PT_MSR] |= 1UL; 0124 ucp->uc_mcontext.gp_regs[PT_NIP] += 4; 0125 } 0126 } 0127 } 0128 0129 /* 0130 * Big-Endian Machine 0131 */ 0132 0133 else { 0134 /* First trap event */ 0135 if (trap_event == 0) { 0136 /* 0137 * Force thread endianness to be LE. Instructions (1), 0138 * (3), and (4) will be executed, generating a second 0139 * trap in TM mode. 0140 */ 0141 ucp->uc_mcontext.gp_regs[PT_MSR] |= 1UL; 0142 } 0143 /* Second trap event */ 0144 else if (trap_event == 1) { 0145 /* 0146 * Do nothing. If bug is present on return from this 0147 * second trap event endianness will flip back "automat- 0148 * ically" to BE, otherwise thread endianness will 0149 * continue to be LE, just as it was set above. 0150 */ 0151 } 0152 /* A third trap event */ 0153 else { 0154 /* 0155 * Once here it means that after returning from the sec- 0156 * ond trap event instruction (4) (trap) was executed 0157 * as LE, generating a third trap event. In that case 0158 * endianness is still LE as set on return from the 0159 * first trap event, hence no bug. Otherwise, bug 0160 * flipped back to BE on return from the second trap 0161 * event and instruction (4) was executed as 'tdi' (so 0162 * basically a 'nop') and branch to 'failure' in 0163 * instruction (5) was taken to indicate failure and we 0164 * never get here. 0165 */ 0166 0167 /* 0168 * Flip back to BE and go to instruction (6), i.e. go to 0169 * 'success'. 0170 */ 0171 ucp->uc_mcontext.gp_regs[PT_MSR] &= ~1UL; 0172 ucp->uc_mcontext.gp_regs[PT_NIP] += 8; 0173 } 0174 } 0175 0176 trap_event++; 0177 } 0178 0179 void usr1_signal_handler(int signo, siginfo_t *si, void *not_used) 0180 { 0181 /* Got a USR1 signal from ping(), so just tell pong() to exit */ 0182 exit_from_pong = 1; 0183 } 0184 0185 void *ping(void *not_used) 0186 { 0187 uint64_t i; 0188 0189 trap_event = 0; 0190 0191 /* 0192 * Wait an amount of context switches so load_fp and load_vec overflows 0193 * and MSR_[FP|VEC|V] is 0. 0194 */ 0195 for (i = 0; i < 1024*1024*512; i++) 0196 ; 0197 0198 asm goto( 0199 /* 0200 * [NA] means "Native Endianness", i.e. it tells how a 0201 * instruction is executed on machine's native endianness (in 0202 * other words, native endianness matches kernel endianness). 0203 * [OP] means "Opposite Endianness", i.e. on a BE machine, it 0204 * tells how a instruction is executed as a LE instruction; con- 0205 * versely, on a LE machine, it tells how a instruction is 0206 * executed as a BE instruction. When [NA] is omitted, it means 0207 * that the native interpretation of a given instruction is not 0208 * relevant for the test. Likewise when [OP] is omitted. 0209 */ 0210 0211 " tbegin. ;" /* (0) tbegin. [NA] */ 0212 " tdi 0, 0, 0x48;" /* (1) nop [NA]; b (3) [OP] */ 0213 " trap ;" /* (2) trap [NA] */ 0214 ".long 0x1D05007C;" /* (3) tbegin. [OP] */ 0215 ".long 0x0800E07F;" /* (4) trap [OP]; nop [NA] */ 0216 " b %l[failure] ;" /* (5) b [NA]; MSR.LE flipped (bug) */ 0217 " b %l[success] ;" /* (6) b [NA]; MSR.LE did not flip (ok)*/ 0218 0219 : : : : failure, success); 0220 0221 failure: 0222 success = false; 0223 goto exit_from_ping; 0224 0225 success: 0226 success = true; 0227 0228 exit_from_ping: 0229 /* Tell pong() to exit before leaving */ 0230 pthread_kill(t1_pong, SIGUSR1); 0231 return NULL; 0232 } 0233 0234 void *pong(void *not_used) 0235 { 0236 while (!exit_from_pong) 0237 /* 0238 * Induce context switches on ping() thread 0239 * until ping() finishes its job and signs 0240 * to exit from this loop. 0241 */ 0242 sched_yield(); 0243 0244 return NULL; 0245 } 0246 0247 int tm_trap_test(void) 0248 { 0249 uint16_t k = 1; 0250 int cpu, rc; 0251 0252 pthread_attr_t attr; 0253 cpu_set_t cpuset; 0254 0255 struct sigaction trap_sa; 0256 0257 SKIP_IF(!have_htm()); 0258 SKIP_IF(htm_is_synthetic()); 0259 0260 trap_sa.sa_flags = SA_SIGINFO; 0261 trap_sa.sa_sigaction = trap_signal_handler; 0262 sigaction(SIGTRAP, &trap_sa, NULL); 0263 0264 struct sigaction usr1_sa; 0265 0266 usr1_sa.sa_flags = SA_SIGINFO; 0267 usr1_sa.sa_sigaction = usr1_signal_handler; 0268 sigaction(SIGUSR1, &usr1_sa, NULL); 0269 0270 cpu = pick_online_cpu(); 0271 FAIL_IF(cpu < 0); 0272 0273 // Set only one CPU in the mask. Both threads will be bound to that CPU. 0274 CPU_ZERO(&cpuset); 0275 CPU_SET(cpu, &cpuset); 0276 0277 /* Init pthread attribute */ 0278 rc = pthread_attr_init(&attr); 0279 if (rc) 0280 pr_error(rc, "pthread_attr_init()"); 0281 0282 /* 0283 * Bind thread ping() and pong() both to CPU 0 so they ping-pong and 0284 * speed up context switches on ping() thread, speeding up the load_fp 0285 * and load_vec overflow. 0286 */ 0287 rc = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset); 0288 if (rc) 0289 pr_error(rc, "pthread_attr_setaffinity()"); 0290 0291 /* Figure out the machine endianness */ 0292 le = (int) *(uint8_t *)&k; 0293 0294 printf("%s machine detected. Checking if endianness flips %s", 0295 le ? "Little-Endian" : "Big-Endian", 0296 "inadvertently on trap in TM... "); 0297 0298 rc = fflush(0); 0299 if (rc) 0300 pr_error(rc, "fflush()"); 0301 0302 /* Launch ping() */ 0303 rc = pthread_create(&t0_ping, &attr, ping, NULL); 0304 if (rc) 0305 pr_error(rc, "pthread_create()"); 0306 0307 exit_from_pong = 0; 0308 0309 /* Launch pong() */ 0310 rc = pthread_create(&t1_pong, &attr, pong, NULL); 0311 if (rc) 0312 pr_error(rc, "pthread_create()"); 0313 0314 rc = pthread_join(t0_ping, NULL); 0315 if (rc) 0316 pr_error(rc, "pthread_join()"); 0317 0318 rc = pthread_join(t1_pong, NULL); 0319 if (rc) 0320 pr_error(rc, "pthread_join()"); 0321 0322 if (success) { 0323 printf("no.\n"); /* no, endianness did not flip inadvertently */ 0324 return EXIT_SUCCESS; 0325 } 0326 0327 printf("yes!\n"); /* yes, endianness did flip inadvertently */ 0328 return EXIT_FAILURE; 0329 } 0330 0331 int main(int argc, char **argv) 0332 { 0333 return test_harness(tm_trap_test, "tm_trap_test"); 0334 }
[ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
This page was automatically generated by the 2.1.0 LXR engine. The LXR team |
![]() ![]() |