cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

tlbie_test.c (20241B)


      1// SPDX-License-Identifier: GPL-2.0
      2
      3/*
      4 * Copyright 2019, Nick Piggin, Gautham R. Shenoy, Aneesh Kumar K.V, IBM Corp.
      5 */
      6
      7/*
      8 *
      9 * Test tlbie/mtpidr race. We have 4 threads doing flush/load/compare/store
     10 * sequence in a loop. The same threads also rung a context switch task
     11 * that does sched_yield() in loop.
     12 *
     13 * The snapshot thread mark the mmap area PROT_READ in between, make a copy
     14 * and copy it back to the original area. This helps us to detect if any
     15 * store continued to happen after we marked the memory PROT_READ.
     16 */
     17
     18#define _GNU_SOURCE
     19#include <stdio.h>
     20#include <sys/mman.h>
     21#include <sys/types.h>
     22#include <sys/wait.h>
     23#include <sys/ipc.h>
     24#include <sys/shm.h>
     25#include <sys/stat.h>
     26#include <sys/time.h>
     27#include <linux/futex.h>
     28#include <unistd.h>
     29#include <asm/unistd.h>
     30#include <string.h>
     31#include <stdlib.h>
     32#include <fcntl.h>
     33#include <sched.h>
     34#include <time.h>
     35#include <stdarg.h>
     36#include <pthread.h>
     37#include <signal.h>
     38#include <sys/prctl.h>
     39
     40static inline void dcbf(volatile unsigned int *addr)
     41{
     42	__asm__ __volatile__ ("dcbf %y0; sync" : : "Z"(*(unsigned char *)addr) : "memory");
     43}
     44
     45static void err_msg(char *msg)
     46{
     47
     48	time_t now;
     49	time(&now);
     50	printf("=================================\n");
     51	printf("    Error: %s\n", msg);
     52	printf("    %s", ctime(&now));
     53	printf("=================================\n");
     54	exit(1);
     55}
     56
     57static char *map1;
     58static char *map2;
     59static pid_t rim_process_pid;
     60
     61/*
     62 * A "rim-sequence" is defined to be the sequence of the following
     63 * operations performed on a memory word:
     64 *	1) FLUSH the contents of that word.
     65 *	2) LOAD the contents of that word.
     66 *	3) COMPARE the contents of that word with the content that was
     67 *	           previously stored at that word
     68 *	4) STORE new content into that word.
     69 *
     70 * The threads in this test that perform the rim-sequence are termed
     71 * as rim_threads.
     72 */
     73
     74/*
     75 * A "corruption" is defined to be the failed COMPARE operation in a
     76 * rim-sequence.
     77 *
     78 * A rim_thread that detects a corruption informs about it to all the
     79 * other rim_threads, and the mem_snapshot thread.
     80 */
     81static volatile unsigned int corruption_found;
     82
     83/*
     84 * This defines the maximum number of rim_threads in this test.
     85 *
     86 * The THREAD_ID_BITS denote the number of bits required
     87 * to represent the thread_ids [0..MAX_THREADS - 1].
     88 * We are being a bit paranoid here and set it to 8 bits,
     89 * though 6 bits suffice.
     90 *
     91 */
     92#define MAX_THREADS 		64
     93#define THREAD_ID_BITS		8
     94#define THREAD_ID_MASK		((1 << THREAD_ID_BITS) - 1)
     95static unsigned int rim_thread_ids[MAX_THREADS];
     96static pthread_t rim_threads[MAX_THREADS];
     97
     98
     99/*
    100 * Each rim_thread works on an exclusive "chunk" of size
    101 * RIM_CHUNK_SIZE.
    102 *
    103 * The ith rim_thread works on the ith chunk.
    104 *
    105 * The ith chunk begins at
    106 * map1 + (i * RIM_CHUNK_SIZE)
    107 */
    108#define RIM_CHUNK_SIZE  	1024
    109#define BITS_PER_BYTE 		8
    110#define WORD_SIZE     		(sizeof(unsigned int))
    111#define WORD_BITS		(WORD_SIZE * BITS_PER_BYTE)
    112#define WORDS_PER_CHUNK		(RIM_CHUNK_SIZE/WORD_SIZE)
    113
    114static inline char *compute_chunk_start_addr(unsigned int thread_id)
    115{
    116	char *chunk_start;
    117
    118	chunk_start = (char *)((unsigned long)map1 +
    119			       (thread_id * RIM_CHUNK_SIZE));
    120
    121	return chunk_start;
    122}
    123
    124/*
    125 * The "word-offset" of a word-aligned address inside a chunk, is
    126 * defined to be the number of words that precede the address in that
    127 * chunk.
    128 *
    129 * WORD_OFFSET_BITS denote the number of bits required to represent
    130 * the word-offsets of all the word-aligned addresses of a chunk.
    131 */
    132#define WORD_OFFSET_BITS	(__builtin_ctz(WORDS_PER_CHUNK))
    133#define WORD_OFFSET_MASK	((1 << WORD_OFFSET_BITS) - 1)
    134
    135static inline unsigned int compute_word_offset(char *start, unsigned int *addr)
    136{
    137	unsigned int delta_bytes, ret;
    138	delta_bytes = (unsigned long)addr - (unsigned long)start;
    139
    140	ret = delta_bytes/WORD_SIZE;
    141
    142	return ret;
    143}
    144
    145/*
    146 * A "sweep" is defined to be the sequential execution of the
    147 * rim-sequence by a rim_thread on its chunk one word at a time,
    148 * starting from the first word of its chunk and ending with the last
    149 * word of its chunk.
    150 *
    151 * Each sweep of a rim_thread is uniquely identified by a sweep_id.
    152 * SWEEP_ID_BITS denote the number of bits required to represent
    153 * the sweep_ids of rim_threads.
    154 *
    155 * As to why SWEEP_ID_BITS are computed as a function of THREAD_ID_BITS,
    156 * WORD_OFFSET_BITS, and WORD_BITS, see the "store-pattern" below.
    157 */
    158#define SWEEP_ID_BITS		(WORD_BITS - (THREAD_ID_BITS + WORD_OFFSET_BITS))
    159#define SWEEP_ID_MASK		((1 << SWEEP_ID_BITS) - 1)
    160
    161/*
    162 * A "store-pattern" is the word-pattern that is stored into a word
    163 * location in the 4)STORE step of the rim-sequence.
    164 *
    165 * In the store-pattern, we shall encode:
    166 *
    167 *      - The thread-id of the rim_thread performing the store
    168 *        (The most significant THREAD_ID_BITS)
    169 *
    170 *      - The word-offset of the address into which the store is being
    171 *        performed (The next WORD_OFFSET_BITS)
    172 *
    173 *      - The sweep_id of the current sweep in which the store is
    174 *        being performed. (The lower SWEEP_ID_BITS)
    175 *
    176 * Store Pattern: 32 bits
    177 * |------------------|--------------------|---------------------------------|
    178 * |    Thread id     |  Word offset       |         sweep_id                |
    179 * |------------------|--------------------|---------------------------------|
    180 *    THREAD_ID_BITS     WORD_OFFSET_BITS          SWEEP_ID_BITS
    181 *
    182 * In the store pattern, the (Thread-id + Word-offset) uniquely identify the
    183 * address to which the store is being performed i.e,
    184 *    address == map1 +
    185 *              (Thread-id * RIM_CHUNK_SIZE) + (Word-offset * WORD_SIZE)
    186 *
    187 * And the sweep_id in the store pattern identifies the time when the
    188 * store was performed by the rim_thread.
    189 *
    190 * We shall use this property in the 3)COMPARE step of the
    191 * rim-sequence.
    192 */
    193#define SWEEP_ID_SHIFT	0
    194#define WORD_OFFSET_SHIFT	(SWEEP_ID_BITS)
    195#define THREAD_ID_SHIFT		(WORD_OFFSET_BITS + SWEEP_ID_BITS)
    196
    197/*
    198 * Compute the store pattern for a given thread with id @tid, at
    199 * location @addr in the sweep identified by @sweep_id
    200 */
    201static inline unsigned int compute_store_pattern(unsigned int tid,
    202						 unsigned int *addr,
    203						 unsigned int sweep_id)
    204{
    205	unsigned int ret = 0;
    206	char *start = compute_chunk_start_addr(tid);
    207	unsigned int word_offset = compute_word_offset(start, addr);
    208
    209	ret += (tid & THREAD_ID_MASK) << THREAD_ID_SHIFT;
    210	ret += (word_offset & WORD_OFFSET_MASK) << WORD_OFFSET_SHIFT;
    211	ret += (sweep_id & SWEEP_ID_MASK) << SWEEP_ID_SHIFT;
    212	return ret;
    213}
    214
    215/* Extract the thread-id from the given store-pattern */
    216static inline unsigned int extract_tid(unsigned int pattern)
    217{
    218	unsigned int ret;
    219
    220	ret = (pattern >> THREAD_ID_SHIFT) & THREAD_ID_MASK;
    221	return ret;
    222}
    223
    224/* Extract the word-offset from the given store-pattern */
    225static inline unsigned int extract_word_offset(unsigned int pattern)
    226{
    227	unsigned int ret;
    228
    229	ret = (pattern >> WORD_OFFSET_SHIFT) & WORD_OFFSET_MASK;
    230
    231	return ret;
    232}
    233
    234/* Extract the sweep-id from the given store-pattern */
    235static inline unsigned int extract_sweep_id(unsigned int pattern)
    236
    237{
    238	unsigned int ret;
    239
    240	ret = (pattern >> SWEEP_ID_SHIFT) & SWEEP_ID_MASK;
    241
    242	return ret;
    243}
    244
    245/************************************************************
    246 *                                                          *
    247 *          Logging the output of the verification          *
    248 *                                                          *
    249 ************************************************************/
    250#define LOGDIR_NAME_SIZE 100
    251static char logdir[LOGDIR_NAME_SIZE];
    252
    253static FILE *fp[MAX_THREADS];
    254static const char logfilename[] ="Thread-%02d-Chunk";
    255
    256static inline void start_verification_log(unsigned int tid,
    257					  unsigned int *addr,
    258					  unsigned int cur_sweep_id,
    259					  unsigned int prev_sweep_id)
    260{
    261	FILE *f;
    262	char logfile[30];
    263	char path[LOGDIR_NAME_SIZE + 30];
    264	char separator[2] = "/";
    265	char *chunk_start = compute_chunk_start_addr(tid);
    266	unsigned int size = RIM_CHUNK_SIZE;
    267
    268	sprintf(logfile, logfilename, tid);
    269	strcpy(path, logdir);
    270	strcat(path, separator);
    271	strcat(path, logfile);
    272	f = fopen(path, "w");
    273
    274	if (!f) {
    275		err_msg("Unable to create logfile\n");
    276	}
    277
    278	fp[tid] = f;
    279
    280	fprintf(f, "----------------------------------------------------------\n");
    281	fprintf(f, "PID                = %d\n", rim_process_pid);
    282	fprintf(f, "Thread id          = %02d\n", tid);
    283	fprintf(f, "Chunk Start Addr   = 0x%016lx\n", (unsigned long)chunk_start);
    284	fprintf(f, "Chunk Size         = %d\n", size);
    285	fprintf(f, "Next Store Addr    = 0x%016lx\n", (unsigned long)addr);
    286	fprintf(f, "Current sweep-id   = 0x%08x\n", cur_sweep_id);
    287	fprintf(f, "Previous sweep-id  = 0x%08x\n", prev_sweep_id);
    288	fprintf(f, "----------------------------------------------------------\n");
    289}
    290
    291static inline void log_anamoly(unsigned int tid, unsigned int *addr,
    292			       unsigned int expected, unsigned int observed)
    293{
    294	FILE *f = fp[tid];
    295
    296	fprintf(f, "Thread %02d: Addr 0x%lx: Expected 0x%x, Observed 0x%x\n",
    297	        tid, (unsigned long)addr, expected, observed);
    298	fprintf(f, "Thread %02d: Expected Thread id   = %02d\n", tid, extract_tid(expected));
    299	fprintf(f, "Thread %02d: Observed Thread id   = %02d\n", tid, extract_tid(observed));
    300	fprintf(f, "Thread %02d: Expected Word offset = %03d\n", tid, extract_word_offset(expected));
    301	fprintf(f, "Thread %02d: Observed Word offset = %03d\n", tid, extract_word_offset(observed));
    302	fprintf(f, "Thread %02d: Expected sweep-id    = 0x%x\n", tid, extract_sweep_id(expected));
    303	fprintf(f, "Thread %02d: Observed sweep-id    = 0x%x\n", tid, extract_sweep_id(observed));
    304	fprintf(f, "----------------------------------------------------------\n");
    305}
    306
    307static inline void end_verification_log(unsigned int tid, unsigned nr_anamolies)
    308{
    309	FILE *f = fp[tid];
    310	char logfile[30];
    311	char path[LOGDIR_NAME_SIZE + 30];
    312	char separator[] = "/";
    313
    314	fclose(f);
    315
    316	if (nr_anamolies == 0) {
    317		remove(path);
    318		return;
    319	}
    320
    321	sprintf(logfile, logfilename, tid);
    322	strcpy(path, logdir);
    323	strcat(path, separator);
    324	strcat(path, logfile);
    325
    326	printf("Thread %02d chunk has %d corrupted words. For details check %s\n",
    327		tid, nr_anamolies, path);
    328}
    329
    330/*
    331 * When a COMPARE step of a rim-sequence fails, the rim_thread informs
    332 * everyone else via the shared_memory pointed to by
    333 * corruption_found variable. On seeing this, every thread verifies the
    334 * content of its chunk as follows.
    335 *
    336 * Suppose a thread identified with @tid was about to store (but not
    337 * yet stored) to @next_store_addr in its current sweep identified
    338 * @cur_sweep_id. Let @prev_sweep_id indicate the previous sweep_id.
    339 *
    340 * This implies that for all the addresses @addr < @next_store_addr,
    341 * Thread @tid has already performed a store as part of its current
    342 * sweep. Hence we expect the content of such @addr to be:
    343 *    |-------------------------------------------------|
    344 *    | tid   | word_offset(addr) |    cur_sweep_id     |
    345 *    |-------------------------------------------------|
    346 *
    347 * Since Thread @tid is yet to perform stores on address
    348 * @next_store_addr and above, we expect the content of such an
    349 * address @addr to be:
    350 *    |-------------------------------------------------|
    351 *    | tid   | word_offset(addr) |    prev_sweep_id    |
    352 *    |-------------------------------------------------|
    353 *
    354 * The verifier function @verify_chunk does this verification and logs
    355 * any anamolies that it finds.
    356 */
    357static void verify_chunk(unsigned int tid, unsigned int *next_store_addr,
    358		  unsigned int cur_sweep_id,
    359		  unsigned int prev_sweep_id)
    360{
    361	unsigned int *iter_ptr;
    362	unsigned int size = RIM_CHUNK_SIZE;
    363	unsigned int expected;
    364	unsigned int observed;
    365	char *chunk_start = compute_chunk_start_addr(tid);
    366
    367	int nr_anamolies = 0;
    368
    369	start_verification_log(tid, next_store_addr,
    370			       cur_sweep_id, prev_sweep_id);
    371
    372	for (iter_ptr = (unsigned int *)chunk_start;
    373	     (unsigned long)iter_ptr < (unsigned long)chunk_start + size;
    374	     iter_ptr++) {
    375		unsigned int expected_sweep_id;
    376
    377		if (iter_ptr < next_store_addr) {
    378			expected_sweep_id = cur_sweep_id;
    379		} else {
    380			expected_sweep_id = prev_sweep_id;
    381		}
    382
    383		expected = compute_store_pattern(tid, iter_ptr, expected_sweep_id);
    384
    385		dcbf((volatile unsigned int*)iter_ptr); //Flush before reading
    386		observed = *iter_ptr;
    387
    388	        if (observed != expected) {
    389			nr_anamolies++;
    390			log_anamoly(tid, iter_ptr, expected, observed);
    391		}
    392	}
    393
    394	end_verification_log(tid, nr_anamolies);
    395}
    396
    397static void set_pthread_cpu(pthread_t th, int cpu)
    398{
    399	cpu_set_t run_cpu_mask;
    400	struct sched_param param;
    401
    402	CPU_ZERO(&run_cpu_mask);
    403	CPU_SET(cpu, &run_cpu_mask);
    404	pthread_setaffinity_np(th, sizeof(cpu_set_t), &run_cpu_mask);
    405
    406	param.sched_priority = 1;
    407	if (0 && sched_setscheduler(0, SCHED_FIFO, &param) == -1) {
    408		/* haven't reproduced with this setting, it kills random preemption which may be a factor */
    409		fprintf(stderr, "could not set SCHED_FIFO, run as root?\n");
    410	}
    411}
    412
    413static void set_mycpu(int cpu)
    414{
    415	cpu_set_t run_cpu_mask;
    416	struct sched_param param;
    417
    418	CPU_ZERO(&run_cpu_mask);
    419	CPU_SET(cpu, &run_cpu_mask);
    420	sched_setaffinity(0, sizeof(cpu_set_t), &run_cpu_mask);
    421
    422	param.sched_priority = 1;
    423	if (0 && sched_setscheduler(0, SCHED_FIFO, &param) == -1) {
    424		fprintf(stderr, "could not set SCHED_FIFO, run as root?\n");
    425	}
    426}
    427
    428static volatile int segv_wait;
    429
    430static void segv_handler(int signo, siginfo_t *info, void *extra)
    431{
    432	while (segv_wait) {
    433		sched_yield();
    434	}
    435
    436}
    437
    438static void set_segv_handler(void)
    439{
    440	struct sigaction sa;
    441
    442	sa.sa_flags = SA_SIGINFO;
    443	sa.sa_sigaction = segv_handler;
    444
    445	if (sigaction(SIGSEGV, &sa, NULL) == -1) {
    446		perror("sigaction");
    447		exit(EXIT_FAILURE);
    448	}
    449}
    450
    451int timeout = 0;
    452/*
    453 * This function is executed by every rim_thread.
    454 *
    455 * This function performs sweeps over the exclusive chunks of the
    456 * rim_threads executing the rim-sequence one word at a time.
    457 */
    458static void *rim_fn(void *arg)
    459{
    460	unsigned int tid = *((unsigned int *)arg);
    461
    462	int size = RIM_CHUNK_SIZE;
    463	char *chunk_start = compute_chunk_start_addr(tid);
    464
    465	unsigned int prev_sweep_id;
    466	unsigned int cur_sweep_id = 0;
    467
    468	/* word access */
    469	unsigned int pattern = cur_sweep_id;
    470	unsigned int *pattern_ptr = &pattern;
    471	unsigned int *w_ptr, read_data;
    472
    473	set_segv_handler();
    474
    475	/*
    476	 * Let us initialize the chunk:
    477	 *
    478	 * Each word-aligned address addr in the chunk,
    479	 * is initialized to :
    480	 *    |-------------------------------------------------|
    481	 *    | tid   | word_offset(addr) |         0           |
    482	 *    |-------------------------------------------------|
    483	 */
    484	for (w_ptr = (unsigned int *)chunk_start;
    485	     (unsigned long)w_ptr < (unsigned long)(chunk_start) + size;
    486	     w_ptr++) {
    487
    488		*pattern_ptr = compute_store_pattern(tid, w_ptr, cur_sweep_id);
    489		*w_ptr = *pattern_ptr;
    490	}
    491
    492	while (!corruption_found && !timeout) {
    493		prev_sweep_id = cur_sweep_id;
    494		cur_sweep_id = cur_sweep_id + 1;
    495
    496		for (w_ptr = (unsigned int *)chunk_start;
    497		     (unsigned long)w_ptr < (unsigned long)(chunk_start) + size;
    498		     w_ptr++)  {
    499			unsigned int old_pattern;
    500
    501			/*
    502			 * Compute the pattern that we would have
    503			 * stored at this location in the previous
    504			 * sweep.
    505			 */
    506			old_pattern = compute_store_pattern(tid, w_ptr, prev_sweep_id);
    507
    508			/*
    509			 * FLUSH:Ensure that we flush the contents of
    510			 *       the cache before loading
    511			 */
    512			dcbf((volatile unsigned int*)w_ptr); //Flush
    513
    514			/* LOAD: Read the value */
    515			read_data = *w_ptr; //Load
    516
    517			/*
    518			 * COMPARE: Is it the same as what we had stored
    519			 *          in the previous sweep ? It better be!
    520			 */
    521			if (read_data != old_pattern) {
    522				/* No it isn't! Tell everyone */
    523				corruption_found = 1;
    524			}
    525
    526			/*
    527			 * Before performing a store, let us check if
    528			 * any rim_thread has found a corruption.
    529			 */
    530			if (corruption_found || timeout) {
    531				/*
    532				 * Yes. Someone (including us!) has found
    533				 * a corruption :(
    534				 *
    535				 * Let us verify that our chunk is
    536				 * correct.
    537				 */
    538				/* But first, let us allow the dust to settle down! */
    539				verify_chunk(tid, w_ptr, cur_sweep_id, prev_sweep_id);
    540
    541				return 0;
    542			}
    543
    544			/*
    545			 * Compute the new pattern that we are going
    546			 * to write to this location
    547			 */
    548			*pattern_ptr = compute_store_pattern(tid, w_ptr, cur_sweep_id);
    549
    550			/*
    551			 * STORE: Now let us write this pattern into
    552			 *        the location
    553			 */
    554			*w_ptr = *pattern_ptr;
    555		}
    556	}
    557
    558	return NULL;
    559}
    560
    561
    562static unsigned long start_cpu = 0;
    563static unsigned long nrthreads = 4;
    564
    565static pthread_t mem_snapshot_thread;
    566
    567static void *mem_snapshot_fn(void *arg)
    568{
    569	int page_size = getpagesize();
    570	size_t size = page_size;
    571	void *tmp = malloc(size);
    572
    573	while (!corruption_found && !timeout) {
    574		/* Stop memory migration once corruption is found */
    575		segv_wait = 1;
    576
    577		mprotect(map1, size, PROT_READ);
    578
    579		/*
    580		 * Load from the working alias (map1). Loading from map2
    581		 * also fails.
    582		 */
    583		memcpy(tmp, map1, size);
    584
    585		/*
    586		 * Stores must go via map2 which has write permissions, but
    587		 * the corrupted data tends to be seen in the snapshot buffer,
    588		 * so corruption does not appear to be introduced at the
    589		 * copy-back via map2 alias here.
    590		 */
    591		memcpy(map2, tmp, size);
    592		/*
    593		 * Before releasing other threads, must ensure the copy
    594		 * back to
    595		 */
    596		asm volatile("sync" ::: "memory");
    597		mprotect(map1, size, PROT_READ|PROT_WRITE);
    598		asm volatile("sync" ::: "memory");
    599		segv_wait = 0;
    600
    601		usleep(1); /* This value makes a big difference */
    602	}
    603
    604	return 0;
    605}
    606
    607void alrm_sighandler(int sig)
    608{
    609	timeout = 1;
    610}
    611
    612int main(int argc, char *argv[])
    613{
    614	int c;
    615	int page_size = getpagesize();
    616	time_t now;
    617	int i, dir_error;
    618	pthread_attr_t attr;
    619	key_t shm_key = (key_t) getpid();
    620	int shmid, run_time = 20 * 60;
    621	struct sigaction sa_alrm;
    622
    623	snprintf(logdir, LOGDIR_NAME_SIZE,
    624		 "/tmp/logdir-%u", (unsigned int)getpid());
    625	while ((c = getopt(argc, argv, "r:hn:l:t:")) != -1) {
    626		switch(c) {
    627		case 'r':
    628			start_cpu = strtoul(optarg, NULL, 10);
    629			break;
    630		case 'h':
    631			printf("%s [-r <start_cpu>] [-n <nrthreads>] [-l <logdir>] [-t <timeout>]\n", argv[0]);
    632			exit(0);
    633			break;
    634		case 'n':
    635			nrthreads = strtoul(optarg, NULL, 10);
    636			break;
    637		case 'l':
    638			strncpy(logdir, optarg, LOGDIR_NAME_SIZE - 1);
    639			break;
    640		case 't':
    641			run_time = strtoul(optarg, NULL, 10);
    642			break;
    643		default:
    644			printf("invalid option\n");
    645			exit(0);
    646			break;
    647		}
    648	}
    649
    650	if (nrthreads > MAX_THREADS)
    651		nrthreads = MAX_THREADS;
    652
    653	shmid = shmget(shm_key, page_size, IPC_CREAT|0666);
    654	if (shmid < 0) {
    655		err_msg("Failed shmget\n");
    656	}
    657
    658	map1 = shmat(shmid, NULL, 0);
    659	if (map1 == (void *) -1) {
    660		err_msg("Failed shmat");
    661	}
    662
    663	map2 = shmat(shmid, NULL, 0);
    664	if (map2 == (void *) -1) {
    665		err_msg("Failed shmat");
    666	}
    667
    668	dir_error = mkdir(logdir, 0755);
    669
    670	if (dir_error) {
    671		err_msg("Failed mkdir");
    672	}
    673
    674	printf("start_cpu list:%lu\n", start_cpu);
    675	printf("number of worker threads:%lu + 1 snapshot thread\n", nrthreads);
    676	printf("Allocated address:0x%016lx + secondary map:0x%016lx\n", (unsigned long)map1, (unsigned long)map2);
    677	printf("logdir at : %s\n", logdir);
    678	printf("Timeout: %d seconds\n", run_time);
    679
    680	time(&now);
    681	printf("=================================\n");
    682	printf("     Starting Test\n");
    683	printf("     %s", ctime(&now));
    684	printf("=================================\n");
    685
    686	for (i = 0; i < nrthreads; i++) {
    687		if (1 && !fork()) {
    688			prctl(PR_SET_PDEATHSIG, SIGKILL);
    689			set_mycpu(start_cpu + i);
    690			for (;;)
    691				sched_yield();
    692			exit(0);
    693		}
    694	}
    695
    696
    697	sa_alrm.sa_handler = &alrm_sighandler;
    698	sigemptyset(&sa_alrm.sa_mask);
    699	sa_alrm.sa_flags = 0;
    700
    701	if (sigaction(SIGALRM, &sa_alrm, 0) == -1) {
    702		err_msg("Failed signal handler registration\n");
    703	}
    704
    705	alarm(run_time);
    706
    707	pthread_attr_init(&attr);
    708	for (i = 0; i < nrthreads; i++) {
    709		rim_thread_ids[i] = i;
    710		pthread_create(&rim_threads[i], &attr, rim_fn, &rim_thread_ids[i]);
    711		set_pthread_cpu(rim_threads[i], start_cpu + i);
    712	}
    713
    714	pthread_create(&mem_snapshot_thread, &attr, mem_snapshot_fn, map1);
    715	set_pthread_cpu(mem_snapshot_thread, start_cpu + i);
    716
    717
    718	pthread_join(mem_snapshot_thread, NULL);
    719	for (i = 0; i < nrthreads; i++) {
    720		pthread_join(rim_threads[i], NULL);
    721	}
    722
    723	if (!timeout) {
    724		time(&now);
    725		printf("=================================\n");
    726		printf("      Data Corruption Detected\n");
    727		printf("      %s", ctime(&now));
    728		printf("      See logfiles in %s\n", logdir);
    729		printf("=================================\n");
    730		return 1;
    731	}
    732	return 0;
    733}