blob: d7c20349e5a25d3b4073bce69474e8480b427d95 [file] [log] [blame]
/*
* Copyright 2023 Google LLC
* Copyright lowRISC contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stddef.h>
#include <stdint.h>
#include "hw/top_matcha/sw/autogen/top_matcha.h"
#include "sw/device/lib/arch/device.h"
#include "sw/device/lib/base/macros.h"
#include "sw/device/lib/base/stdasm.h"
#include "sw/device/lib/dif/dif_uart.h"
#include "sw/device/lib/runtime/hart.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/runtime/print.h"
#include "sw/device/lib/testing/test_framework/check.h"
#include "sw/device/lib/testing/test_framework/ottf_test_config.h"
#include "sw/device/lib/testing/test_framework/status.h"
#include "sw/device/lib/testing/test_framework/test_util.h"
#include "sw/device/silicon_creator/lib/manifest_def.h"
OTTF_DEFINE_TEST_CONFIG();
// Symbols defined in `sw/device/lib/testing/test_framework/ottf.ld`,
// which we use to check that the CRT did what it was supposed to.
extern char _bss_start;
extern char _bss_end;
extern char _data_start;
extern char _data_end;
extern char _data_init_start;
// The addresses of the values above.
static const uintptr_t bss_start_addr = (uintptr_t)&_bss_start;
static const uintptr_t bss_end_addr = (uintptr_t)&_bss_end;
static const uintptr_t data_start_addr = (uintptr_t)&_data_start;
static const uintptr_t data_end_addr = (uintptr_t)&_data_end;
static const uintptr_t data_init_start_addr = (uintptr_t)&_data_init_start;
// Ensure that both .bss and .data are non-empty. The compiler will always keep
// these symbols, since they're volatile.
volatile char ensure_data_exists = 42;
volatile char ensure_bss_exists;
static dif_uart_t uart0;
/**
* Test that crt_section_clear correctly zeros word aligned sections.
*
* Sections are simulated using word aligned regions of various sizes within an
* array.
*
* Does not return if the test fails.
*/
static void test_crt_section_clear(void) {
// Function to test (symbol in the CRT assembly library).
extern void crt_section_clear(void *start, void *end);
// Maximum end index of target section.
const size_t kLen = 32;
// Section indices (start inclusive, end exclusive).
const struct {
size_t start;
size_t end;
} kTests[] = {{.start = 0, .end = 0}, {.start = 0, .end = 1},
{.start = kLen - 1, .end = kLen}, {.start = 0, .end = kLen - 1},
{.start = 1, .end = kLen}, {.start = 0, .end = kLen}};
for (size_t t = 0; t < ARRAYSIZE(kTests); ++t) {
// Set target array to non-zero values.
uint32_t section[kLen];
const uint32_t kVal = ~0u;
for (size_t i = 0; i < kLen; ++i) {
section[i] = kVal;
}
// Clear section of target array.
const size_t start = kTests[t].start;
const size_t end = kTests[t].end;
crt_section_clear(&section[start], &section[end]);
// Check that section was cleared.
for (size_t i = 0; i < kLen; ++i) {
const uint32_t expect = i >= start && i < end ? 0 : kVal;
CHECK(section[i] == expect,
"%s case %u: section[%u] got 0x%08x, want 0x%08x", __func__, t, i,
section[i], expect);
}
}
}
/**
* Test that crt_section_copy correctly copies data between word aligned
* sections.
*
* Sections are simulated using word aligned regions of various sizes within
* arrays.
*
* Does not return if the test fails.
*/
static void test_crt_section_copy(void) {
// Function to test (symbol in the CRT assembly library).
extern void crt_section_copy(void *start, void *end, void *source);
// Maximum end index of target section.
const size_t kLen = 32;
// Section indices (start inclusive, end exclusive) and source index
// (inclusive).
const struct {
size_t start;
size_t end;
size_t source;
} kTests[] = {{.start = 0, .end = 0, .source = 0},
{.start = 0, .end = 1, .source = 1},
{.start = kLen - 1, .end = kLen, .source = 2},
{.start = 0, .end = kLen - 1, .source = 1},
{.start = 1, .end = kLen, .source = 1},
{.start = 0, .end = kLen, .source = 0},
{.start = 0, .end = kLen, .source = 0},
{.start = 1, .end = kLen, .source = 0},
{.start = 2, .end = kLen, .source = 0},
{.start = 3, .end = kLen, .source = 0},
{.start = 0, .end = kLen / 2, .source = 0},
{.start = 1, .end = kLen / 2, .source = 0},
{.start = 2, .end = kLen / 2, .source = 0},
{.start = 3, .end = kLen / 2, .source = 0}};
for (size_t t = 0; t < ARRAYSIZE(kTests); ++t) {
// Clear target array and setup source array with known values (index + 1).
uint32_t dst[kLen], src[kLen];
for (size_t i = 0; i < kLen; ++i) {
src[i] = (uint32_t)(i) + 1;
dst[i] = 0;
}
// Copy section from source to target array.
const size_t start = kTests[t].start;
const size_t end = kTests[t].end;
const size_t source = kTests[t].source;
crt_section_copy(&dst[start], &dst[end], &src[source]);
// First expected value.
uint32_t val = (uint32_t)(source) + 1;
// Check section was copied correctly.
for (size_t i = 0; i < kLen; ++i) {
const uint32_t expect = i >= start && i < end ? val++ : 0;
CHECK(dst[i] == expect, "%s case %u: dst[%u] got 0x%08x, want 0x%08x",
__func__, t, i, dst[i], expect);
}
}
}
void _ottf_main(void) {
// NOTE: we cannot call any external functions until all checks of post-CRT
// state are complete; this is to ensure that our checks are not tainted by
// external functions.
//
// Among other things, this means we can't CHECK, since we can't initialize
// UART. Thus, any critical failures are handled by returning from main.
// To minimize the chance of things going wrong, we don't even bother placing
// the checks in their own function.
// Test core assumptions above the five addresses above. The test code
// must be able to assume these all hold.
//
// Note that performing these comparisons on their addresses is UB, and will
// cause this entire function to get deleted by the compiler.
if (&_bss_start > &_bss_end || &_data_start > &_data_end) {
// Something has gone terribly wrong and we have no hope of continuing the
// test, so we're going to just abort.
abort();
}
// Ensure that .bss was *actually* zeroed at the start of execution. If it
// wasn't, we note the offset from _bss_start at which it wasn't.
char *bss = &_bss_start;
ptrdiff_t bss_len = &_bss_end - &_bss_start;
int bad_bss_index = -1;
for (int i = 0; i < bss_len; ++i) {
if (bss[i] != 0) {
bad_bss_index = i;
break;
}
}
// Similarly, ensure that .data has the values in the init section.
char *data = &_data_start;
char *data_init = &_data_init_start;
ptrdiff_t data_len = &_data_end - &_data_start;
int bad_data_index = -1;
for (int i = 0; i < data_len; ++i) {
if (data[i] != data_init[i]) {
bad_data_index = i;
break;
}
}
// End of post-CRT checks; begin actual assertions..
test_status_set(kTestStatusInTest);
// Initialize the UART to enable logging for non-DV simulation platforms.
if (kDeviceType != kDeviceSimDV) {
init_uart(TOP_MATCHA_UART0_BASE_ADDR, &uart0);
}
CHECK(bss_start_addr % sizeof(uint32_t) == 0,
"_bss_start not word-aligned: 0x%08x", bss_start_addr);
CHECK(bss_end_addr % sizeof(uint32_t) == 0,
"_bss_end not word-aligned: 0x%08x", bss_end_addr);
CHECK(data_start_addr % sizeof(uint32_t) == 0,
"_data_start not word-aligned: 0x%08x", data_start_addr);
CHECK(data_end_addr % sizeof(uint32_t) == 0,
"_data_end not word-aligned: 0x%08x", data_end_addr);
CHECK(data_init_start_addr % sizeof(uint32_t) == 0,
"_data_init_start not word-aligned: 0x%08x", data_init_start_addr);
CHECK(bad_bss_index == -1, "found non-zero .bss byte at *0x%08x == 0x%02x",
bss_start_addr + bad_bss_index, (uint32_t)bss[bad_bss_index]);
CHECK(bad_data_index == -1,
"found bad .data byte at *0x%08x == 0x%02x, expected 0x%02x",
data_start_addr + bad_data_index, (uint32_t)data_init[bad_data_index]);
// Unit test CRT utility functions.
test_crt_section_clear();
test_crt_section_copy();
test_status_set(kTestStatusPassed);
}