Skip to content

Commit

Permalink
Add hyperram test
Browse files Browse the repository at this point in the history
  • Loading branch information
GregAC committed Aug 19, 2024
1 parent bc70113 commit ffc0ef8
Show file tree
Hide file tree
Showing 3 changed files with 336 additions and 0 deletions.
12 changes: 12 additions & 0 deletions sw/cheri/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,15 @@ add_custom_command(
COMMAND srec_cat "$<TARGET_FILE:${NAME}>.bin" -binary -offset 0x0000 -byte-swap 4 -o "$<TARGET_FILE:${NAME}>.vmem" -vmem
VERBATIM
)

set(NAME hyperram_test)

add_executable(${NAME} hyperram_exec_test.S hyperram_test.cc boot.S)
target_include_directories(${NAME} PRIVATE ${CHERIOT_SDK_INCLUDES})

add_custom_command(
TARGET ${NAME} POST_BUILD
COMMAND ${CMAKE_OBJCOPY} -O binary "$<TARGET_FILE:${NAME}>" "$<TARGET_FILE:${NAME}>.bin"
COMMAND srec_cat "$<TARGET_FILE:${NAME}>.bin" -binary -offset 0x0000 -byte-swap 4 -o "$<TARGET_FILE:${NAME}>.vmem" -vmem
VERBATIM
)
11 changes: 11 additions & 0 deletions sw/cheri/tests/hyperram_exec_test.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.include "assembly-helpers.s"

.section .text, "ax", @progbits

.globl get_hyperram_fn_ptr
.p2align 2
.type get_hyperram_fn_ptr,@function
get_hyperram_fn_ptr:
auipcc ct0, 0
csetaddr ca0, ct0, a0
cret
313 changes: 313 additions & 0 deletions sw/cheri/tests/hyperram_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,313 @@
#define CHERIOT_NO_AMBIENT_MALLOC
#define CHERIOT_NO_NEW_DELETE
#define CHERIOT_PLATFORM_CUSTOM_UART

#include "../../common/defs.h"
#include "../common/uart-utils.hh"

#include <cheri.hh>
#include <ds/xoroshiro.h>
#include <platform-uart.hh>
#include <stdint.h>

using namespace CHERI;

const int RandTestBlockSize = 256;
const int HyperramSize = (1024 * 1024) / 4;

#define CC_BOLD "1"
#define CC_RED "31"
#define CC_GREEN "32"
#define CC_RESET "0"

void set_console_mode(Capability<volatile OpenTitanUart<>> uart,
const char *cc) {
write_str(uart, "\x1b[");
write_str(uart, cc);
write_str(uart, "m");
}

void write_test_result(Capability<volatile OpenTitanUart<>> uart,
int failures) {
if (failures == 0) {
set_console_mode(uart, CC_GREEN);
write_str(uart, "PASS!\n");
} else {
set_console_mode(uart, CC_RED);
write_str(uart, "FAIL!\n");
}
set_console_mode(uart, CC_RESET);
}

// Write random values to a block of memory (size given by 'RandTestBlockSize'
// global constant). Reads them all back and checks read values matched written
// values.
int rand_data_test_block(Capability<volatile uint32_t> hyperram_area,
ds::xoroshiro::P64R32 &prng, uint32_t start_hr_addr) {
uint32_t write_values[RandTestBlockSize];
uint32_t read_values[RandTestBlockSize];

for (int i = 0; i < RandTestBlockSize; ++i) {
write_values[i] = prng();
}

for (int i = 0; i < RandTestBlockSize; ++i) {
hyperram_area[i + start_hr_addr] = write_values[i];
}

for (int i = 0; i < RandTestBlockSize; ++i) {
read_values[i] = hyperram_area[i + start_hr_addr];
}

int failures = 0;

for (int i = 0; i < RandTestBlockSize; ++i) {
if (read_values[i] != write_values[i]) {
++failures;
}
}

return failures;
}

