qemu/tests/tcg/multiarch/system/memory.c
Alex Bennée fcf112317c tests/tcg: better detect truncated reads
If we've truncated a wider read we can detect the condition earlier by
looking at the number of zeros we've read. So we don't trip up on
cases where we have written zeros to the start of the buffer we also
ensure we only start each offset read from the right address.

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
2019-06-12 17:53:22 +01:00

476 lines
12 KiB
C

/*
* Memory Test
*
* This is intended to test the softmmu code and ensure we properly
* behave across normal and unaligned accesses across several pages.
* We are not replicating memory tests for stuck bits and other
* hardware level failures but looking for issues with different size
* accesses when access is:
*
* - unaligned at various sizes (if -DCHECK_UNALIGNED set)
* - spanning a (softmmu) page
* - sign extension when loading
*/
#include <inttypes.h>
#include <stdbool.h>
#include <minilib.h>
#ifndef CHECK_UNALIGNED
# error "Target does not specify CHECK_UNALIGNED"
#endif
#define PAGE_SIZE 4096 /* nominal 4k "pages" */
#define TEST_SIZE (PAGE_SIZE * 4) /* 4 pages */
#define ARRAY_SIZE(x) ((sizeof(x) / sizeof((x)[0])))
__attribute__((aligned(PAGE_SIZE)))
static uint8_t test_data[TEST_SIZE];
typedef void (*init_ufn) (int offset);
typedef bool (*read_ufn) (int offset);
typedef bool (*read_sfn) (int offset, bool nf);
static void pdot(int count)
{
if (count % 128 == 0) {
ml_printf(".");
}
}
/*
* Helper macros for shift/extract so we can keep our endian handling
* in one place.
*/
#define BYTE_SHIFT(b, pos) ((uint64_t)b << (pos * 8))
#define BYTE_EXTRACT(b, pos) ((b >> (pos * 8)) & 0xff)
/*
* Fill the data with ascending value bytes.
*
* Currently we only support Little Endian machines so write in
* ascending address order. When we read higher address bytes should
* either be zero or higher than the lower bytes.
*/
static void init_test_data_u8(int unused_offset)
{
uint8_t count = 0, *ptr = &test_data[0];
int i;
(void)(unused_offset);
ml_printf("Filling test area with u8:");
for (i = 0; i < TEST_SIZE; i++) {
*ptr++ = count++;
pdot(i);
}
ml_printf("done\n");
}
/*
* Full the data with alternating positive and negative bytes. This
* should mean for reads larger than a byte all subsequent reads will
* stay either negative or positive. We never write 0.
*/
static inline uint8_t get_byte(int index, bool neg)
{
return neg ? (0xff << (index % 7)) : (0xff >> ((index % 6) + 1));
}
static void init_test_data_s8(bool neg_first)
{
uint8_t top, bottom, *ptr = &test_data[0];
int i;
ml_printf("Filling test area with s8 pairs (%s):",
neg_first ? "neg first" : "pos first");
for (i = 0; i < TEST_SIZE / 2; i++) {
*ptr++ = get_byte(i, neg_first);
*ptr++ = get_byte(i, !neg_first);
pdot(i);
}
ml_printf("done\n");
}
/*
* Zero the first few bytes of the test data in preparation for
* new offset values.
*/
static void reset_start_data(int offset)
{
uint32_t *ptr = (uint32_t *) &test_data[0];
int i;
for (i = 0; i < offset; i++) {
*ptr++ = 0;
}
}
static void init_test_data_u16(int offset)
{
uint8_t count = 0;
uint16_t word, *ptr = (uint16_t *) &test_data[offset];
const int max = (TEST_SIZE - offset) / sizeof(word);
int i;
ml_printf("Filling test area with u16 (offset %d, %p):", offset, ptr);
reset_start_data(offset);
for (i = 0; i < max; i++) {
uint8_t low = count++, high = count++;
word = BYTE_SHIFT(high, 1) | BYTE_SHIFT(low, 0);
*ptr++ = word;
pdot(i);
}
ml_printf("done @ %p\n", ptr);
}
static void init_test_data_u32(int offset)
{
uint8_t count = 0;
uint32_t word, *ptr = (uint32_t *) &test_data[offset];
const int max = (TEST_SIZE - offset) / sizeof(word);
int i;
ml_printf("Filling test area with u32 (offset %d, %p):", offset, ptr);
reset_start_data(offset);
for (i = 0; i < max; i++) {
uint8_t b4 = count++, b3 = count++;
uint8_t b2 = count++, b1 = count++;
word = BYTE_SHIFT(b1, 3) | BYTE_SHIFT(b2, 2) | BYTE_SHIFT(b3, 1) | b4;
*ptr++ = word;
pdot(i);
}
ml_printf("done @ %p\n", ptr);
}
static void init_test_data_u64(int offset)
{
uint8_t count = 0;
uint64_t word, *ptr = (uint64_t *) &test_data[offset];
const int max = (TEST_SIZE - offset) / sizeof(word);
int i;
ml_printf("Filling test area with u64 (offset %d, %p):", offset, ptr);
reset_start_data(offset);
for (i = 0; i < max; i++) {
uint8_t b8 = count++, b7 = count++;
uint8_t b6 = count++, b5 = count++;
uint8_t b4 = count++, b3 = count++;
uint8_t b2 = count++, b1 = count++;
word = BYTE_SHIFT(b1, 7) | BYTE_SHIFT(b2, 6) | BYTE_SHIFT(b3, 5) |
BYTE_SHIFT(b4, 4) | BYTE_SHIFT(b5, 3) | BYTE_SHIFT(b6, 2) |
BYTE_SHIFT(b7, 1) | b8;
*ptr++ = word;
pdot(i);
}
ml_printf("done @ %p\n", ptr);
}
static bool read_test_data_u16(int offset)
{
uint16_t word, *ptr = (uint16_t *)&test_data[offset];
int i;
const int max = (TEST_SIZE - offset) / sizeof(word);
ml_printf("Reading u16 from %#lx (offset %d):", ptr, offset);
for (i = 0; i < max; i++) {
uint8_t high, low;
word = *ptr++;
high = (word >> 8) & 0xff;
low = word & 0xff;
if (high < low && high != 0) {
ml_printf("Error %d < %d\n", high, low);
return false;
} else {
pdot(i);
}
}
ml_printf("done @ %p\n", ptr);
return true;
}
static bool read_test_data_u32(int offset)
{
uint32_t word, *ptr = (uint32_t *)&test_data[offset];
int i;
const int max = (TEST_SIZE - offset) / sizeof(word);
ml_printf("Reading u32 from %#lx (offset %d):", ptr, offset);
for (i = 0; i < max; i++) {
uint8_t b1, b2, b3, b4;
int zeros = 0;
word = *ptr++;
b1 = word >> 24 & 0xff;
b2 = word >> 16 & 0xff;
b3 = word >> 8 & 0xff;
b4 = word & 0xff;
zeros += (b1 == 0 ? 1 : 0);
zeros += (b2 == 0 ? 1 : 0);
zeros += (b3 == 0 ? 1 : 0);
zeros += (b4 == 0 ? 1 : 0);
if (zeros > 1) {
ml_printf("Error @ %p, more zeros than expected: %d, %d, %d, %d",
ptr - 1, b1, b2, b3, b4);
return false;
}
if ((b1 < b2 && b1 != 0) ||
(b2 < b3 && b2 != 0) ||
(b3 < b4 && b3 != 0)) {
ml_printf("Error %d, %d, %d, %d", b1, b2, b3, b4);
return false;
} else {
pdot(i);
}
}
ml_printf("done @ %p\n", ptr);
return true;
}
static bool read_test_data_u64(int offset)
{
uint64_t word, *ptr = (uint64_t *)&test_data[offset];
int i;
const int max = (TEST_SIZE - offset) / sizeof(word);
ml_printf("Reading u64 from %#lx (offset %d):", ptr, offset);
for (i = 0; i < max; i++) {
uint8_t b1, b2, b3, b4, b5, b6, b7, b8;
int zeros = 0;
word = *ptr++;
b1 = ((uint64_t) (word >> 56)) & 0xff;
b2 = ((uint64_t) (word >> 48)) & 0xff;
b3 = ((uint64_t) (word >> 40)) & 0xff;
b4 = (word >> 32) & 0xff;
b5 = (word >> 24) & 0xff;
b6 = (word >> 16) & 0xff;
b7 = (word >> 8) & 0xff;
b8 = (word >> 0) & 0xff;
zeros += (b1 == 0 ? 1 : 0);
zeros += (b2 == 0 ? 1 : 0);
zeros += (b3 == 0 ? 1 : 0);
zeros += (b4 == 0 ? 1 : 0);
zeros += (b5 == 0 ? 1 : 0);
zeros += (b6 == 0 ? 1 : 0);
zeros += (b7 == 0 ? 1 : 0);
zeros += (b8 == 0 ? 1 : 0);
if (zeros > 1) {
ml_printf("Error @ %p, more zeros than expected: %d, %d, %d, %d, %d, %d, %d, %d",
ptr - 1, b1, b2, b3, b4, b5, b6, b7, b8);
return false;
}
if ((b1 < b2 && b1 != 0) ||
(b2 < b3 && b2 != 0) ||
(b3 < b4 && b3 != 0) ||
(b4 < b5 && b4 != 0) ||
(b5 < b6 && b5 != 0) ||
(b6 < b7 && b6 != 0) ||
(b7 < b8 && b7 != 0)) {
ml_printf("Error %d, %d, %d, %d, %d, %d, %d, %d",
b1, b2, b3, b4, b5, b6, b7, b8);
return false;
} else {
pdot(i);
}
}
ml_printf("done @ %p\n", ptr);
return true;
}
/* Read the test data and verify at various offsets */
read_ufn read_ufns[] = { read_test_data_u16,
read_test_data_u32,
read_test_data_u64 };
bool do_unsigned_reads(int start_off)
{
int i;
bool ok = true;
for (i = 0; i < ARRAY_SIZE(read_ufns) && ok; i++) {
#if CHECK_UNALIGNED
int off;
for (off = start_off; off < 8 && ok; off++) {
ok = read_ufns[i](off);
}
#else
ok = read_ufns[i](start_off);
#endif
}
return ok;
}
static bool do_unsigned_test(init_ufn fn)
{
#if CHECK_UNALIGNED
bool ok = true;
int i;
for (i = 0; i < 8 && ok; i++) {
fn(i);
ok = do_unsigned_reads(i);
}
#else
fn(0);
return do_unsigned_reads(0);
#endif
}
/*
* We need to ensure signed data is read into a larger data type to
* ensure that sign extension is working properly.
*/
static bool read_test_data_s8(int offset, bool neg_first)
{
int8_t *ptr = (int8_t *)&test_data[offset];
int i;
const int max = (TEST_SIZE - offset) / 2;
ml_printf("Reading s8 pairs from %#lx (offset %d):", ptr, offset);
for (i = 0; i < max; i++) {
int16_t first, second;
bool ok;
first = *ptr++;
second = *ptr++;
if (neg_first && first < 0 && second > 0) {
pdot(i);
} else if (!neg_first && first > 0 && second < 0) {
pdot(i);
} else {
ml_printf("Error %d %c %d\n", first, neg_first ? '<' : '>', second);
return false;
}
}
ml_printf("done @ %p\n", ptr);
return true;
}
static bool read_test_data_s16(int offset, bool neg_first)
{
int16_t *ptr = (int16_t *)&test_data[offset];
int i;
const int max = (TEST_SIZE - offset) / (sizeof(*ptr));
ml_printf("Reading s16 from %#lx (offset %d, %s):", ptr,
offset, neg_first ? "neg" : "pos");
for (i = 0; i < max; i++) {
int32_t data = *ptr++;
if (neg_first && data < 0) {
pdot(i);
} else if (data > 0) {
pdot(i);
} else {
ml_printf("Error %d %c 0\n", data, neg_first ? '<' : '>');
return false;
}
}
ml_printf("done @ %p\n", ptr);
return true;
}
static bool read_test_data_s32(int offset, bool neg_first)
{
int32_t *ptr = (int32_t *)&test_data[offset];
int i;
const int max = (TEST_SIZE - offset) / (sizeof(int32_t));
ml_printf("Reading s32 from %#lx (offset %d, %s):",
ptr, offset, neg_first ? "neg" : "pos");
for (i = 0; i < max; i++) {
int64_t data = *ptr++;
if (neg_first && data < 0) {
pdot(i);
} else if (data > 0) {
pdot(i);
} else {
ml_printf("Error %d %c 0\n", data, neg_first ? '<' : '>');
return false;
}
}
ml_printf("done @ %p\n", ptr);
return true;
}
/*
* Read the test data and verify at various offsets
*
* For everything except bytes all our reads should be either positive
* or negative depending on what offset we are reading from. Currently
* we only handle LE systems.
*/
read_sfn read_sfns[] = { read_test_data_s8,
read_test_data_s16,
read_test_data_s32 };
bool do_signed_reads(bool neg_first)
{
int i;
bool ok = true;
for (i = 0; i < ARRAY_SIZE(read_sfns) && ok; i++) {
#if CHECK_UNALIGNED
int off;
for (off = 0; off < 8 && ok; off++) {
bool nf = i == 0 ? neg_first ^ (off & 1) : !(neg_first ^ (off & 1));
ok = read_sfns[i](off, nf);
}
#else
ok = read_sfns[i](0, i == 0 ? neg_first : !neg_first);
#endif
}
return ok;
}
init_ufn init_ufns[] = { init_test_data_u8,
init_test_data_u16,
init_test_data_u32,
init_test_data_u64 };
int main(void)
{
int i;
bool ok = true;
/* Run through the unsigned tests first */
for (i = 0; i < ARRAY_SIZE(init_ufns) && ok; i++) {
ok = do_unsigned_test(init_ufns[i]);
}
if (ok) {
init_test_data_s8(false);
ok = do_signed_reads(false);
}
if (ok) {
init_test_data_s8(true);
ok = do_signed_reads(true);
}
ml_printf("Test complete: %s\n", ok ? "PASSED" : "FAILED");
return ok ? 0 : -1;
}