aboutsummaryrefslogtreecommitdiffstats
path: root/tools/testing/selftests/powerpc/stringloops/memcmp.c
blob: cb2f18855c8d87713790c041c4fbcf03a8a82591 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// SPDX-License-Identifier: GPL-2.0
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>

#include "utils.h"

#define SIZE 256
#define ITERATIONS 10000

#define LARGE_SIZE (5 * 1024)
#define LARGE_ITERATIONS 1000
#define LARGE_MAX_OFFSET 32
#define LARGE_SIZE_START 4096

/* This is big enough to fit LARGE_SIZE and works on 4K & 64K kernels */
#define MAP_SIZE (64 * 1024)

#define MAX_OFFSET_DIFF_S1_S2 48

int vmx_count;
int enter_vmx_ops(void)
{
	vmx_count++;
	return 1;
}

void exit_vmx_ops(void)
{
	vmx_count--;
}
int test_memcmp(const void *s1, const void *s2, size_t n);

/* test all offsets and lengths */
static void test_one(char *s1, char *s2, unsigned long max_offset,
		unsigned long size_start, unsigned long max_size)
{
	unsigned long offset, size;

	for (offset = 0; offset < max_offset; offset++) {
		for (size = size_start; size < (max_size - offset); size++) {
			int x, y;
			unsigned long i;

			y = memcmp(s1+offset, s2+offset, size);
			x = test_memcmp(s1+offset, s2+offset, size);

			if (((x ^ y) < 0) &&	/* Trick to compare sign */
				((x | y) != 0)) { /* check for zero */
				printf("memcmp returned %d, should have returned %d (offset %ld size %ld)\n", x, y, offset, size);

				for (i = offset; i < offset+size; i++)
					printf("%02x ", s1[i]);
				printf("\n");

				for (i = offset; i < offset+size; i++)
					printf("%02x ", s2[i]);
				printf("\n");
				abort();
			}

			if (vmx_count != 0) {
				printf("vmx enter/exit not paired.(offset:%ld size:%ld s1:%p s2:%p vc:%d\n",
					offset, size, s1, s2, vmx_count);
				printf("\n");
				abort();
			}
		}
	}
}

static int testcase(bool islarge)
{
	unsigned long i, comp_size, alloc_size;
	char *p, *s1, *s2;
	int iterations;

	comp_size = (islarge ? LARGE_SIZE : SIZE);
	alloc_size = comp_size + MAX_OFFSET_DIFF_S1_S2;
	iterations = islarge ? LARGE_ITERATIONS : ITERATIONS;

	p = mmap(NULL, 4 * MAP_SIZE, PROT_READ | PROT_WRITE,
		 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
	FAIL_IF(p == MAP_FAILED);

	/* Put s1/s2 at the end of a page */
	s1 = p + MAP_SIZE - alloc_size;
	s2 = p + 3 * MAP_SIZE - alloc_size;

	/* And unmap the subsequent page to force a fault if we overread */
	munmap(p + MAP_SIZE, MAP_SIZE);
	munmap(p + 3 * MAP_SIZE, MAP_SIZE);

	srandom(time(0));

	for (i = 0; i < iterations; i++) {
		unsigned long j;
		unsigned long change;
		char *rand_s1 = s1;
		char *rand_s2 = s2;

		for (j = 0; j < alloc_size; j++)
			s1[j] = random();

		rand_s1 += random() % MAX_OFFSET_DIFF_S1_S2;
		rand_s2 += random() % MAX_OFFSET_DIFF_S1_S2;
		memcpy(rand_s2, rand_s1, comp_size);

		/* change one byte */
		change = random() % comp_size;
		rand_s2[change] = random() & 0xff;

		if (islarge)
			test_one(rand_s1, rand_s2, LARGE_MAX_OFFSET,
					LARGE_SIZE_START, comp_size);
		else
			test_one(rand_s1, rand_s2, SIZE, 0, comp_size);
	}

	srandom(time(0));

	for (i = 0; i < iterations; i++) {
		unsigned long j;
		unsigned long change;
		char *rand_s1 = s1;
		char *rand_s2 = s2;

		for (j = 0; j < alloc_size; j++)
			s1[j] = random();

		rand_s1 += random() % MAX_OFFSET_DIFF_S1_S2;
		rand_s2 += random() % MAX_OFFSET_DIFF_S1_S2;
		memcpy(rand_s2, rand_s1, comp_size);

		/* change multiple bytes, 1/8 of total */
		for (j = 0; j < comp_size / 8; j++) {
			change = random() % comp_size;
			s2[change] = random() & 0xff;
		}

		if (islarge)
			test_one(rand_s1, rand_s2, LARGE_MAX_OFFSET,
					LARGE_SIZE_START, comp_size);
		else
			test_one(rand_s1, rand_s2, SIZE, 0, comp_size);
	}

	return 0;
}

static int testcases(void)
{
#ifdef __powerpc64__
	// vcmpequd used in memcmp_64.S is v2.07
	SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07));
#endif

	testcase(0);
	testcase(1);
	return 0;
}

int main(void)
{
	test_harness_set_timeout(300);
	return test_harness(testcases, "memcmp");
}