// Writes random values to all of hyperram and reads back to check read values
// matched written values. It does this one 'RandTestBlockSize' sized block at a
// time.
int rand_data_test_full(Capability<volatile uint32_t> hyperram_area,
ds::xoroshiro::P64R32 &prng) {

int failures = 0;
for (uint32_t addr = 0; addr < HyperramSize; addr += RandTestBlockSize) {
failures += rand_data_test_block(hyperram_area, prng, addr);
}

return failures;
}

// Writes a random value to a random address then reads it back to check the
// written value matches the read value.
int rand_data_addr_test(Capability<volatile uint32_t> hyperram_area,
ds::xoroshiro::P64R32 &prng, int iterations) {

int failures = 0;

for (int i = 0; i < iterations; ++i) {
uint32_t rand_addr;
uint32_t rand_val;
uint32_t read_val;

rand_addr = prng() % HyperramSize;
rand_val = prng();

hyperram_area[rand_addr] = rand_val;
read_val = hyperram_area[rand_addr];

if (read_val != rand_val) {
failures += 1;
}
}

return failures;
}

// Writes a random value to a random address and then writes a capability for
// that random address to another random location. Reads back the capability and
// then reads back the value via the capability to check it matches what we was
// originally written.
int rand_cap_test(Capability<volatile uint32_t> hyperram_area,
Capability<Capability<volatile uint32_t>> hyperram_cap_area,
ds::xoroshiro::P64R32 &prng, int iterations) {

int failures = 0;

for (int i = 0; i < iterations; ++i) {
uint32_t rand_index;
uint32_t rand_cap_index;
uint32_t rand_val;
uint32_t read_val;

Capability<volatile uint32_t> write_cap;
Capability<volatile uint32_t> read_cap;

do {
rand_index = prng() % HyperramSize;

// Capability is double word in size.
rand_cap_index = prng() % (HyperramSize / 2);
} while (rand_index / 2 == rand_cap_index);

rand_val = prng();

write_cap = hyperram_area;
write_cap.address() += (rand_index * 4);
write_cap.bounds() = 4;

hyperram_area[rand_index] = rand_val;
hyperram_cap_area[rand_cap_index] = write_cap;

asm volatile("" : : : "memory");

read_cap = hyperram_cap_area[rand_cap_index];
read_val = *read_cap;

if (read_val != rand_val) {
failures++;
}
}

return failures;
}

// Writes a 32-bit value in every location in hyperram and then reads back to
// check read values match written values. The values written alternate between
// 'initial_val' and the inversion of 'initial_val'.
int stripe_test(Capability<volatile uint32_t> hyperram_area,
uint32_t initial_val) {
uint32_t failures = 0;
uint32_t cur_write_val = initial_val;

for (uint32_t addr = 0; addr < HyperramSize; addr++) {
hyperram_area[addr] = cur_write_val;
cur_write_val = ~cur_write_val;
}

uint32_t cur_expected_val = initial_val;

for (uint32_t addr = 0; addr < HyperramSize; addr++) {
uint32_t read_value = hyperram_area[addr];
if (read_value != cur_expected_val) {
failures++;
}

cur_expected_val = ~cur_expected_val;
}

return failures;
}

typedef void *(*test_fn_t)(uint32_t *);
// Gets a function pointer to an address in hyperram, expectes to be called with
// a PC capability that provides execution in hyperram. 'addr' is relative to
// the base of hyperram.
extern "C" test_fn_t get_hyperram_fn_ptr(uint32_t addr);

void write_prog(Capability<volatile uint32_t> &hyperram_area, uint32_t addr) {
// Avoid use of global data (as it's currently broken in the test environment)
// by writing program data directly here.

// Test program, writes 0xdeadbeef to capability provided in first argument
// (ca0) and returns a capability pointing to the middle of the function
// (offset + 0xC from function start).
//
// li t0, 0xdeadbeef # Expands to two 32-bit instructions
// csw t0, 0(ca0)
// auipcc ca0, 0
// cret

hyperram_area[addr] = 0xdeadc2b7;
hyperram_area[addr + 1] = 0xeef28293;
hyperram_area[addr + 2] = 0x00552023;
hyperram_area[addr + 3] = 0x00000517;
hyperram_area[addr + 4] = 0x8082;
}

