162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <malloc.h>
362306a36Sopenharmony_ci#include <stdlib.h>
462306a36Sopenharmony_ci#include <string.h>
562306a36Sopenharmony_ci#include <sys/mman.h>
662306a36Sopenharmony_ci#include <time.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "utils.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define SIZE 256
1162306a36Sopenharmony_ci#define ITERATIONS 10000
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#define LARGE_SIZE (5 * 1024)
1462306a36Sopenharmony_ci#define LARGE_ITERATIONS 1000
1562306a36Sopenharmony_ci#define LARGE_MAX_OFFSET 32
1662306a36Sopenharmony_ci#define LARGE_SIZE_START 4096
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/* This is big enough to fit LARGE_SIZE and works on 4K & 64K kernels */
1962306a36Sopenharmony_ci#define MAP_SIZE (64 * 1024)
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define MAX_OFFSET_DIFF_S1_S2 48
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ciint vmx_count;
2462306a36Sopenharmony_ciint enter_vmx_ops(void)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	vmx_count++;
2762306a36Sopenharmony_ci	return 1;
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_civoid exit_vmx_ops(void)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	vmx_count--;
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ciint test_memcmp(const void *s1, const void *s2, size_t n);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/* test all offsets and lengths */
3762306a36Sopenharmony_cistatic void test_one(char *s1, char *s2, unsigned long max_offset,
3862306a36Sopenharmony_ci		unsigned long size_start, unsigned long max_size)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	unsigned long offset, size;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	for (offset = 0; offset < max_offset; offset++) {
4362306a36Sopenharmony_ci		for (size = size_start; size < (max_size - offset); size++) {
4462306a36Sopenharmony_ci			int x, y;
4562306a36Sopenharmony_ci			unsigned long i;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci			y = memcmp(s1+offset, s2+offset, size);
4862306a36Sopenharmony_ci			x = test_memcmp(s1+offset, s2+offset, size);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci			if (((x ^ y) < 0) &&	/* Trick to compare sign */
5162306a36Sopenharmony_ci				((x | y) != 0)) { /* check for zero */
5262306a36Sopenharmony_ci				printf("memcmp returned %d, should have returned %d (offset %ld size %ld)\n", x, y, offset, size);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci				for (i = offset; i < offset+size; i++)
5562306a36Sopenharmony_ci					printf("%02x ", s1[i]);
5662306a36Sopenharmony_ci				printf("\n");
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci				for (i = offset; i < offset+size; i++)
5962306a36Sopenharmony_ci					printf("%02x ", s2[i]);
6062306a36Sopenharmony_ci				printf("\n");
6162306a36Sopenharmony_ci				abort();
6262306a36Sopenharmony_ci			}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci			if (vmx_count != 0) {
6562306a36Sopenharmony_ci				printf("vmx enter/exit not paired.(offset:%ld size:%ld s1:%p s2:%p vc:%d\n",
6662306a36Sopenharmony_ci					offset, size, s1, s2, vmx_count);
6762306a36Sopenharmony_ci				printf("\n");
6862306a36Sopenharmony_ci				abort();
6962306a36Sopenharmony_ci			}
7062306a36Sopenharmony_ci		}
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic int testcase(bool islarge)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	unsigned long i, comp_size, alloc_size;
7762306a36Sopenharmony_ci	char *p, *s1, *s2;
7862306a36Sopenharmony_ci	int iterations;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	comp_size = (islarge ? LARGE_SIZE : SIZE);
8162306a36Sopenharmony_ci	alloc_size = comp_size + MAX_OFFSET_DIFF_S1_S2;
8262306a36Sopenharmony_ci	iterations = islarge ? LARGE_ITERATIONS : ITERATIONS;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	p = mmap(NULL, 4 * MAP_SIZE, PROT_READ | PROT_WRITE,
8562306a36Sopenharmony_ci		 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
8662306a36Sopenharmony_ci	FAIL_IF(p == MAP_FAILED);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/* Put s1/s2 at the end of a page */
8962306a36Sopenharmony_ci	s1 = p + MAP_SIZE - alloc_size;
9062306a36Sopenharmony_ci	s2 = p + 3 * MAP_SIZE - alloc_size;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	/* And unmap the subsequent page to force a fault if we overread */
9362306a36Sopenharmony_ci	munmap(p + MAP_SIZE, MAP_SIZE);
9462306a36Sopenharmony_ci	munmap(p + 3 * MAP_SIZE, MAP_SIZE);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	srandom(time(0));
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	for (i = 0; i < iterations; i++) {
9962306a36Sopenharmony_ci		unsigned long j;
10062306a36Sopenharmony_ci		unsigned long change;
10162306a36Sopenharmony_ci		char *rand_s1 = s1;
10262306a36Sopenharmony_ci		char *rand_s2 = s2;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci		for (j = 0; j < alloc_size; j++)
10562306a36Sopenharmony_ci			s1[j] = random();
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci		rand_s1 += random() % MAX_OFFSET_DIFF_S1_S2;
10862306a36Sopenharmony_ci		rand_s2 += random() % MAX_OFFSET_DIFF_S1_S2;
10962306a36Sopenharmony_ci		memcpy(rand_s2, rand_s1, comp_size);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci		/* change one byte */
11262306a36Sopenharmony_ci		change = random() % comp_size;
11362306a36Sopenharmony_ci		rand_s2[change] = random() & 0xff;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci		if (islarge)
11662306a36Sopenharmony_ci			test_one(rand_s1, rand_s2, LARGE_MAX_OFFSET,
11762306a36Sopenharmony_ci					LARGE_SIZE_START, comp_size);
11862306a36Sopenharmony_ci		else
11962306a36Sopenharmony_ci			test_one(rand_s1, rand_s2, SIZE, 0, comp_size);
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	srandom(time(0));
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	for (i = 0; i < iterations; i++) {
12562306a36Sopenharmony_ci		unsigned long j;
12662306a36Sopenharmony_ci		unsigned long change;
12762306a36Sopenharmony_ci		char *rand_s1 = s1;
12862306a36Sopenharmony_ci		char *rand_s2 = s2;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci		for (j = 0; j < alloc_size; j++)
13162306a36Sopenharmony_ci			s1[j] = random();
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci		rand_s1 += random() % MAX_OFFSET_DIFF_S1_S2;
13462306a36Sopenharmony_ci		rand_s2 += random() % MAX_OFFSET_DIFF_S1_S2;
13562306a36Sopenharmony_ci		memcpy(rand_s2, rand_s1, comp_size);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci		/* change multiple bytes, 1/8 of total */
13862306a36Sopenharmony_ci		for (j = 0; j < comp_size / 8; j++) {
13962306a36Sopenharmony_ci			change = random() % comp_size;
14062306a36Sopenharmony_ci			s2[change] = random() & 0xff;
14162306a36Sopenharmony_ci		}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci		if (islarge)
14462306a36Sopenharmony_ci			test_one(rand_s1, rand_s2, LARGE_MAX_OFFSET,
14562306a36Sopenharmony_ci					LARGE_SIZE_START, comp_size);
14662306a36Sopenharmony_ci		else
14762306a36Sopenharmony_ci			test_one(rand_s1, rand_s2, SIZE, 0, comp_size);
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	return 0;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic int testcases(void)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci#ifdef __powerpc64__
15662306a36Sopenharmony_ci	// vcmpequd used in memcmp_64.S is v2.07
15762306a36Sopenharmony_ci	SKIP_IF(!have_hwcap2(PPC_FEATURE2_ARCH_2_07));
15862306a36Sopenharmony_ci#endif
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	testcase(0);
16162306a36Sopenharmony_ci	testcase(1);
16262306a36Sopenharmony_ci	return 0;
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ciint main(void)
16662306a36Sopenharmony_ci{
16762306a36Sopenharmony_ci	test_harness_set_timeout(300);
16862306a36Sopenharmony_ci	return test_harness(testcases, "memcmp");
16962306a36Sopenharmony_ci}
170