// Writes a short function to a random area of hyperram and executes it checking
// for successful execution (see 'write_prog' for details on the function
// written).
int execute_test(Capability<volatile uint32_t> &hyperram_area,
ds::xoroshiro::P64R32 &prng, int iterations) {

int failures = 0;

for (int i = 0; i < iterations; ++i) {
uint32_t prog_addr = prng() % (HyperramSize - 5);

write_prog(hyperram_area, prog_addr);

uint32_t test_int = 0x0;
void *test_ptr;

test_fn_t test_fn = get_hyperram_fn_ptr(HYPERRAM_ADDRESS + (prog_addr * 4));
test_ptr = test_fn(&test_int);

if (test_int != 0xdeadbeef) {
failures++;
}

if (!__builtin_cheri_tag_get(test_ptr)) {
failures++;
}

uint32_t expected_ptr_addr = HYPERRAM_ADDRESS + 0xC + (prog_addr * 4);
uint32_t test_ptr_addr = __builtin_cheri_address_get(test_ptr);

if (test_ptr_addr != expected_ptr_addr) {
failures++;
}
}

return failures;
}

/**
* C++ entry point for the loader. This is called from assembly, with the
* read-write root in the first argument.
*/
[[noreturn]] extern "C" void rom_loader_entry(void *rwRoot) {
Capability<void> root{rwRoot};

// Create a bounded capability to the UART
Capability<volatile OpenTitanUart<>> uart =
root.cast<volatile OpenTitanUart<>>();
uart.address() = UART_ADDRESS;
uart.bounds() = UART_BOUNDS;

uart->init(BAUD_RATE);
set_console_mode(uart, CC_BOLD);
write_str(uart, "\r\n\r\nGet hyped for hyperram!\r\n");
set_console_mode(uart, CC_RESET);

ds::xoroshiro::P64R32 prng;
prng.set_state(0xDEADBEEF, 0xBAADCAFE);

Capability<volatile uint32_t> hyperram_area = root.cast<volatile uint32_t>();
hyperram_area.address() = HYPERRAM_ADDRESS;
hyperram_area.bounds() = HYPERRAM_BOUNDS;

Capability<Capability<volatile uint32_t>> hyperram_cap_area =
root.cast<Capability<volatile uint32_t>>();
hyperram_cap_area.address() = HYPERRAM_ADDRESS;
hyperram_cap_area.bounds() = HYPERRAM_BOUNDS;

while (true) {
int failures = 0;
write_str(uart, "Running RND cap test...");
failures +=
rand_cap_test(hyperram_area, hyperram_cap_area, prng, HyperramSize / 4);
write_test_result(uart, failures);

write_str(uart, "Running RND data test...");
failures = rand_data_test_full(hyperram_area, prng);
write_test_result(uart, failures);

write_str(uart, "Running RND data & address test...");
failures = rand_data_addr_test(hyperram_area, prng, HyperramSize / 4);
write_test_result(uart, failures);

write_str(uart, "Running 0101 stripe test...");
failures = stripe_test(hyperram_area, 0x55555555);
write_test_result(uart, failures);

write_str(uart, "Running 1001 stripe test...");
failures = stripe_test(hyperram_area, 0x99999999);
write_test_result(uart, failures);

write_str(uart, "Running 0000_1111 stripe test...");
failures = stripe_test(hyperram_area, 0x0F0F0F0F);
write_test_result(uart, failures);

write_str(uart, "Running Execution test...");
failures = execute_test(hyperram_area, prng, HyperramSize / 4);
write_test_result(uart, failures);
}
}

0 comments on commit ffc0ef8

Please sign in to comment.